0

I have a problem with dll that contains procedure with Firebird TIBScript.

uses
  System.SysUtils,
  System.Classes,
  DLLMainData in 'DLLMainData.pas' {DataModule2: TDataModule};

{$R *.res}

Procedure RunScript();stdcall;
begin
   TDataModule2.RunScriptProc;
end;

exports
  RunScript;

begin
end.

dll procedure

class function TDataModule2.RunScriptProc: boolean;
begin
  with self.Create(nil) do
  begin
    try
      IBDatabase1.Open;
      IBScript1.ExecuteScript;
    finally
      IBDatabase1.Close;
      Free;
    end;
  end;
end;

Now I call this procedure from an exe as follows:

procedure TForm2.Button1Click(Sender: TObject); 
var
  Handle: Integer;    
  LibraryProc :procedure();stdcall;
begin
   Handle := LoadLibrary('dllka.dll');
   if Handle <> 0 then
   begin
     try
       LibraryProc := GetProcAddress(Handle,'RunScript');
       if @LibraryProc = nil then
         raise Exception.Create('erorr')
       else
         LibraryProc();
     finally
       Showmessage('Before free library');
       FreeLibrary(Handle);
       Handle := 0;
       LibraryProc := nil
     end
   end;
   Showmessage('ok');
end;

When TIBScript raise exception while executing (problem with sql etc.) in main App (witch this procedrue is calling from) hangs on FreeLibrary(). When Script was executed without problems everything works fine. I create small example because i thought that problem was with passing params to library but it is not.

Appreciate any assistance. I am using Delphi XE2. Thanks

2 Answers2

4

A DLL is contracted not to throw exceptions out of exported functions. Your DLL breaks this contract.

Fix the problem by handling all exceptions in any exported function. Convert those exceptions into, for instance, an error code return value. This is one of the few times that it is reasonable to use an indiscriminate catch-all exception handler.

Your exported function would look like this:

function RunScript: Integer; stdcall;
begin
  Try
    TDataModule2.RunScriptProc;
    Result := ERROR_CODE_SUCCESS;
  Except
    Result := ...; // your code to convert exception to error code goes here
  End;
end;
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • I do like you suggested and also I put an exceptions handling everywhere, but it still hangs on FreeLibrary – Piotr Olszewski Jul 25 '17 at 12:10
  • That sounds like a problem with `TIBScript`. I'm a bit sceptical of you saying "I put an exceptions handling everywhere". That sounds like you are trying stuff at random without a clear understanding. At this point, your code is probably nothing like that in the question and if we engage further, based on part experience, it is liable to become a wild goose chase. Perhaps you need a new question with a [mcve]. – David Heffernan Jul 25 '17 at 12:17
  • FWIW, I would regard it as rather pointless to keep loading and unloading the same DLL. Load it once at startup. Or even better, link to it implicitly using the `external` directive. I've seen the code in the question that calls `LoadLibrary` many times before. It's really awful code. `LoadLibrary` does not return `Integer`. It returns `HMODULE`. I'd imagine that the expedient way to deal with this problem is to stop calling `FreeLibrary`. Indeed, I wonder why you don't just get rid of the DLL completely and build a single monolithic executable. – David Heffernan Jul 25 '17 at 12:30
  • David, "I put an exceptions handling everywhere" i mean i put it only i two places :). This library with this function (IBScript) will be run only ones at the start MainApp. MainApp starts, check if it is nessesery to make some changes on databese structure and if it is run this procedure from this dll and thast's all, otherwise i don't even need to load this dll. – Piotr Olszewski Jul 25 '17 at 12:39
  • That sounds to me also like a problem with TIBScript, I have this dll created on Delphi 5 and everything works without problemes. – Piotr Olszewski Jul 25 '17 at 12:41
0

@David Heffemen is correct. You cannot throw an exception from a DLL, but you can exit with an ExitCode if your intention is to exit the DLL when Delphi catches an exception.

I inherited code that was raising an exception when the DLL could not load, but was just hanging when the DLL could not be found. I solved it checking that the file exists and the handle was assigned, setting ExitCode to 1, and exiting. It produced a nice exception in my C# when I called an exported function:

procedure LoadMyDll();
  begin
    if (FileExists(MY_DLL_NAME)) then
      _hMy32Dll := LoadLibrary(PChar(MY_DLL_NAME));
    if (_hMy32Dll = 0) then
      begin
        System.ExitCode:=1;
        exit;
      end;
  end;

The Exception message in C# was:

System.DllNotFoundException: Unable to load DLL 'C:\src\MyDLL\Delphi\Win32\Debug\MyDLL.dll': A dynamic link library (DLL) initialization routine failed. (Exception from HRESULT: 0x8007045A)
   at MyDLLTest.Program.Min(Int32 x, Int32 y)
   at MyDLLTest.Program.Main(String[] args) in C:\src\MyDLL\CSharp\MyDLLTest\MyDLLTest\Program.cs:line 23
Kevin S. Miller
  • 913
  • 1
  • 9
  • 21