Копирует ли .NET данные массива взад и вперед или закрепляет массив?

У меня есть эта подпись метода COM, объявленная в C #:

void Next(ref int pcch,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
char[] pchText);

Я называю это так:

int cch = 100;
var buff = new char[cch];
com.Next(ref cch, buff);

Уровень взаимодействия .NET сначала копирует весь массив во временный буфер неуправляемой памяти, а затем копирует его обратно? Или массив автоматически закрепляется и передается по ссылке?

Ради попытки я сделал это в объекте COM (C ++):

*pcch = 1;
pchText[0] = L'A';
pchText[1] = L'\x38F'; // 'Ώ'

Я получаю 'Ώ' назад, когда я проверю buff[1] в C # по возвращении. Но я не думаю, что это убедительное доказательство того, что массив закрепляется, а не копируется туда-сюда.

10

Решение

Давайте сделаем небольшой эксперимент. Во-первых, давайте изменим ваш COM-метод, чтобы он выглядел следующим образом (в C ++):

STDMETHODIMP CComObject::Next(ULONG* pcch, int* addr, OLECHAR* pbuff)
{
pbuff[0] = L'A';
pbuff[1] = L'\x38F';
*addr = (int)pbuff;
*pcch = 1;
return S_OK;
}

Затем измените сигнатуру метода C #:

void Next(ref uint pcch, out IntPtr addr,
[In, Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
char[] pbuff);

Наконец, проверьте это так:

uint cch = 10;
var buff = new char[cch];
IntPtr addr1;

unsafe
{
fixed (char* p = &buff[0])
{
addr1 = (IntPtr)p;
}
}

IntPtr addr2;
com.Next(ref cch, out addr2, buff);
Console.WriteLine(addr1 == addr2);

Как и ожидалось, addr1 == addr2 является true, Таким образом, очевидно, что массив передается в COM вместо того, чтобы копироваться, а не копироваться.

Тем не менее, я не смог найти никакой документации, которая бы представляла это как жесткое требование для реализации CLR. Например, это может или не может быть правдой для Mono.

6

Другие решения

Это не всегда легко узнать, особенно если вы, конечно, используете неверное объявление. Char [] нельзя маршалировать как LPWStr, это должен быть LPArray. Теперь атрибут CharSet играет роль, так как вы его не указали, char [] будет маршалироваться как 8-битный char [], а не 16-битный wchar_t []. Элемент массива маршалирования не имеет одинаковый размер (он не является «blittable»), поэтому маршаллер должен скопировать массив.

Довольно нежелательно, особенно если учесть, что ваш код на C ++ ожидает wchar_t. В этом конкретном случае очень просто сказать, что ничего не возвращается в массив. Если массив маршалируется путем копирования, вы должны явно сказать маршаллеру, что массив должен быть скопирован обратно после вызова. Вы должны применить [In, Out] атрибут на аргумент. Вы получите китайский.

Обычный способ определить, будет ли массив маршалироваться при копировании, — с помощью отладчика. Включите неуправляемую отладку в вашей программе на C #. Установите точку останова в вызове, а также точку останова в первом операторе в нативной функции. Когда наступит 1-ая точка останова, используйте Debug + Windows + Memory + Memory 1. Поставьте полировать в поле адреса и переключите отображение на «4-байтовое целое число». Вы увидите адрес объекта массива, 4-байтовый дескриптор типа, длину 4-байтового массива и само содержимое массива. Итак, вы знаете, что если массив не скопирован, то переданный адрес является отображаемым адресом плюс 8.

Нажмите F5, чтобы продолжить, остановится точка останова в собственной функции. Посмотрите на pchText аргумент, отладчик сообщает вам свой адрес. Если он совпадает, маршаллер просто передал указатель. Если нет, то вы получили копию массива.

9

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector