从 C# 传递结构作为参数调用非托管 DLL
Calling an unmanaged DLL from C# passing structs as parameters
我有一个 DLL 文件,但没有类型库和文档。我所拥有的是使用它的 Delphi (Pascal) 代码。我正在尝试使用 .Net 5 C# 调用此 DLL。
我得到的错误是
System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'
这是我的尝试:
[StructLayout(LayoutKind.Sequential)]
public struct MyDLLQuery
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string Frame;
public int Width { get; set; }
public double Cost { get; set; }
...
}
[StructLayout(LayoutKind.Sequential)]
public struct MyDLLResult
{
public int NumTubes { get; set; }
public double Spacing { get; set; }
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string Material;
...
}
internal static class MyDLLWrapper
{
const string DLL = @"bin\MyDLL.dll";
private static CallbackDelegate delegateInstance;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CallbackDelegate();
[DllImport(DLL, CallingConvention = CallingConvention.StdCall)]
private static extern void CalculateData(MyDLLQuery indata, out MyDLLResult outdata, CallbackDelegate f);
internal static void Query()
{
MyDLLQuery indata = new MyDLLQuery();
indata.Frame = "G";
indata.Width = 1300;
indata.Cost = 50.0;
...
MyDLLResult outdata = new MyDLLResult();
delegateInstance = MyFunc;
CalculateData(indata, out outdata, delegateInstance);
}
public static void MyFunc()
{
}
}
这里是 Delphi 调用相同 DLL 的代码(确实有效):
TInParams = record
Frame: array [0..15] of Char;
Width: Integer;
Cost: Double;
...
TOutParams = record
NumTubes: Integer;
Spacing: Double;
Material: array [0..15] of Char;
...
TInfoCallBackProcedure = procedure() cdecl;
const LIBNAME = 'MyDLL.dll';
procedure CalculateData(InData: TInParams; var OutData: TOutParams; Callback: TInfoCallbackProcedure); stdcall external LIBNAME;
var
InParams: TInParams;
OutParams: TOutParams;
SetInData(sourceData, InParams); // assigns a value to all fields in InParams
FillChar(OutParams, SizeOf(OutParams), 0);
CalculateData(InParams, OutParams, Callback); // No callback = nil
我已确保结构中的所有字段的顺序与 Delphi 代码中的顺序相同。
终于解决了这个问题。我之前曾尝试在 DLL 调用中使用 Charset unicode,但我错过了在结构上设置它:
[DllImport(DLL, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern void CalculateData(MyDLLQuery indata, out MyDLLResult outdata, CallbackDelegate f);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MyDLLQuery
...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MyDLLResult
...
StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct Result
{
//SizeCount bezieht sich anscheinden auf Char (hier 2 Byte) und
// nicht auf die Bytegröße des Felds
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)] public string BIC; //Delphi: array[0..10] of char
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 35)] public string IBAN; //Delphi: array[0..33] of char
[MarshalAs(UnmanagedType.I4)] public BACIbanKonverterStatus Status;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)] public string StatusMessage;
//Delphi: array[0..199] of char
[MarshalAs(UnmanagedType.Bool)] //Delphi: Longbool
public bool Success;
}
已在 Delphi 中实现如下:
TRecIban=packed record
public
Bic : array[0..10] of char;
Filler_1 : Char;
Iban : array[0..33] of char;
Filler_2 : Char;
Status : integer;
StatusMessage: array[0..199] of char;
Filler_3 : Char;
Success : Longbool;
end
C#中字符串的结尾映射到Delphi中的Filler_X。今天我将在 C# 中编组 [MarshalAs (UnmanagedType.LPWStr)]。在 Delphi 中,我会使用 PChar。那么填充物就不用了
我有一个 DLL 文件,但没有类型库和文档。我所拥有的是使用它的 Delphi (Pascal) 代码。我正在尝试使用 .Net 5 C# 调用此 DLL。
我得到的错误是
System.AccessViolationException: 'Attempted to read or write protected memory. This is often an indication that other memory is corrupt.'
这是我的尝试:
[StructLayout(LayoutKind.Sequential)]
public struct MyDLLQuery
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string Frame;
public int Width { get; set; }
public double Cost { get; set; }
...
}
[StructLayout(LayoutKind.Sequential)]
public struct MyDLLResult
{
public int NumTubes { get; set; }
public double Spacing { get; set; }
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 16)]
public string Material;
...
}
internal static class MyDLLWrapper
{
const string DLL = @"bin\MyDLL.dll";
private static CallbackDelegate delegateInstance;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void CallbackDelegate();
[DllImport(DLL, CallingConvention = CallingConvention.StdCall)]
private static extern void CalculateData(MyDLLQuery indata, out MyDLLResult outdata, CallbackDelegate f);
internal static void Query()
{
MyDLLQuery indata = new MyDLLQuery();
indata.Frame = "G";
indata.Width = 1300;
indata.Cost = 50.0;
...
MyDLLResult outdata = new MyDLLResult();
delegateInstance = MyFunc;
CalculateData(indata, out outdata, delegateInstance);
}
public static void MyFunc()
{
}
}
这里是 Delphi 调用相同 DLL 的代码(确实有效):
TInParams = record
Frame: array [0..15] of Char;
Width: Integer;
Cost: Double;
...
TOutParams = record
NumTubes: Integer;
Spacing: Double;
Material: array [0..15] of Char;
...
TInfoCallBackProcedure = procedure() cdecl;
const LIBNAME = 'MyDLL.dll';
procedure CalculateData(InData: TInParams; var OutData: TOutParams; Callback: TInfoCallbackProcedure); stdcall external LIBNAME;
var
InParams: TInParams;
OutParams: TOutParams;
SetInData(sourceData, InParams); // assigns a value to all fields in InParams
FillChar(OutParams, SizeOf(OutParams), 0);
CalculateData(InParams, OutParams, Callback); // No callback = nil
我已确保结构中的所有字段的顺序与 Delphi 代码中的顺序相同。
终于解决了这个问题。我之前曾尝试在 DLL 调用中使用 Charset unicode,但我错过了在结构上设置它:
[DllImport(DLL, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern void CalculateData(MyDLLQuery indata, out MyDLLResult outdata, CallbackDelegate f);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MyDLLQuery
...
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MyDLLResult
...
StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)]
public struct Result
{
//SizeCount bezieht sich anscheinden auf Char (hier 2 Byte) und
// nicht auf die Bytegröße des Felds
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 12)] public string BIC; //Delphi: array[0..10] of char
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 35)] public string IBAN; //Delphi: array[0..33] of char
[MarshalAs(UnmanagedType.I4)] public BACIbanKonverterStatus Status;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 201)] public string StatusMessage;
//Delphi: array[0..199] of char
[MarshalAs(UnmanagedType.Bool)] //Delphi: Longbool
public bool Success;
}
已在 Delphi 中实现如下:
TRecIban=packed record
public
Bic : array[0..10] of char;
Filler_1 : Char;
Iban : array[0..33] of char;
Filler_2 : Char;
Status : integer;
StatusMessage: array[0..199] of char;
Filler_3 : Char;
Success : Longbool;
end
C#中字符串的结尾映射到Delphi中的Filler_X。今天我将在 C# 中编组 [MarshalAs (UnmanagedType.LPWStr)]。在 Delphi 中,我会使用 PChar。那么填充物就不用了