C#P / Invoke | blittable结构数组的编组行为不一致?

时间:2018-05-12 21:30:03

标签: c# interop pinvoke

我今天正在测试P / Invoke的东西,并遇到了让我很困惑的事情。

我有一个非托管库,其函数采用数组参数,打印出值并修改它们:

#include <cstdio>

#define export extern "C" void __cdecl __declspec(dllexport)

struct S
{
    int x;
};

export PassIntArray(int* x, int size)
{
    for (int i = 0; i < size; i++)
    {
        printf("x[%i] = %i\n", i, x[i]);
        x[i] *= 10;
    }
}

export PassSArray(S* s, int size)
{
    for (int i = 0; i < size; i++)
    {
        printf("s[%i].x = %i\n", i, s[i].x);
        s[i].x *= 10;
    }
}

以及通过P / Invoke访问这些功能的C#程序:

using System.Runtime.InteropServices;
using static System.Console;

namespace Test
{
    [StructLayout(LayoutKind.Sequential)]
    struct S
    {
        public int X;
    }

    class Program
    {
        [DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void PassIntArray(int[] x, int size);

        [DllImport("mylib.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void PassSArray(S[] s, int size);

        static void Main(string[] args)
        {
            var z = new[] {1,2,3};
            PassIntArray(z, z.Length);
            foreach (var i in z)
                WriteLine(i);

            var u = new[] { new S { X = 1 }, new S { X = 2 }, new S { X = 3 } };
            PassSArray(u, u.Length);
            foreach (var i in u)
                WriteLine(i.X);
        }
    }
}

运行该程序,数组函数的输出如下:

// Unmanaged side:
x[0] = 1
x[1] = 2
x[2] = 3
// Managed side, after modification:
10
20
30

但是,对于PassSArray,就是这样:

// Unmanaged side:
s[0].x = 1
s[1].x = 2
s[2].x = 3
// Managed side, after modification:
1
2
3

来自this question“当值为blit时发生,一个昂贵的单词意味着托管值或对象布局与本机布局相同。然后,​​pinvoke marshaller可以获取快捷方式,固定对象并将指针传递给托管对象存储你将不可避免地看到变化,因为本机代码直接修改了托管对象。“

根据我的理解,S应该是blittable(因为它使用顺序布局并且只包含blittable类型的字段),因此应该由marshaller固定,导致修改'​​结转',就像使用PassIntArray一样。 区别在哪里,为什么?

0 个答案:

没有答案