编组普通结构:C#将它们复制到堆上吗?

前端之家收集整理的这篇文章主要介绍了编组普通结构:C#将它们复制到堆上吗?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个用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由.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;
}

并将回调声明为

[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 / invoke marshaller必须获取它收到的原始指针并将其转换为TStructType对象. TStructType包含C#字符串,这些字符串绝对不能与那些Delphi字符数组一起使用.所以marshaller已经在你的C#代码和Delphi代码之间添加了一个层.

由于函数通过ref接收结构,所发生的事情如下:

>在调用CallbackProc之前,marshaller将原始非托管指针反序列化为TStructType对象.
>然后通过引用传递那个TStructType对象的CallbackProc.
>当CallbackProc返回时,p / invoke marshaller将TStructType对象序列化回原始的非原始非托管指针.

其中一个后果是,在回调过程返回之前,您对TStructType对象所做的更改对Delphi代码不可见.对比当你调用Delphi过程将变量作为var参数传递时会发生什么.在这种情况下,程序中的任何更改都会立即显示在该过程之外.

猜你在找的C#相关文章