使用回调从 Inno 安装程序调用 C# DLL

本文关键字:程序 调用 DLL 安装 Inno 回调 | 更新日期: 2023-09-27 18:34:40

我有一个正在运行的Inno Setup脚本,其中我使用Sherlock Software的innocallback.dll。

此 DLL 包装了我的一个过程,以便可以将其传递给 C# DLL。

我不想使用此 DLL,我想直接调用导出的 C# 方法并将回调过程传递给它。

我的问题是:

如何将我的 Inno 安装程序 ( @mycallback( 传递给我的 C# DLL,以便我可以将其用作我的delegate/UnmanagedFunctionPointer

正如我所说,这段代码有效,但我想尽可能少地使用外部 DLL。

这是我的代码:

创新设置脚本

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;

这是我的 C# 代码

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);
    }
}

使用回调从 Inno 安装程序调用 C# DLL

此答案在 Inno Setup 6 中不再有效。有关最新解决方案,请参阅其他答案。

<小时 />无法

放弃包装 InnoCallback 库的使用,因为您根本无法在 Inno Setup 中使用您选择的调用约定定义回调过程,也无法使用 C# 库中的register调用约定(特定于 Delphi 编译器的约定(定义回调。

由于此限制,您必须使用外部库,该库将来自 Inno Setup 的回调方法包装到具有函数的函数中,该函数具有库可以使用的调用约定(InnoCallback 为此使用 stdcall(。

因此,如果您使用支持 Delphi register调用约定的语言编写库,那么您所要求的内容是可能的。出于好奇,在德尔福中你可以写,例如:

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.

然后在Inno Setup中(没有任何包装库(:

[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;

在Inno Setup 6中,有内置的CreateCallback函数,其用途与InnoTools InnoCallback库中WrapCallback函数相同。

因此,您现在可以执行以下操作:

Test(CreateCallback(@mycallback));