Объявление IDL (в C ++) для функции, которая будет принимать массив в стиле C из C # - PullRequest
0 голосов
/ 14 октября 2008

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

Что я хотел бы сделать, так это уметь передавать массив int в метод из C #, что-то вроде:

// desired C# signature
void GetFoo(int bufferSize, int[] buffer);

// desired usage
int[] blah = ...;
GetFoo(blah.Length, blah);

Пара гаечных ключей в работах:

  • C ++ / CLI или Managed C ++ не могут быть использованы (в этом случае с COM можно покончить).
  • Сторона C # не может быть скомпилирована с / unsafe (использование Marshal разрешено).

Интерфейс COM используется (и будет использоваться только когда-либо) частью C #, поэтому меня меньше беспокоит взаимодействие с другими потребителями COM. Переносимость между 32 и 64 битами также не является проблемой (все компилируется и запускается с 32-битной машины, поэтому генераторы кода преобразуют указатели в целые числа). В конце концов он будет заменен просто C ++ / CLI, но это далеко не так.


Моя первоначальная попытка

похож на:

HRESULT GetFoo([in] int bufferSize, [in, size_is(bufferSize)] int buffer[]);

И определение выходного TLB (кажется разумным):

HRESULT _stdcall GetFoo([in] int bufferSize, [in] int* buffer);

Что импортируется C # как (не очень разумно):

void GetFoo(int bufferSize, ref int buffer);

Который я мог бы использовать с

int[] b = ...;
fixed(int *bp = &b[0])
{
    GetFoo(b.Length, ref *bp);
}

... за исключением того, что я не могу скомпилировать с /unsafe.


На данный момент

Я использую:

HRESULT GetFoo([in] int bufferSize, [in] INT_PTR buffer);

Который импортирует как:

void GetFoo(int bufferSize, int buffer);

И мне нужно использовать его как:

int[] b = ...;
GCHandle bPin = GCHandle.Alloc(b, GCHandleType.Pinned);
try
{
    GetFoo(b.Length, (int)Marshal.UnsafeAddrOfPinnedArrayElement(b, 0));
}
finally
{
    bPin.Free();
}

Что работает ... но я бы хотел найти более чистый способ.


Итак, вопрос

Существует ли определение IDL, которое подходит для импорта C # из генератора TLB для этого случая? Если нет, что можно сделать на стороне C #, чтобы сделать его немного безопаснее?

Ответы [ 3 ]

1 голос
/ 14 октября 2008

Итак, вы запрашиваете тип данных IDL, 32-битный на 32-битной машине и 64-битный на 64-битной машине. Но вы не хотите, чтобы код маршалинга воспринимал его как указатель, просто как int. Так что же вы ожидаете от лишних 32-битных при вызове из 64-битного процесса в 32-битный процесс?

Звучит как нарушение физики для меня.

Если это только inproc, см. Нижнюю часть этого обсуждения: http://www.techtalkz.com/vc-net/125190-how-interop-net-client-com-dll.html.

Похоже, что рекомендуется использовать void * вместо intptr и пометить [local], чтобы маршаллер не вмешивался.

0 голосов
/ 14 октября 2008

Хммм ... Я нашел информацию, которая приближает меня ...

Изменения в маршалинге - соответствующие массивы в стиле C

Это объявление IDL (C ++)

HRESULT GetFoo([in] int bufferSize, [in, size_is(bufferSize)] int buffer[]);

импортируется как (MSIL)

method public hidebysig newslot virtual instance void GetFoo([in] int32 bufferSize, [in] int32& buffer) runtime managed internalcall

И если изменить на (MSIL)

method public hidebysig newslot virtual instance void GetFoo([in] int32 bufferSize, [in] int32[] marshal([]) buffer) runtime managed internalcall

Может использоваться как (C #)

int[] b = ...;
GetFoo(b.Length, b);

Именно то, для чего я стрелял!

Но есть ли другие решения, которые не требуют исправления MSIL вызываемой оболочки времени выполнения, сгенерированной tlbimport?

0 голосов
/ 14 октября 2008

Я мало что знаю о работоспособности C # COM, но вы пробовали использовать SAFEARRAY (INT_PTR) или что-то подобное?

...