我有一个用Delphi编写的本地DLL,它主动使用回调机制:“注册”了一个回调函数,然后从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#的狂热者/专家,所以请原谅我的天真的问题,编码员是否在幕后做一些事情,例如将结构复制到堆上或类似的事情,或者应用程序有时可以正常工作的事实仅仅是一个纯粹的问题。机会?

先感谢您。

最佳答案

C#结构是一种值类型。意思就是

SomeObjectList.Add(Struct)

将复制该结构。因此,无需担心。

实际上,在CallbackProc中,您没有对在Delphi代码中分配的对象进行操作。这是因为p/调用编码器必须采用接收到的原始指针并将其转换为TStructType对象。 TStructType包含C#字符串,这些字符串绝对不能用这些Delphi字符数组进行变色。因此,编码人员已经在您的C#代码和Delphi代码之间添加了一层。

由于该函数通过ref接收结构,因此发生的情况如下:
  • 在调用CallbackProc之前,编码(marshal)处理程序将未托管的原始指针反序列化为TStructType对象。
  • 然后通过引用将CallbackProc对象传递给
  • TStructType
  • CallbackProc返回时,p/调用编码器将TStructType对象序列化回原始的原始非托管指针。

  • 其后果之一是,您对TStructType对象所做的更改对于Delphi代码而言是不可见的,直到回调过程返回。与调用Delphi过程将变量作为var参数传递时所发生的情况形成对比。在这种情况下,该过程中的任何更改都可以在该过程外部立即看到。

    10-07 13:54
    查看更多