5
votes

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

2
Install stack tracer like Exception Dialog from Jedi CodeLibrary (used by Delphi IDE), Eureka, madExcept, etc... Run with debug-info. Check stacktrace of the exception to understand where the train went down the track.Arioch 'The
Did you alreay solve that issue? I have the some problem. But only on XP. On Win7 it runs fine. I just need to create and free the form in the dll, then that error occurres after the exe is closed. I am using DelphiXE.markus_ja

2 Answers

6
votes

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.

3
votes

"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 ?