Call C# DLL from Inno Setup with callback
Asked Answered
S

2

7

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);
    }
}
Stevana answered 18/12, 2014 at 15:17 Comment(0)
T
5

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;
Timisoara answered 18/12, 2014 at 16:59 Comment(1)
I just saw you got the inno-setup gold Badge. Congratulations :)Stevana
R
4

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));
Reinforce answered 12/6, 2019 at 7:19 Comment(1)
Update for Inno Setup 6.0.3: InnoCallback library stopped working completely and native CreateCallback() is now the only option.Osteoblast

© 2022 - 2024 — McMap. All rights reserved.