Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi: Access violation after calling procedure in dll

I have created a procedure in a dll that opens a form and then prints a report. This procedure works perfectly from an exe. I have wrapped the unit that contains this procedure and forms in a dll and exported the procedure as follows:

{$R *.res}


Procedure PrintTopSellers; stdcall;
begin
  Form1 := TForm1.create(nil);
  GetMonth := TGetMonth.create(nil);
  Form1.PrintTopSellers;
end;


exports PrintTopSellers;

begin
end.

Now I call this procedure PrintTopSellers from an exe as follows:

procedure TForm1.Button5Click(Sender: TObject);
type
  TRead_iButton = function :integer;
var
    DLL_Handle: THandle;
    Read_iButton: TRead_iButton;
Begin
    DLL_Handle := LoadLibrary('c:\Catalog.dll');
    if DLL_Handle <> 0 then
    begin
       @Read_iButton:= GetProcAddress(DLL_Handle, 'PrintTopSellers');
        Read_iButton;
    end;
    application.ProcessMessages;
    FreeLibrary(DLL_Handle);

end;

The call to the procedure works perfectly. However, after I close the calling exe, I get an access violation - "Access violation at address 00BAC89C. Read of address 00BAC89C."

Appreciate any assistance. I am using Delphi 7. Thanks

like image 586
user1685639 Avatar asked Sep 20 '12 10:09

user1685639


2 Answers

You are creating Form1, a windowed control, in the DLL. But you never destroy it. Then you unload the DLL which unloads the code that implements the window procedures for all windows created by the DLL. Presumably when the process shuts down, the window procedures are called, but there is no code there anymore.

Fix the problem by destroying all objects that the DLL creates. It looks to me like the best approach is to do that when PrintTopSellers terminates.

Procedure PrintTopSellers; stdcall;
begin
  Form1 := TForm1.create(nil);
  try
    GetMonth := TGetMonth.create(nil);
    try
      Form1.PrintTopSellers;
    finally
      GetMonth.Free;
    end;
  finally
    Form1.Free;
  end;
end;

In the code that loads the DLL, TRead_iButton is declared incorrectly. It should be

TRead_iButton = procedure; stdcall;

But that doesn't actually explain the problem here since the signature mismatch is benign for a parameterless procedure.

like image 52
David Heffernan Avatar answered Oct 18 '22 19:10

David Heffernan


"TRead_iButton = function: integer; register;"

"Procedure PrintTopSellers; stdcall;"

Absolutely different conventions/types, ain't them ?

Make them the same. And better ditch DLL and use packages (BPL), then compiler would make you safe from such errors


We also don't see the code neither in Form1.PrintTopSellers nor in TGetMonth. The all can leave some dangling pointers in the host exe, that would get accesses after DLL unloaded.


Show exactly chain of function calls leading to AV - it is called stack trace. Debug info + some excaption interrupt like Jedi CodeLibrary (used by Delphi IDE) madExcept, EurekaLog, synopse log and a lot of other exist.

Display the call stack in a Delphi Win32 application


Does DLL or EXE use Runtime packages ?

like image 32
Arioch 'The Avatar answered Oct 18 '22 20:10

Arioch 'The