Andy,
I'm not familiar with the VCL Scanner (do you have a link with some info?)
but the following approach worked when I tried to do something similar:
1. Register all the classes in the application. (See below for code to do
this automatically). You need this directly for step 2 and also indirectly
to make sure all the components that you will load are registered. The
component classes need to be registered becasue will no be loading the DFM's
in the "normal" way.
2. Loop through all the form classes that are defined in the application.
(You can find these directly during step 1 directly or else by iterating
through all DFM-like resources, reading the class type from the dfm, and
then using FindClass. FindClass will work becasue you have just registered
all the classes in the app.)
3. For each form, I created a form object, then loaded the whole heirarchy
of DFMs into that one object. Starting with the top-most ancestor and
finishing with the form's own DFM. This is how the VCL initialises
inherited forms.
The steps for part 3 were:
(a) Create a form object to read the DFM's into. Don't create an object
of the real type (e.g. TMyForm) just create a TForm. This is because you
don't want anything fancy in any construtors/FormCreate events to run.
(b) Using the real class of the form (e.g. TMyForm) work back up the
"chain" of ancestors until you get back to TForm. You can use ClassParent
to do this. Then, working back "down" (probably in a recursive function),
you need to load the DFM for each ancestor class into the object you created
in step a. Last of all you load in the DFM for the "child" form that you
are actually interested in. There is a function in the VCL that does
something very similar, called InitInheritedComponent I believe.
During this stage you have to ignore errors that are raised by the
streaming system. These can be becasuse the components you are loading try
to link to components on other forms or datamodules, or else they may be
warnings about event handlers not being found. (Since the DFM will have
events for components, but we are reading the DFM into a plain TForm that
does not have those events). To do this you need to use the OnError event
of the TReader object that is reading in the DFM. There should be an
example in the Forms or Classes units of creating and using a TReader if you
haven't done so before.
This approach works reasonably well. I used it to write a unit that checks
the tab order of all forms in an application, and that takes about 20
seconds for a large-ish app.
How to register all the classes in an application: To do this you can use
some very clever code posted to a newsgroup by Ludovic Dubois. You can find
the code by searching for "Dubois POSearchClasses" at www.tamaracka.com or
your favourite newsgroup search engine. You'll need to add some declarations
to get the code to compile, I'll add them to the bottom of this email. It's
very clever. Personally, I would be hesitant to include it in code that is
delivered to customers becasue its a bit of a sneaky hack, but for internal
use generating documentation etc I think you'll find it works very well.
Regards
John
PS the "missing" declarations:
pIMAGE_DOS_HEADER = ^IMAGE_DOS_HEADER;
IMAGE_DOS_HEADER = packed record { DOS .EXE header }
e_magic : WORD; { Magic number }
e_cblp : WORD; { Bytes on last page of file }
e_cp : WORD; { Pages in file }
e_crlc : WORD; { Relocations }
e_cparhdr : WORD; { Size of header in paragraphs }
e_minalloc : WORD; { Minimum extra paragraphs needed }
e_maxalloc : WORD; { Maximum extra paragraphs needed }
e_ss : WORD; { Initial (relative) SS value }
e_sp : WORD; { Initial SP value }
e_csum : WORD; { Checksum }
e_ip : WORD; { Initial IP value }
e_cs : WORD; { Initial (relative) CS value }
e_lfarlc : WORD; { File address of relocation table }
e_ovno : WORD; { Overlay number }
e_res : packed array [0..3] of WORD; { Reserved words }
e_oemid : WORD; { OEM identifier (for e_oeminfo) }
e_oeminfo : WORD; { OEM information; e_oemid specific }
e_res2 : packed array [0..9] of WORD; { Reserved words }
e_lfanew : LongWord; { File address of new exe header }
end;
PIMAGE_NT_HEADERS = ^IMAGE_NT_HEADERS;
IMAGE_NT_HEADERS = packed record
Signature : DWORD;
FileHeader : IMAGE_FILE_HEADER;
OptionalHeader : IMAGE_OPTIONAL_HEADER;
end;
PIMAGE_SECTION_HEADER = ^IMAGE_SECTION_HEADER;
IMAGE_SECTION_HEADER = packed record
Name : packed array [0..IMAGE_SIZEOF_SHORT_NAME-1] of Char;
VirtualSize : DWORD; // or VirtualSize (union);
VirtualAddress : DWORD;
SizeOfRawData : DWORD;
PointerToRawData : DWORD;
PointerToRelocations : DWORD;
PointerToLinenumbers : DWORD;
NumberOfRelocations : WORD;
NumberOfLinenumbers : WORD;
Characteristics : DWORD;
end;