C # P / Invoke |Несогласованное поведение сортировки массива blittable структуры? - PullRequest
0 голосов
/ 13 мая 2018

Сегодня я тестировал материал 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;
    }
}

А также программа на C #, которая обращается к этой функции через P / Invoke:

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

С этот вопрос : "Происходит, когда значение является blittable, дорогим словом, означающим, что управляемое значение или компоновка объекта идентичны собственному компоновщику. Затем маршаллер pinvoke может использовать ярлык, закреплять объект и передавать указатель на хранилище управляемого объекта . Тогда вы неизбежно увидите изменения, поскольку собственный код напрямую изменяет управляемый объект. "

Насколько я понимаю, S должен быть blittable (так как он использует последовательную разметку и содержит только поля типов, которые являются blittable), и поэтому должен быть закреплен маршаллером, вызывая модификации, "переносимые", как они делают с PassIntArray. В чем разница и почему?

...