如何使用 DNNE 从 Inno Setup 调用 .NET DLL?

小红帽

我之前成功地使用了Unmanaged ExportsDllExport来使用 Inno Setup 的 .NET DLL 文件。

但是现在我正试图让它与DNNE一起工作。

我有以下针对 x86 的 C# 代码

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <EnableDynamicLoading>true</EnableDynamicLoading>
    <Platforms>x86</Platforms>
    <RuntimeIdentifier>win-x86</RuntimeIdentifier>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="DNNE" Version="1.0.31" />
  </ItemGroup>

</Project>
using System.Runtime.InteropServices;

namespace DNNETest
{
    internal static class NativeMethods
    {
        [DllImport("User32.dll", EntryPoint = "MessageBox",
            CharSet = CharSet.Auto)]
        internal static extern int MsgBox(
            IntPtr hWnd, string lpText, string lpCaption, uint uType);
    }

    public class Class1
    {
        [UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
        public static void Test()
        {
            _ = NativeMethods.MsgBox(IntPtr.Zero, "Hello from C#", ":)", 0);
            return;
        }
    }
}

我制作了一个小型控制台应用程序来验证导出的代码是否正常工作:

using System.Runtime.InteropServices;

NE.Test();

public static class NE
{
    [DllImport("DNNETestNE", CallingConvention = CallingConvention.StdCall)]
    public extern static void Test();
}

工作正常!


现在我尝试将其移至 Inno Setup:

[Files]
Source: Files\Dotnet\DNNETest.deps.json; Flags: dontcopy
Source: Files\Dotnet\DNNETest.dll; Flags: dontcopy
Source: Files\Dotnet\DNNETest.runtimeconfig.json; Flags: dontcopy
Source: Files\Dotnet\DNNETestNE.dll; Flags: dontcopy
procedure Test();
external 'Test@{tmp}\DNNETestNE.dll stdcall delayload';

procedure InitializeDotnet;
begin
  ExtractTemporaryFiles('{tmp}\DNNETest.deps.json');
  ExtractTemporaryFiles('{tmp}\DNNETest.dll');
  ExtractTemporaryFiles('{tmp}\DNNETest.runtimeconfig.json');
  ExtractTemporaryFiles('{tmp}\DNNETestNE.dll');
  Test();
end;

会崩溃Could not call proc

我也试过

external 'Test@{tmp}\DNNETestNE.dll,DNNETest.dll stdcall delayload loadwithalteredsearchpath';

玩弄,的组合AnyCPU但无济于事x86x64

但同样的错误

我不确定我还能尝试什么,因为这些步骤适用于其他 DllImport 包。

小红帽

它不起作用,因为

编译器还使用下划线 (_) 前缀和由 at 符号 (@) 后跟参数列表中的字节数(十进制)组成的后缀修饰使用 __stdcall 调用约定的 C 函数。

来源:https ://docs.microsoft.com/en-us/cpp/build/reference/exports?view=msvc-170

快速修复是在 C# 和 Pascal 定义中使用cdecl而不是stdcall

如果您真的想使用stdcall,请继续阅读...


修复它:将此行添加到<PropertyGroup>

<DnneWindowsExportsDef>$(MSBuildProjectDirectory)\DnneWindowsExports.def</DnneWindowsExportsDef>

添加以下内容:

EXPORTS
   Test=Test

替换Test为您要导出的功能


我制作了一个小型控制台应用程序,它将生成此文件:只需添加对您的项目的引用并将类名替换为typeof您的导出文件。

using DNNETest;
using System.Text;

var names = typeof(NativeExports).GetMethods(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).Select(m => m.Name).ToArray();

var output = new StringBuilder();
output.AppendLine("EXPORTS");

foreach (var name in names)
{
    output.AppendLine($"\t{name}={name}");
}

var result = output.ToString();
Console.WriteLine(result);
File.WriteAllText(@"SomeLocation\DnneWindowsExports.def", result);

我做了下面的例子来展示它的工作原理

public static class NativeExports
{
    [UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
    public static void Test()
    {
        _ = NativeMethods.MsgBox(IntPtr.Zero, nameof(Test), "C#", 0);
    }

    [UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
    public static void SendInt(int value)
    {
        _ = NativeMethods.MsgBox(IntPtr.Zero, $"{nameof(SendInt)}: {value}", "C#", 0);
    }

    [UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
    public static void SendString(IntPtr value)
    {
        var message = Marshal.PtrToStringUni(value);
        _ = NativeMethods.MsgBox(IntPtr.Zero, $"{nameof(SendString)}: {message}", "C#", 0);
    }

    [UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
    public static unsafe void ReturnString(IntPtr value, IntPtr* result)
    {
        var message = Marshal.PtrToStringUni(value);

        var returnString = new string(message.Reverse().ToArray());
        _ = NativeMethods.MsgBox(IntPtr.Zero, $"{nameof(ReturnString)}: {message} => {returnString}", "C#", 0);

        *result = Marshal.StringToBSTR(returnString);
    }

    [UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
    public static int ReturnInt(int input)
    {
        return input;
    }

    public delegate bool ExpandConstantDelegate([MarshalAs(UnmanagedType.LPWStr)] string input, [MarshalAs(UnmanagedType.BStr)] out string output);
    [UnmanagedCallersOnly(CallConvs = new[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
    public static void CallExpandConstantCallback(IntPtr callbackPtr)
    {
        var ExpandConstant = Marshal.GetDelegateForFunctionPointer<ExpandConstantDelegate>(callbackPtr);

        var constant = "{tmp}";
        ExpandConstant(constant, out var result);

        _ = NativeMethods.MsgBox(IntPtr.Zero, $"{nameof(ExpandConstant)}({constant}) => {result}", "C#", 0);
    }
}
procedure Test();
external 'Test@{tmp}\DNNETestNE.dll stdcall delayload';

procedure SendInt(value: Integer);
external 'SendInt@{tmp}\DNNETestNE.dll stdcall delayload';

procedure SendString(value: string);
external 'SendString@{tmp}\DNNETestNE.dll stdcall delayload';

procedure ReturnString(value: string; out outValue: WideString);
external 'ReturnString@{tmp}\DNNETestNE.dll stdcall delayload';

function ReturnInt(value: Integer) : Integer;
external 'ReturnInt@{tmp}\DNNETestNE.dll stdcall delayload';

procedure ExpandConstantWrapper(const toExpandString: string; out expandedString: WideString);
begin
  expandedString := ExpandConstant(toExpandString);
end;

procedure CallExpandConstantCallback(callback: Longword);
external 'CallExpandConstantCallback@{tmp}\DNNETestNE.dll stdcall delayload';

procedure InitializeDotnet;
var
  outString: WideString;
begin
  ExtractTemporaryFiles('{tmp}\DNNETest*');
  Test();
  SendInt(1234);
  SendString('Hello World');
  ReturnString('ReverseMe!', outString);
  MessageBox(outString, 0);
  MessageBox(IntToStr(ReturnInt(4321)), 0);
  CallExpandConstantCallback(CreateCallback(@ExpandConstantWrapper));
end;

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

使用函数在 Inno Setup 中填充的已分配字符缓冲区调用 DLL 函数

通过回调从Inno Setup调用C#DLL

Inno Setup 以字符串为参数调用 DLL

Inno Setup-具有依赖项的外部.NET DLL

如何在Inno Setup iss文件中添加DLL函数?

使用 Inno Setup 安装程序安装 Windows shell 扩展 DLL

如何在 Inno Setup iss 文件中调用 GetNativeSystemInfo?

Inno Setup 6不能在字符串参数中使用DLL函数,而可以在Inno Setup 5中使用

如何使用Windows Shell32.dll中的图标作为Inno Setup快捷方式

无法从[代码]调用Inno Setup ParseVersion

在Inno Setup中从过程调用函数?

Inno Setup如何检查系统上是否存在MSVCR100.dll

如何在Inno Setup中将VCL样式应用于基于DLL的表单?

在Inno Setup的[Code]部分中,可以使用64位DLL吗?

从.NET使用Dll的指针参数调用函数

如何从Inno Setup安装JRE?

如何使用Inno Setup检查Internet连接

如何在Inno Setup中使用UTC

在Inno Setup的Setup部分中使用GetStringFileInfo

JNI调用.NET dll

使用inno Setup安装程序调用RegQueryDWordValue获取Office Excel版本

Inno Setup 32位和64位dll安装

Inno Setup:将DLL放在子目录中

在Inno Setup中加载用于卸载过程的外部DLL

如何在Inno Setup中将VCL样式应用于基于DLL的表单?新样式将无法加载

在哪里可以找到VclStylesinno.dll,以便能够在Inno Setup脚本中使用VCL样式?

在使用 Inno Setup 安装的两个应用程序之间共享注册的 .NET 程序集

使用Inno Setup启动.NET Core安装程序失败,结果代码为2

Inno Setup调用或复制本机Windows文件复制操作