
Copernic Desktop Search is arguably one of the best desktop search tools available. One of the features that makes it great is the ability to extend it using the simple file extractor plug-in API. The API is fairly simple to use and this article is intended to get a Delphi developer up to speed quickly.
This article will demonstrate how to create a plug-in to find and search within binary Delphi form resource files (binary DFMs). This may not seem that useful to those who are only familiar with more recent generations of Delphi that default to text DFMs. However, if you maintain any older projects you will likely have the odd binary DFM floating around. Nevertheless, it demonstrates the basic concepts for writing a fully functional CDS plug-in in the Delphi language.
One of my main motivations in writing this particular plug-in was to finally eradicate all binary DFMs from my personal source code library. Borland's "convert" utility seemed useful to do this, but I wanted a bit more control over the conversion. I had some binary DFMs lurking in freeware components or libraries that I'd acquired in the past as well as a few older projects of mine that had been upgraded through older versions of Delphi. Little did my binary DFMs know but they would face extinction in about one hour! Well, all except some old Delphi 3 stuff that was in permanent cryogenic storage... never to be returned to this world (hopefully)!
Usually a file extractor plug-in simply extracts all text and inserts appropriate whitespace and presents that to Copernic so that it can index all the keywords. In this particular case there is one additional goal: to make it easy to differentiate between binary and text DFMs. The simple solution is to add a distinctive keyword to the DFM results: "BinDFM" for binary and "TextDFM" for text. This makes it easy to find text in any text/binary DFM and still differentiate the binary ones by placing "BinDFM" or "TextDFM" at the top of the preview pane. In addition, finding binary DFMs simply involves a search for "Bindfm" (and possibly restricting the search to only *.dfm files).
Now that I've explained a bit about what we're going to do, here's step by step instructions to accomplish this:
uses
Classes;
procedure ReadDFMToMemoryStream(
const FileName: string;
const MemoryStream: TMemoryStream);
uses
SysUtils;
procedure ReadDFMToMemoryStream(
const FileName: string;
const MemoryStream: TMemoryStream);
var
FileStream: TFileStream;
TempMemoryStream: TMemoryStream;
PrependString: string;
CharBuffer: array[0..65535] of char;
ReadCount: integer;
OriginalFormat: TStreamOriginalFormat;
begin
FileStream := TFileStream.Create(FileName, fmOpenRead);
try
TempMemoryStream := TMemoryStream.Create;
try
ReadCount := FileStream.Read(CharBuffer, 3);
FileStream.Seek(0, soFromBeginning);
if ReadCount = 3 then
begin
if (CharBuffer[0] = #255) and (CharBuffer[1] = #10)
and (CharBuffer[2] = #0) then
begin
// Has the binary file signature
OriginalFormat := sofBinary;
ObjectResourceToText(FileStream, TempMemoryStream);
end
else
begin
// Assume text otherwise
OriginalFormat := sofText;
end;
end
else
begin
// Too short to be binary or text, but more likely
// empty text file so go with that!
OriginalFormat := sofText;
end;
TempMemoryStream.Seek(0, soFromBeginning);
case OriginalFormat of
sofUnknown: PrependString := 'UnknownDFM';
sofBinary: PrependString := 'BinDFM';
sofText: PrependString := 'TextDFM';
else PrependString := 'OtherDFM';
end;
PrependString := PrependString + #13#10#13#10;
if OriginalFormat = sofBinary then
begin
MemoryStream.Write(PrependString[1], Length(PrependString));
ReadCount := TempMemoryStream.Read(CharBuffer[0],
SizeOf(CharBuffer));
while ReadCount > 0 do
begin
MemoryStream.Write(CharBuffer[0], ReadCount);
ReadCount := TempMemoryStream.Read(CharBuffer[0],
SizeOf(CharBuffer));
end;
end
else
begin
MemoryStream.Write(PrependString[1], Length(PrependString));
FileStream.Free;
FileStream := TFileStream.Create(FileName, fmOpenRead);
ReadCount := FileStream.Read(CharBuffer, SizeOf(CharBuffer));
while ReadCount > 0 do
begin
MemoryStream.Write(CharBuffer, ReadCount);
ReadCount := FileStream.Read(CharBuffer, SizeOf(CharBuffer));
end;
end;
MemoryStream.Seek(0, soFromBeginning);
finally
TempMemoryStream.Free;
end;
finally
FileStream.Free;
end;
end;
private
FFilePath: string;
Classes, DelphiContentExtractionUtils
Value := False;
Result := S_OK;
var
StreamAdapter: TStreamAdapter;
MemoryStream: TMemoryStream;
begin
// Note: StreamAdapter will look after freeing MemoryStream
MemoryStream := TMemoryStream.Create;
ReadDFMToMemoryStream(FFilePath, MemoryStream);
StreamAdapter := TStreamAdapter.Create(MemoryStream);
ContentStream := StreamAdapter;
Result := S_OK;
end;
FFilePath := URI;
Result := S_OK;
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Copernic\DesktopSearch\CustomExtractors]
".dfm"="{<substitute your GUID here>}"
In the step-by-step instructions above, the procedure "ObjectResourceToText" was used. The VCL has two overloadeded procedures available: one takes 2 parameters and another that takes 3. The "ObjectResourceToText" procedure that takes 3 parameters seemed like the most logical choice at first because it returned an OriginalFormat enumerated type. Unfortunately, this procedure didn't appear to work the way it should. In the end I resorted to writing my own code to detect a binary DFM file by checking the first 3 characters for the binary form signature. That worked reliably.
Copernic provides a document called "Writing a File Extractor Plug-in for Copernic Desktop Search 1.5" that provides a good reference document for creating plugins and can be found at this URL: http://redirect.copernic.com/data/pdf/cds-file-extractor-plug-in-en.pdf
























































































