views:

115

answers:

3

Hi,

I have a form (form2) and I implemented the following PUBLIC method:

function ShowInterface(i:integer):boolean;

This form is in a package that will be DYNAMIC LOADED. Now I want to instantiate this form (form2) and execute the method above.

Important: I can't reference form2's unit in form1.

I tryed this code, but it never finds "ShowInterface" pointer (returns nil).

procedure TfrmForm1.Button1Click(Sender: TObject);
var
  PackageModule: HModule;
  AClass: TPersistentClass;
  ShowInterface: function (i:integer):boolean;
  frm: TCustomForm;
begin
  PackageModule := LoadPackage('form2.bpl');
  if PackageModule <> 0 then
  begin
    AClass := GetClass('TfrmForm2');
    if AClass <> nil then // <<-- FINE!! IT FINDS OUT 'TfrmForm2' in 'form2.bpl')
    begin
      frm := TComponentClass(AClass).Create(Self) as TCustomForm;
      ShowInterface := frm.MethodAddress('ShowInterface'); // <<-- HERE!! ALLWAYS RETURNS "NIL"
      if @ShowInterface <> nil then
        ShowInterface(1);
      // but if I call frm.Show, it works fine. frm is "loaded"!!!

      frm.Free;
    end;
    DoUnloadPackage(PackageModule);
  end;
end;

Thanks in advance.

A: 

In D2007 and some earlier versions, that only works with published methods, or extended RTTI: {$METHODINFO ON}. I haven't used D2010 yet; it seems to have a new RTTI system which has been extended a lot.

TOndrej
+3  A: 

MethodAddress only works for published methods. Move it to the published section and it should work.

Or, if you have Delphi 2010, the extended RTTI offers a way to find public methods by name. (Or other visibility levels, if you change it from the default.)

Mason Wheeler
OK. Now it works. (I did some changes in code, because it raises an AV. See corrected code below).
Christian Almeida
+2  A: 

As Mason and TOndrej said, I have to put the method in published section. (Thank you!)

But, some fixes were needed:

procedure TfrmForm1.Button1Click(Sender: TObject);
type
  TShowInterface = function(i:integer):boolean of object;
var
  PackageModule: HModule;
  AClass: TPersistentClass;
  Routine: TMethod;
  ShowInterface : TShowInterface;
  frm: TCustomForm;
begin
  PackageModule := LoadPackage('form2.bpl');
  if PackageModule <> 0 then
  begin
    AClass := GetClass('TfrmForm2');
    if AClass <> nil then
    begin
      frm := TComponentClass(AClass).Create(Self) as TCustomForm;
      Routine.Data := Pointer(frm);
      Routine.Code := frm.MethodAddress('ShowInterface');
      if Assigned(Routine.Code) then
      begin
        ShowInterface := TShowInterface(Routine);
        ShowInterface(1); // showinterface executes a "ShowModal", so we can "free" form after this.
      end;
      frm.Free;
    end;
    DoUnloadPackage(PackageModule);
  end;
end;
Christian Almeida
+1 for publishing the corrected code while accepting Mason's answer.
François