我有一个用Delphi编写的本机DLL,主动使用回调机制:回调函数被"注册",后来从DLL内部调用:
function RegisterCallback(CallbackProc: TCallbackProc): Integer; stdcall;
Run Code Online (Sandbox Code Playgroud)
大多数回调函数都是通过引用传递普通结构,如下所示:
TCallbackProc = procedure(Struct: PStructType); stdcall;
Run Code Online (Sandbox Code Playgroud)
其中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;
Run Code Online (Sandbox Code Playgroud)
此DLL由.NET应用程序使用,用C#编写.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;
}
Run Code Online (Sandbox Code Playgroud)
并将回调声明为
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void CallbackProc(ref TStructType Struct);
Run Code Online (Sandbox Code Playgroud)
现在有趣的事情开始了 让我们假设,在DLL中,以这种方式调用已注册的回调:
var
Struct: TStructType;
begin
// Struct is initialized and filled with values
CallbackProc(@Struct);
end;
Run Code Online (Sandbox Code Playgroud)
但是我在C#应用程序中看到的,以及我根本不喜欢的是,编组结构被保存为未来使用的指针:
private void CallbackProc(ref TStructType Struct)
{
SomeObjectList.Add(Struct); // !!! WTF?
}
Run Code Online (Sandbox Code Playgroud)
据我所知,Struct变量是在DLL深处内部的Delphi堆栈上创建的,并且在客户端应用程序中将指针存储在堆上 - 这是一次纯粹的冒险.
我不是C#的忠实粉丝/专家,所以请原谅我天真的问题,编组人员在幕后做了什么,比如将结构复制到堆上或类似的东西,或者应用程序有时工作的事实是纯粹的问题机会?
先感谢您.
AC#struct是一种值类型.意思就是
SomeObjectList.Add(Struct)
Run Code Online (Sandbox Code Playgroud)
将制作结构的副本.所以,没什么值得关注的.
实际上,CallbackProc您没有对Delphi代码中分配的对象进行操作.那是因为p/invoke marshaller必须获取它收到的原始指针并将其转换为TStructType对象.并且TStructType包含C#字符串,这些字符串绝对不会与那些Delphi字符数组相关联.所以marshaller已经在你的C#代码和Delphi代码之间添加了一个层.
由于函数通过ref接收结构所发生的事情如下:
CallbackProcmarshaller 之前,将原始非托管指针反序列化为TStructType对象.CallbackProc然后通过TStructType引用传递该对象.CallbackProc返回时,在p /调用编组序列化TStructType对象回到原来的原始非托管指针.其中一个后果是,TStructType在回调过程返回之前,对对象所做的更改在Delphi代码中是不可见的.与您调用Delphi过程将变量作为var参数传递时发生的情况进行对比.在这种情况下,程序中的任何更改都会立即在该过程之外可见.
| 归档时间: |
|
| 查看次数: |
593 次 |
| 最近记录: |