Board index » delphi » Retrieving the ancestor form class using the VCL Scanner source

Retrieving the ancestor form class using the VCL Scanner source

Hello,    

Recently I've been running tests using the source code of the VCL
Scanner.  I've developed a tool that retrieves all controls from an
executable so I can use that to include user defined help in our
Delphi projects.
My problem is that the DFM's I retrieve only contain object
definitions of objects whose properties have changed relative to the
parent form.  If you don't change any property of the control the
object is only defined in the parent form.
Is there any possibility to obtain the class of the parent form, so I
could merge the object definitions to compose a full form component
description, including the controls of the parent form?

Thanks!
Best regards,

Andy Thevelein
Research & Development
---------------------------------------------------------
HEMMIS n.v.
Koning LeopoldIII-laan 2
8500 Kortrijk
BELGIUM
Tel: +32-56-37.26.37
Fax: +32-56-37.23.24
mailto:a...@hemmis.be
Visit our website at http://www.hemmis.be
---------------------------------------------------------

 

Re:Retrieving the ancestor form class using the VCL Scanner source


Quote
In article <3b0531cf.7879620@PROXYSERVER>, Andy Thevelein wrote:
> Recently I've been running tests using the source code of the VCL
> Scanner.  I've developed a tool that retrieves all controls from an
> executable so I can use that to include user defined help in our
> Delphi projects.
> My problem is that the DFM's I retrieve only contain object
> definitions of objects whose properties have changed relative to the
> parent form.  If you don't change any property of the control the
> object is only defined in the parent form.
> Is there any possibility to obtain the class of the parent form, so I
> could merge the object definitions to compose a full form component
> description, including the controls of the parent form?

Not from the DFM file, as far as i know. The DFM for the parent form class
is of course also embedded into a resource but there is no way to find the
association between two form classes from the DFMs. This info is contained
in the class records only.

Peter Below (TeamB)  100113.1...@compuserve.com)
No e-mail responses, please, unless explicitly requested!
Note: I'm unable to visit the newsgroups every day at the moment,
so be patient if you don't get a reply immediately.

Re:Retrieving the ancestor form class using the VCL Scanner source


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;

Other Threads