Board index » delphi » DLL function that returns an object, failed when calling methods

DLL function that returns an object, failed when calling methods

I try to write a  DLL-polymorphism tester,
which is compiled as two parts,
the main program and the DLL.

The main program declares a
base-class with some attributes and methods.

The DLL inherits the base-class,
and overrides some methods.

When the main program calls the DLL,
the DLL creates an object
and returns it to the main program.

So the main program can handle the object,
such as accessing its attributes and calling its methods,
even not knowing the descendant class in advance.

Below are my programs.
I could access the attribute but couldn't call the method.

<please copy and paste to your Delphi>

------------------------------------<caller.pas>
unit Caller;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TDLLobj=class(TObject)
  public
     a:integer;
     procedure about;   virtual;  
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

function getDLLobj:TDLLobj;   far;   external 'theDLL';

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var DLLobj:TDLLobj;
begin
   DLLobj:=getDLLobj;
   messageDlg('a='+inttostr(DLLobj.a),mtInformation,[mbOK],0);
   (DLLobj as TDLLobj).about;
end;

procedure TDLLobj.about;
begin
   messageDlg('method not overrided',mtInformation,[mbOK],0);
end;

end.
------------------------------------<theDLL.dpr>
library Thedll;

uses caller,dialogs;

type
   TDLLtest=class(TDLLobj)
   public
      procedure about;   override;
   end;

function getDLLobj:TDLLobj;   export;
var DLLtest:TDLLtest;
begin
   DLLtest:=TDLLtest.create;
   DLLtest.a:=3;
   messageDlg('DLL called',mtInformation,[mbOK],0);
   result:=DLLtest;
end;

exports getDLLobj;

procedure TDLLtest.about;
begin
   messageDlg('Override method',mtInformation,[mbOK],0);
end;

begin
end.

------------------------------------
The results when button1 clicked:
   message box of 'DLL called' poped up, OK
   message box of 'a=3' poped up, OK
   exception box of 'invalid class typecast' poped up, Oh, no.

The following are some testing cases if I make some change:

(1) if the line '(DLLobj as TDLLobj).about' in caller.pas
is changed to 'DLLobj.about' then
it causes a "General Protection Fault"

(2) if theDLL.dpr is rewritten as an unit and
compiled together to make a single .exe then
it's OK, no error.

(3) if 'var DLLtest:TDLLtest' and 'DLLtest:=TDLLtest.create'
in theDLL.dpr changed to
'var DLLtest:TDLLobj' and 'DLLtest:=TDLLobj.create'
then the message box 'method not overrided' poped up, OK.

Would you help me how to solve it?
Thank you very much.

 

Re:DLL function that returns an object, failed when calling methods


Not really qualified to answer John, but I'll give you my thoughts.

I would think that the calling program would need run-time type
information about the descendant class before it could up-cast to an
ancestor (it has to be able to confirm that it is type-compatible).
When you build the project as an exe, do you have TheDLL unit in the
uses clause of Caller or maybe you build them as one unit (This would
be why it works when you build it as an exe).  What happens if you
handle this exception the DLL?

I'd like to hear if you solve this one.  I'd also like to see the
source for the exe build.

Hope this helps,
The Roo

Quote
john%ltp...@tlrouter.tl.gov.tw (Jihg-Hong Lin) wrote:
>I try to write a  DLL-polymorphism tester,
>which is compiled as two parts,
>the main program and the DLL.
>The main program declares a
>base-class with some attributes and methods.
>The DLL inherits the base-class,
>and overrides some methods.
>When the main program calls the DLL,
>the DLL creates an object
>and returns it to the main program.
>So the main program can handle the object,
>such as accessing its attributes and calling its methods,
>even not knowing the descendant class in advance.
>Below are my programs.
>I could access the attribute but couldn't call the method.
><please copy and paste to your Delphi>
>------------------------------------<caller.pas>
>unit Caller;
>interface
>uses
>  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
>  Forms, Dialogs, StdCtrls;
>type
>  TDLLobj=class(TObject)
>  public
>     a:integer;
>     procedure about;   virtual;  
>  end;
>  TForm1 = class(TForm)
>    Button1: TButton;
>    procedure Button1Click(Sender: TObject);
>  private
>    { Private declarations }
>  public
>    { Public declarations }
>  end;
>var
>  Form1: TForm1;
>implementation
>function getDLLobj:TDLLobj;   far;   external 'theDLL';
>{$R *.DFM}
>procedure TForm1.Button1Click(Sender: TObject);
>var DLLobj:TDLLobj;
>begin
>   DLLobj:=getDLLobj;
>   messageDlg('a='+inttostr(DLLobj.a),mtInformation,[mbOK],0);
>   (DLLobj as TDLLobj).about;
>end;
>procedure TDLLobj.about;
>begin
>   messageDlg('method not overrided',mtInformation,[mbOK],0);
>end;
>end.
>------------------------------------<theDLL.dpr>
>library Thedll;
>uses caller,dialogs;
>type
>   TDLLtest=class(TDLLobj)
>   public
>      procedure about;   override;
>   end;
>function getDLLobj:TDLLobj;   export;
>var DLLtest:TDLLtest;
>begin
>   DLLtest:=TDLLtest.create;
>   DLLtest.a:=3;
>   messageDlg('DLL called',mtInformation,[mbOK],0);
>   result:=DLLtest;
>end;
>exports getDLLobj;
>procedure TDLLtest.about;
>begin
>   messageDlg('Override method',mtInformation,[mbOK],0);
>end;
>begin
>end.
>------------------------------------
>The results when button1 clicked:
>   message box of 'DLL called' poped up, OK
>   message box of 'a=3' poped up, OK
>   exception box of 'invalid class typecast' poped up, Oh, no.
>The following are some testing cases if I make some change:
>(1) if the line '(DLLobj as TDLLobj).about' in caller.pas
>is changed to 'DLLobj.about' then
>it causes a "General Protection Fault"
>(2) if theDLL.dpr is rewritten as an unit and
>compiled together to make a single .exe then
>it's OK, no error.
>(3) if 'var DLLtest:TDLLtest' and 'DLLtest:=TDLLtest.create'
>in theDLL.dpr changed to
>'var DLLtest:TDLLobj' and 'DLLtest:=TDLLobj.create'
>then the message box 'method not overrided' poped up, OK.
>Would you help me how to solve it?
>Thank you very much.

Re:DLL function that returns an object, failed when calling methods


   Thanks for responding.

Quote
>When you build the project as an exe, do you have TheDLL unit in the
>uses clause of Caller or maybe you build them as one unit (This would
>be why it works when you build it as an exe).  

I built two units, caller.pas and theDLL2.pas
Their source codes are listed behind.

Quote
>What happens if you handle this exception in the DLL?

I put two exception handlers in each unit

try { in caller.pas procedure TForm1.Button1Click }
   DLLobj.about;
except
   messageDlg('exception in caller',mtInformation,[mbOK],0);
end;

try { in theDLL.pas procedure TDLLtest.about }
   messageDlg('Override method',mtInformation,[mbOK],0);  
except
   messageDlg('exception in DLL',mtInformation,[mbOK],0);
end;

the result: 'exception in caller' poped up

Quote
>I would think that the calling program would need run-time type
>information about the descendant class before it could up-cast to an
>ancestor (it has to be able to confirm that it is type-compatible).

I think Delphi's "create" has provided this information at run-time.
We can see this in the 'two unit one exe' case. The caller doesn't
know the descendant class until run-time.

What I was doubting about is
why 'two unit' succeeded but 'DLL' failed?

Quote
>I'd also like to see the source for the exe build.

The following two units, caller.pas and theDLL2.pas,
were included in callDLL.dpr to build a single callDLL.exe

---------------------------------< caller.pas >
unit Caller;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, StdCtrls;

type
  TDLLobj=class(TObject)
  public
     a:integer;
     procedure about;   virtual;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses theDLL2;

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
var DLLobj:TDLLobj;
begin
   DLLobj:=getDLLobj;
   messageDlg('a='+inttostr(DLLobj.a),mtInformation,[mbOK],0);
   (DLLobj as TDLLobj).about;
end;

procedure TDLLobj.about;
begin
   messageDlg('method not overrided',mtInformation,[mbOK],0);
end;

end.

---------------------------------< theDLL2.pas >

unit Thedll2;

interface
uses caller,dialogs;

function getDLLobj:TDLLobj;

type
   TDLLtest=class(TDLLobj)
   public
      procedure about;   override;
   end;

implementation

function getDLLobj:TDLLobj;
var DLLtest:TDLLtest;
begin
   DLLtest:=TDLLtest.create;
   DLLtest.a:=3;
   messageDlg('DLL called',mtInformation,[mbOK],0);
   result:=DLLtest;
end;

procedure TDLLtest.about;
begin
   messageDlg('Override method',mtInformation,[mbOK],0);
end;

end.

---------------------------------

results

'DLL called' poped up, OK
'a=2' poped up, OK
'Override method' poped up, OK

Other Threads