У меня есть эта подпись метода 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 # по возвращении. Но я не думаю, что это убедительное доказательство того, что массив закрепляется, а не копируется туда-сюда.
Давайте сделаем небольшой эксперимент. Во-первых, давайте изменим ваш 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.
Это не всегда легко узнать, особенно если вы, конечно, используете неверное объявление. 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 аргумент, отладчик сообщает вам свой адрес. Если он совпадает, маршаллер просто передал указатель. Если нет, то вы получили копию массива.