我之前成功地使用了Unmanaged Exports和DllExport来使用 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
,但无济于事x86
x64
但同样的错误
我不确定我还能尝试什么,因为这些步骤适用于其他 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] 删除。
我来说两句