简体   繁体   English

数组的C#Interop封送处理行为似乎与文档不一致

[英]C# Interop Marshaling behaviour for arrays seems inconsistent with documentation

I am currently writing a thin C# binding for OpenGL. 我目前正在为OpenGL编写薄的C#绑定。 I've just recently implemented the OpenGL GenVertexArrays function, which has the following signature: OpenGL Documentation on glGenVertexArrays . 我最近刚刚实现了OpenGL GenVertexArrays函数,该函数具有以下签名: glGenVertexArrays上的OpenGL文档

Essentially, you pass it an array in which to store generated object values for the vertex arrays created by OpenGL. 本质上,您为它传递了一个数组,该数组中存储了由OpenGL创建的顶点数组的生成的对象值。

In order to create the binding, I use delegates as glGenVertexArrays is an OpenGL extension function, so I have to load it dynamically using wglGetProcAddress. 为了创建绑定,我使用委托,因为glGenVertexArrays是OpenGL扩展函数,因此必须使用wglGetProcAddress动态加载它。 The delegate signature I have defined in C# looks like this: 我在C#中定义的委托签名如下所示:

[SuppressUnmanagedCodeSecurity]
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
private delegate void glGenVertexArrays(uint amount, uint[] array);

The function pointer is retrieved and converted to this delegate using Marshal.GetDelegateForFunctionPointer, like this: 使用Marshal.GetDelegateForFunctionPointer检索函数指针并将其转换为该委托,如下所示:

IntPtr proc = wglGetProcAddress(name);

del = Marshal.GetDelegateForFunctionPointer(proc, delegateType);

Anyways, here's what bothers me: 无论如何,这就是困扰我的地方:

In any official documentation I can find on default marshalling behaviour for reference types (which includes arrays), is this: 在任何官方文档中,我都可以找到引用类型(包括数组)的默认编组行为,它是:

By default, reference types (classes, arrays, strings, and interfaces) passed by value are marshaled as In parameters for performance reasons. 默认情况下,出于性能原因,按值传递的引用类型(类,数组,字符串和接口)将被编组为In参数。 You do not see changes to these types unless you apply InAttribute and OutAttribute (or just OutAttribute) to the method parameter. 除非您将InAttribute和OutAttribute(或仅将OutAttribute)应用于方法参数,否则您不会看到对这些类型的更改。

This is taken from this MSDN page: MSDN page on directional attributes 这是从以下MSDN页面上获得的: 有关方向属性的MSDN页面

However, as can be seen from my delegate signatures, the [In] and [Out] directional attributes have not been used on the array of unsigned integers, meaning when I call this function I should actually not be able to see the generated object values which OpenGL should have stored in them. 但是,从我的委托签名可以看出,[In]和[Out]方向属性尚未在无符号整数数组上使用,这意味着当我调用此函数时,我实际上应该看不到生成的对象值哪些OpenGL应该已经存储在其中。 Except, I am. 除了,我是。 Using this signature, I can the following result when running the debugger: 使用此签名,运行调试器时可以得到以下结果:

调试结果是肯定的

As can be seen, the call absolutely did affect the array, even though I did not explicitly use the [Out] attribute. 可以看出,即使我没有显式使用[Out]属性,该调用也确实影响了数组。 This is not, from what I understand, a result I should expect. 从我的理解来看,这不是我应该期待的结果。

Does anyone know the reason behind this? 有谁知道背后的原因吗? I know it might seem as a minor deal, but I am very curious to know why this seems to break the default marshalling behaviour described by Microsoft. 我知道这似乎没什么大不了的,但是我很好奇为什么这似乎破坏了Microsoft描述的默认编组行为。 Is there some behind-the-scenes stuff going on when invoking delegates compared to pure platform invoke prototypes? 与纯平台调用原型相比,调用委托时是否有一些幕后操作? Or am I misinterpreting the documentation? 还是我误解了文档?

[EDIT] [编辑]

For anyone curious, the public method that invokes the delegate is defined on a static "GL" class, and is as followed: 对于任何好奇的人,调用委托的公共方法是在静态“ GL”类上定义的,如下所示:

public static void GenVertexArrays(uint amount, uint[] array)
{
    InvokeExtensionFunction<glGenVertexArrays>()(amount, array);
}

It is not mentioned on the documentation page you linked, but there is another topic dedicated to the marshaling of arrays, where it says: 您链接的文档页面上没有提到它,但是还有另一个专门讨论数组封送处理的主题 ,它说:

With pinning optimization, a blittable array can appear to operate as an In/Out parameter when interacting with objects in the same apartment. 通过固定优化,可蓝变数组在与同一单元中的对象进行交互时似乎可以用作In / Out参数。

Both conditions are met in your case: array of uint is blittable, and there is no machine-to-machine marshaling. 在您的情况下,这两个条件都得到满足:uint数组是可blittable的,并且没有机器到机器的封送处理。 It is still a good idea to declare it [Out], so your intention is documented within the code. 声明为[Out]仍然是一个好主意,因此您的意图已记录在代码中。

The documentation is correct in the general case. 该文档在一般情况下是正确的。 But uint is a bit special, it is a blittable type. 但是uint有点特殊,它是可漂白的类型。 An expensive word that means that the pinvoke marshaller does not have to do anything special to convert the array element values. 一个昂贵的词,意味着pinvoke marshaller不必执行任何特殊操作即可转换数组元素值。 An uint in C# is exactly the same type as an unsigned int in C. Not a coincidence at all, it is the kind of type that a processor can handle natively. C#中的uint与C中的unsigned int完全相同。完全不是巧合,它是处理器可以本地处理的类型。

So the marshaller can simply pin the array and pass a pointer to the first array element as the second argument. 因此,编组人员可以简单地固定数组并将指针传递给第一个数组元素作为第二个参数。 Very fast, always what you want. 非常快,始终是您想要的。 And the function scribbles directly into the managed array, so copying the values back is not necessary. 而且该函数直接将内容写到托管数组中,因此不需要将值复制回去。 A bit dangerous too, you never ever want to lie about the amount argument, GC heap corruption is an excessively ugly bug to diagnose. 同样有点危险,您永远也不想撒谎关于amount参数,GC堆损坏是要诊断的过于丑陋的错误。

Most simple value types and structs of simple values types are blittable. 大多数简单值类型和简单值类型的结构都是可Blittable的。 bool is a notable exception. bool是一个明显的例外。 You'll otherwise never have to be sorry for using [Out] even if it is not necessary. 否则,即使没有必要,您也不必为使用[Out]而后悔。 The marshaller simply ignores it here. 编组人员只是在这里忽略它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM