使用回调从 Inno Setup 调用 C# DLL [英] Call C# DLL from Inno Setup with callback

查看:30
本文介绍了使用回调从 Inno Setup 调用 C# DLL的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

这个 DLL 包装了我的一个过程,以便它可以传递给 C# DLL.

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

我的问题是:

如何将我的 Inno Setup 过程 (@mycallback) 传递给我的 C# DLL,以便我可以将其用作我的 delegate/UnmanagedFunctionPointer?

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

这是我的代码:

Inno 安装脚本

typeTTimerProc=procedure();TProgressCallback=procedure(progress:Integer);函数 WrapProgressProc(callback:TProgressCallback; paramcount:integer):longword;外部'wrapcallback@files:innocallback.dll stdcall';功能测试(回调:长字):字符串;外部 'Test@files:ExposeTestLibrary.dll stdcall';无功结束程序:布尔值;过程 mycallback(progress:Integer);开始MsgBox(IntToStr(progress), mbInformation, MB_OK);如果进展>15 然后开始结束程序:=真;结尾结尾;函数 InitializeSetup:boolean;无功progCallBack :长字;回调 :长字;msg :长字;msg2 :宽字符串;开始结束程序:= 假;progCallBack:= WrapProgressProc(@mycallback,1);//我们的过程有1个参数测试(程序回调);结果:=真;结尾;

这是我的 C# 代码

公共类TestClass{[UnmanagedFunctionPointer(CallingConvention.StdCall)]公共委托 void ReportProgress(uint progress);公共静态 ReportProgress m_reportProgess;静态 uint m_iProgress;[DllExport("Test", CallingConvention = CallingConvention.StdCall)]静态 int 测试(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);返回0;}静态无效aTimer_Elapsed(对象发送者,System.Timers.ElapsedEventArgs e){m_iProgress++;m_reportProgess(m_iProgress);}}

解决方案

此答案不再适用于 Inno Setup 6.请参阅我的 (@MartinPrikryl) 答案以获取最新解决方案.>

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

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

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

library MyLib;类型TMyCallback = procedure(IntParam: Integer; StrParam: WideString) of object;过程 CallMeBack(Callback: TMyCallback);标准调用;开始回调(123,'你好!');结尾;出口回电;开始结尾.

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

[设置]AppName=我的程序应用版本=1.5DefaultDirName={pf}My Program[文件]来源:MyLib.dll";标志:不要复制

[代码]类型TMyCallback = procedure(IntParam: Integer; StrParam: WideString);过程 CallMeBack(Callback: TMyCallback);外部'CallMeBack@files:mylib.dll stdcall';过程 MyCallback(IntParam: Integer; StrParam: WideString);开始MsgBox(Format('IntParam: %d; StrParam: %s', [IntParam, StrParam]),mbInformation, MB_OK);结尾;程序初始化向导;开始CallMeBack(@MyCallback);结尾;

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

解决方案

This answer is no longer valid with Inno Setup 6. See my (@MartinPrikryl) 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;

这篇关于使用回调从 Inno Setup 调用 C# DLL的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆