Bongo
Bongo

Reputation: 3153

Call C# DLL from Inno Setup with callback

I have a running Inno Setup script, wherein I use innocallback.dll by Sherlock Software.

This DLL wraps a procedure of mine so that it can be passed to a C# DLL.

I don't want to use this DLL, I want to call my exported C# method directly and pass to it the callback procedure.

My question is:

How can I pass my Inno Setup procedure (@mycallback) to my C# DLL so that I can use it as my delegate/UnmanagedFunctionPointer?

As I said this code works, but I want to use as little external DLL's as possible.

Here is my code:

Inno Setup Script

type
  TTimerProc=procedure();
  TProgressCallback=procedure(progress:Integer);
    
function WrapProgressProc(callback:TProgressCallback; paramcount:integer):longword;
  external 'wrapcallback@files:innocallback.dll stdcall';

function Test(callback:longword): String;
  external 'Test@files:ExposeTestLibrary.dll stdcall';

var
  endProgram : Boolean;

procedure mycallback(progress:Integer);
begin
  MsgBox(IntToStr(progress), mbInformation, MB_OK); 
  if progress > 15 then
  begin
    endProgram := True;
  end
end;
  
function InitializeSetup:boolean;
var
  progCallBack   : longword;
  callback       : longword;
  msg            : longword;
  msg2           : widestring;
begin
  endProgram := False;
  progCallBack:= WrapProgressProc(@mycallback,1); //Our proc has 1 arguments
  Test(progCallBack);
  result:=true;
end;

And this is my C# code

public class TestClass
{
    [UnmanagedFunctionPointer(CallingConvention.StdCall)]
    public delegate void ReportProgress(uint progress);

    public static ReportProgress m_reportProgess;
    static uint m_iProgress;
    
    [DllExport("Test", CallingConvention = CallingConvention.StdCall)]
    static int Test(ReportProgress rProg)
    {
        m_iProgress = 0;
        m_reportProgess = rProg;
        System.Timers.Timer pTimer = new System.Timers.Timer();
        pTimer.Elapsed += aTimer_Elapsed;
        pTimer.Interval = 1000;
        pTimer.Enabled = true;
        GC.KeepAlive(pTimer);
        return 0;
    }

    static void aTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
    {
        m_iProgress++;
        m_reportProgess(m_iProgress);
    }
}

Upvotes: 7

Views: 2539

Answers (2)

TLama
TLama

Reputation: 76693

This answer is no longer valid with Inno Setup 6. See the other answer for up to date solution.


There's no way to drop the usage of the wrapping InnoCallback library since you simply cannot define a callback procedure with a calling convention of your choice in Inno Setup, nor you can define a callback with the register calling convention (the one specific to Delphi compiler) in your C# library.

Due to this limit you must use an external library, which wraps a callback method from Inno Setup into a function with a calling convention that your library can consume (InnoCallback uses stdcall for that).

So, what you're asking for would be possible if you were writing your library in a language that supports Delphi's register calling convention. Out of curiosity, in Delphi you could write e.g.:

library MyLib;

type
  TMyCallback = procedure(IntParam: Integer; StrParam: WideString) of object;

procedure CallMeBack(Callback: TMyCallback); stdcall;
begin
  Callback(123, 'Hello!');
end;

exports
  CallMeBack;

begin
end.

And in Inno Setup then (without any wrapping library):

[Setup]
AppName=My Program
AppVersion=1.5
DefaultDirName={pf}\My Program

[Files]
Source: "MyLib.dll"; Flags: dontcopy
[Code]
type
  TMyCallback = procedure(IntParam: Integer; StrParam: WideString);

procedure CallMeBack(Callback: TMyCallback);
  external 'CallMeBack@files:mylib.dll stdcall';

procedure MyCallback(IntParam: Integer; StrParam: WideString);
begin
  MsgBox(Format('IntParam: %d; StrParam: %s', [IntParam, StrParam]),
    mbInformation, MB_OK);
end;

procedure InitializeWizard;
begin
  CallMeBack(@MyCallback);
end;

Upvotes: 5

Martin Prikryl
Martin Prikryl

Reputation: 202271

With Inno Setup 6, there's built-in CreateCallback function that serves the same purpose as WrapCallback function from InnoTools InnoCallback library.

So you can now do:

Test(CreateCallback(@mycallback));

Upvotes: 4

Related Questions