Я вызываю предоставляемую извне COM DLL, для которой сгенерировал оболочку взаимодействия COM. Ради аргумента, давайте назовем интерфейс, который я хочу вызвать IEnumFoo
,
IEnumFoo
имеет типичный шаблон перечислителя COM:
HRESULT Next (
ULONG celt,
IFoo** rgelt,
ULONG* pceltFetched
);
где первый параметр — это число желаемых результатов, второй параметр — это буфер, в который записываются результаты, а последний параметр описывает количество фактически записанных результатов.
Когда я выбираю «Добавить ссылку» и указываю Visual Studio на эту DLL, она создает сборку взаимодействия COM со следующей подписью:
void Next(uint, out IFoo, out uint)
Это позволяет только .NET-коду запрашивать один объект за один раз, что может значительно увеличить накладные расходы при использовании этих API.
Есть ли какой-то механизм, который я могу использовать для создания версии Next
что позволило бы мне предоставить больше IFoo
«слоты» над которыми бы маршалер был счастлив? (Я не против редактирования IL в сборке взаимодействия вручную :))
Подходящая подпись для этого будет выглядеть так:
void Next(
uint celt,
[Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)] IFoo[] rgelt,
out uint pceltFetched);
Согласно MSDN, по крайней мере, нет механизма, чтобы генерировать это автоматически. Даже если оригинальный IDL для интерфейса имел length_is
применительно к rgelt
, эта информация теряется в библиотеке типов. Поэтому вам нужно отредактировать сборку взаимодействия вручную.
Еще один вариант — полностью определить этот конкретный интерфейс вручную в основной сборке и просто игнорировать созданную версию взаимодействия. Помните, что при приведении типов к RCW будет работать любой интерфейс с совпадающим GUID (т. Е. Тот, для которого QueryInterface успешен), поэтому вы можете иметь несколько разных управляемых интерфейсов, которые представляют разные представления одного и того же COM-интерфейса.
Не ответ на ваш вопрос, а предложение попробовать другой подход. Я бы создал оболочку C ++ / CLI для перечисления через интерфейс COM в неуправляемом коде (таким образом, избегая маршалинга), а затем создавал управляемый List
или другой контейнер, в котором вы возвращаете свои объекты.
Это почти наверняка будет проще, чем ручная настройка IL сборки взаимодействия, и вы также можете легко отладить его. Неуправляемый код C ++ будет довольно простым, как и управляемая оболочка.
Если объект, реализующий этот интерфейс, является нативным, просто переопределите интерфейс в своем коде, как и должно быть, убедившись, что в интерфейсе используются те же атрибуты ComImport и Guid. Затем возьмите объект и приведите к своему интерфейсу. Вы можете звонить через этот интерфейс просто отлично.
Помните: сборки взаимодействия не волшебны, вы всегда можете определить интерфейс вручную.