我有一个本机 DLL,用 Delphi 编写,积极使用回调机制:回调函数被“注册”,然后从 DLL 内部调用:
function RegisterCallback(CallbackProc: TCallbackProc): Integer; stdcall;
大多数回调函数通过引用传递普通结构,如下所示:
TCallbackProc = procedure(Struct: PStructType); stdcall;
其中 PStructType 被声明为
TStructType = packed record
Parameter1: array[0..9] of AnsiChar;
Parameter2: array[0..19] of AnsiChar;
Parameter3: array[0..29] of AnsiChar;
end;
PStructType = ^TStructType;
此 DLL 由用 C# 编写的 .NET 应用程序使用。C# 代码的编写非常粗心,整个应用程序的行为不可靠,显示出难以识别的异常,在不同的地方引发了从运行到运行。
我没有理由怀疑 DLL,因为它已经证明自己是一个非常强大的软件,用于许多其他应用程序。我目前关心的是这些结构在 C# 中的使用方式。
假设,上面的记录在 C# 中重新声明如下:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct TStructType
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string Parameter1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string Parameter2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 30)]
public string Parameter3;
}
并且回调被声明为
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void CallbackProc(ref TStructType Struct);
现在有趣的事情开始了。假设,在 DLL 中,注册的回调以这种方式调用:
var
Struct: TStructType;
begin
// Struct is initialized and filled with values
CallbackProc(@Struct);
end;
但是我在 C# 应用程序中看到的,以及我根本不喜欢的是,编组结构被保存为指针以供将来使用:
private void CallbackProc(ref TStructType Struct)
{
SomeObjectList.Add(Struct); // !!! WTF?
}
据我了解,Struct 变量是在 DLL 深处的 Delphi 堆栈上创建的,并将指向它的指针存储在客户端应用程序的堆上 - 纯粹是一次冒险。
我不是 C# 的忠实粉丝/专家,所以请原谅我的幼稚问题,编组器是否在幕后做了一些事情,比如将结构复制到堆上或类似的事情,或者应用程序有时工作的事实是纯粹的问题机会?
先感谢您。