Where is the problem with importing function from C++ DLL

179 views Asked by At

I'm trying to learn how does function import from DLL work. I created an Inno Setup script with wizard:

[Files]
Source: "TestDll\x64\Release\TestDll.dll"; Flags: dontcopy;
[Code]
procedure MyDllFunc(hWnd: Integer; lpText, lpCaption: AnsiString; uType: Cardinal);
  external '[email protected] stdcall delayload setuponly';

function TestBool() : Boolean;
  external '[email protected] stdcall delayload setuponly';

procedure InitializeWizard;
var
  TestVar: Boolean;
  hWnd: Integer;
begin
  Log('InitializeWizard');
  TestVar := TestBool();
  if TestVar then begin
    Log('TestVar: True');
  end;
  hWnd := StrToInt(ExpandConstant('{wizardhwnd}'));
  MyDllFunc(hWnd, 'Hello from custom DLL function', 'MyDllFunc', MB_OK);
end;

DLL is created with MS Visual Studio 2022, the C++ project created as "Dynamic-Link Library (DLL)" project, code for the MyDllFunc function is copied from Inno Setup examples, here is code for dll:

// dllmain.cpp
#include <windows.h>

void __stdcall MyDllFunc(HWND hWnd, char *lpText, char *lpCaption, UINT uType)
{
    MessageBoxA(hWnd, lpText, lpCaption, uType);
}

bool __stdcall TestBool()
{
    return true;
}

When running the Inno Setup script I always get error for both imported functions:

Runtime error. Line 43: couldn't call proc.

Inno Setup log excerpt regarding DLL:

[16:08:04.542]   -- DLL function import --
[16:08:04.567]   Function name: MyDllFunc
[16:08:04.570]   DLL name: setup:TestDll.dll
[16:08:04.571]   Dest DLL name: TestDll.dll
[16:08:04.571]   Importing the DLL function.
[16:08:04.572]   Successfully imported the DLL function. Delay loaded? Yes
[16:08:04.572]   -- DLL function import --
[16:08:04.573]   Function name: TestBool
[16:08:04.573]   DLL name: setup:TestDll.dll
[16:08:04.574]   Dest DLL name: TestDll.dll
[16:08:04.575]   Importing the DLL function.
[16:08:04.575]   Successfully imported the DLL function. Delay loaded? Yes

However, Inno Setup example "CodeDll.iss" is working. So I can suppose, that there is problem with my C++ project.

1

There are 1 answers

0
qloq On BEST ANSWER

I was asked to post my solution as an answer, so here it is (note, I'm not completely understand all the things regarding this theme, so I will post only points you may consider, if you got in the same situation as me):

  1. Use x86 (32-bit) release mode to build your DLL;
  2. Enclose your code into extern "C" option;
  3. Add the __declspec(dllexport) keyword to your functions declaration;
  4. Use the cdecl convension in Pascal-script;
  5. Use files: keyword in DLL path;
  6. Also, I noted that Pascal-script demands only single space in export declaration. For example this line (with 2 spaces between cdecl and delayloadexternal 'TestBool@files:TestDll.dll cdecl delayload setuponly';) – will trigger an error.

Here is the full code, that works for me:

// dllmain.cpp
#include <windows.h>

extern "C"
{
    void __declspec(dllexport) MyDllFunc(HWND hWnd, char *lpText, char *lpCaption, UINT uType)
    {
        MessageBoxA(hWnd, lpText, lpCaption, uType);
    }

    bool __declspec(dllexport) TestBool()
    {
        return true;
    }
}

Inno Setup script:

[Files]
Source: "TestDll\Release\TestDll.dll"; Flags: dontcopy;
[Code]
procedure MyDllFunc(hWnd: Integer; lpText, lpCaption: AnsiString; uType: Cardinal);
  external 'MyDllFunc@files:TestDll.dll cdecl delayload setuponly';

function TestBool() : Boolean;
  external 'TestBool@files:TestDll.dll cdecl delayload setuponly';

procedure InitializeWizard;
var
  TestVar: Boolean;
  hWnd: Integer;
begin
  Log('InitializeWizard');
  TestVar := TestBool();
  if TestVar then begin
    Log('TestVar: True');
  end;
  hWnd := StrToInt(ExpandConstant('{wizardhwnd}'));
  MyDllFunc(hWnd, 'Hello from custom DLL function', 'MyDllFunc', MB_OK);
end;