Сегодня я тестировал материал 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. В чем разница и почему?