Reputation: 3153
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
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
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