2

Using Delphi XE.

When trying to access a Delphi interface object from a DLL, it fails if I try to do it dynamically versus statically.

The interface unit in the dll implements a function to return an instance of the interface. When linked statically, the result is nil when entering the function and everything works. When loading dynamically, the result is non-nil, so when the assignment to result is being done, the IntFCopy code sees it as non-nil and so tries to free it before the assignment, which raises an exception.

Any insight would be appreciated.

The DLL includes testinterfaceload_u and exports testInt:

library testinterfaceload;

uses
  SimpleShareMem,
  SysUtils,
  Classes,
  testinterfaceload_u in 'testinterfaceload_u.pas';

{$R *.res}
exports testInt;

begin
end.

testinterfaceload_u is the unit that defines the interface and simple class implementation:

unit testinterfaceload_u;

interface

type ITestInt = interface
 procedure Test;
end;

{this function returns an instance of the interface}
function testInt : ITestInt; stdcall; export;

type

TTestInt = class(TInterfacedObject,ITestInt)
 procedure Test;
end;



implementation

function testInt : ITestInt;
begin
//debugger shows result as non-nil ITestInt even before this assignment, when dynamic
  result := TTestInt.Create;  
end;

procedure TTestInt.Test;
var
  i : integer;
begin
  i := 0;
end;



end.

Here's a console app that loads the dll and calls the testInt function to return the interface:

program testload_console;

{$APPTYPE CONSOLE}

uses

SysUtils,
  Windows,
  testinterfaceload_u in 'testinterfaceload_u.pas';

type
  TTestInt = function() : ITestInt;

var
   TestInt: TTestInt;
   NewTestInt : ITestInt;
   DLLHandle: THandle;
begin
  DLLHandle := LoadLibrary('testinterfaceload.dll');
  if (DLLHandle < HINSTANCE_ERROR) then
       raise Exception.Create('testinterfaceload.dll can not be loaded or not found. ' +     SysErrorMessage(GetLastError));
  @TestInt := GetProcAddress(DLLHandle, 'testInt');
  try
    if Assigned(TestInt) then
      NewTestInt := TestInt;
  except on e:Exception do
    WriteLn(Output,e.Message);
  end;
end.
Dan Hacker
  • 73
  • 2
  • 8
  • Why use explicit LoadLibrary rather than implicit linking? – David Heffernan Jul 08 '11 at 20:29
  • I'm having a problem in InstallAware trying to run one of my dll's that calls another of my dll's. InstallAware seems to choke when trying to unload the libraries if the second dll is loaded, so I was trying to handle its lifespan in the first dll. – Dan Hacker Jul 08 '11 at 20:37
  • 1
    `if (DLLHandle < HINSTANCE_ERROR) then` is incorrect. `LoadLibrary` returns 0 (i.e. `NULL`) in case of error. Also, `export` is not needed and just serves to confuse. Remove it. – David Heffernan Jul 08 '11 at 21:14

4 Answers4

5

Your interface definition should contain a GUID and each function needs the "stdcall" declaration. Without it you might run into problems..

type ITestInt = interface
  ['{AA286610-E3E1-4E6F-B631-F54BC6B31150}']
  procedure Test; stdcall
end;
Konrad Viltersten
  • 36,151
  • 76
  • 250
  • 438
timelord2102
  • 51
  • 1
  • 1
5

TTestInt needs to be declared as stdcall in the code that imports the DLL.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Hm, interesting, didnt know about it. Care to give a link to documentation on that, if you have no objections? My goal is learning something new, not just idle argument. – Premature Optimization Jul 08 '11 at 23:41
  • 1
    @Downvoter It's [here](http://docwiki.embarcadero.com/RADStudio/en/Program_Control#Handling_Function_Results): For a string, dynamic array, method pointer, or variant result, the effects are the same as if the function result were declared as an additional var parameter following the declared parameters. In other words, the caller passes an additional 32-bit pointer that points to a variable in which to return the function result. – David Heffernan Jul 09 '11 at 19:37
  • Thanks, worth to know the "theoretical" part. – Premature Optimization Jul 09 '11 at 20:57
3

Taking a stab at things: Try adding a calling method (stdcall, pascal, etc.) to the declaration of the dll's testInt function and the TTestInt fnction type in the console app.

Erik Knowles
  • 999
  • 7
  • 15
  • stdcall on TTestInt function type. Unfortunately, it didn't solve my InstallAware problem. – Dan Hacker Jul 08 '11 at 21:31
  • That's clearly something else then. Once you have the calling conventions lined up then the code you present above is fine. Calling convetion mismatch perfectly explains "debugger shows result as non-nil ITestInt even before this assignment, when dynamic" – David Heffernan Jul 08 '11 at 21:41
1

necroposting mode on

function testInt : ITestInt;
begin
//debugger shows result as non-nil ITestInt even before this assignment, when dynamic
  result := TTestInt.Create;  
end;

should be a procedure like this

procedure testInt(out intf: ITestInt); stdcall;
begin
 intf := TTestInt.Create;  
end;