Являются ли атрибуты P/Invoke [In, Out] необязательными для маршалинга массивов?
Предположим, что есть собственная функция с интерфейсом pure-C, подобная следующей, экспортированная из собственной DLL:
// NativeDll.cpp
extern "C" void __stdcall FillArray(
int fillValue,
int count,
int* data)
{
// Assume parameters are OK...
// Fill the array
for (int i = 0; i < count; i++)
{
data[i] = fillValue;
}
}
Следующий P/Invoke работает нормально (протестировано с VS2010 SP1):
[DllImport("NativeDll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern void FillArray(
int fillValue,
int count,
[In, Out] int[] data
);
а также этот P/Invoke, такой же, как указано выше, но без [In, Out]
атрибуты:
[DllImport("NativeDll.dll", CallingConvention=CallingConvention.StdCall)]
public static extern void FillArray(
int fillValue,
int count,
int[] data
);
Итак, это те, [In, Out]
атрибуты необязательные для маршалинга массивов? Какова их цель, если таковая имеется? Можно ли их опускать в наших декларациях P/Invoke?
1 ответ
Нет, они не являются обязательными. Это просто происходит случайно. Это, однако, очень распространенная авария. Это работает, потому что массив на самом деле не маршалируется. Маршаллер pinvoke видит, что массив C# уже совместим с собственным массивом, поэтому пропускает шаг для создания его копии. Он просто закрепляет массив и передает указатель на нативный код.
Это, конечно, очень эффективно, и вы неизбежно получите результаты обратно, потому что нативный код напрямую записывает элементы массива. Таким образом, ни атрибуты [In], ни [Out] не имеют значения.
Это становится намного мрачнее, если тип элемента массива не так прост. Нелегко определить тип элемента, который является структурой или типом класса, который не может быть слепым или чья компоновка не совпадает после маршалинга, поэтому маршаллеру pinvoke приходится делать копию массива. Особенно трудно определить несовместимость макета, поскольку управляемый макет невозможно обнаружить. И может меняться в зависимости от используемого джиттера. Он может работать в x86, но не в x64, например, довольно неприятно, когда выбран AnyCPU. Чтобы скопировать измененную копию обратно в массив C# , требуется [Out].
Не уверен, что советовать, кроме того, что никто никогда не был уволен за то, что был явным в своих заявлениях. Возможно, вы всегда должны быть явными, когда тип элемента массива не прост, поэтому вы никогда не попадете в аварию.