Преобразование / приведение SAFEARRAY IUnknowns к итерируемому массиву указателей интерфейса

У меня есть следующий интерфейс в C # с классом с тем же именем (без I), реализующим его.

[ComVisible(true)]
[Guid("B2B134CC-70A6-43CD-9E1E-B3A3D9992C3E")]
public interface IOrder
{
long GetQuantity();
long GetOrderType();
long GetPositionType();
}

Реализация открытого класса Order: IOrder — это всего три приватных поля и конструктор с необходимыми 3 параметрами.

Где-то еще у меня есть следующий метод, в результате которого я хочу работать внутри неуправляемого кода C ++, передаваемого туда через файлы COM и .tlb / .tlh.

public ScOrder[] GetOrders()
{
//constant return value for simplicity
return new Order[] {
new Order(1, 2, 3),
new Order(4, 5, 6)
};
}

Мне уже удалось получить основы работы между неуправляемым кодом C ++ с использованием управляемого кода C #.

Но классовые массивы оказались другой проблемой …

Я признаю, что для меня COM является новым и жестоко запутанным, а C ++ давно забыт … но я разрабатываю обе библиотеки, поэтому я не сдамся; Я хочу, чтобы C ++ DLL работала как прокси между какой-то программой и моим C # кодом.

Разъяснение: Я не использую ни MFC, ни ATL. Я использую #import в коде C ++ для получения сгенерированного интерфейса C # и указателей классов и других вещей COM, которые я пока не совсем понимаю.

После часа исследований я просто иду сюда и прошу помощи>.<

Ниже приведен код C ++, который я пытаюсь достичь.

//this is how the instance of C# gets created, read it from the internets
//this type has the method GetOrders
IProxyPtr iPtr;
CoInitialize(NULL);
iPtr.CreateInstance(CLSID_Proxy);

IOrderPtr* ordArr;
//IOrderPtr is just a pointer to the interface type transferred
//right? So IOrderPtr* should represent the array of those pointers, right?

SAFEARRAY* orders;
iPtr->GetOrders(&orders);

Теперь на этом этапе мне нужна некоторая магия COM, которую я пока не понимаю, чтобы преобразовать этот SAFEARRAY * в IOrderPtr * или что-то подобное, чтобы я мог перебирать весь возвращаемый массив и вызывать методы типа «Порядок».

  • GetQuantity ()
  • GetOrderType ()
  • GetPositionType ()

Итак, для первого цикла я получу значения 1,2,3, а для второго цикла получу значения 4,5,6.

Поскольку я являюсь автором библиотек C ++ и C #, я могу просто пропустить все эти безумные COM-вещи и создать методы, чтобы получить счетчик коллекций и другие методы, чтобы получить значение свойства для определенного индекса.

Но это не кажется хорошим. Я подозреваю, что механика того, что я хочу, проста, но все ответы, которые я нашел в Google, всегда что-то упускают.

2

Решение

Не зная, используете ли вы MFC, ATL или какую-либо другую библиотеку в своем клиенте C ++, трудно упростить ее, поэтому я буду использовать Win32 API (эти библиотеки предоставляют вспомогательные классы для более простого использования safearrays)

Тем не менее, я буду считать, что вы используете C # lib через #import библиотеки типов взаимодействия, так что вы можете использовать сгенерированные классы интеллектуальных указателей. Я также предполагаю, что вы возвращаете SAFEARRAY IUnknowns, а не SAFEARRAY Variants, которые содержат IUnknowns — это можно изменить, указав соответствующие атрибуты маршалинга на вашем интерфейсе C #, например:

[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)]
// [return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_VARIANT)]
IOrder[] GetOrders();

Вот реализации типов C # (ссылка на образец решения находится внизу ответа):

[ComVisible(true)]
[Guid("F3071EE2-84C9-4347-A5FC-E72736FC441F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IProxy
{
[return: MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UNKNOWN)]
IOrder[] GetOrders();
}

[ComVisible(true)]
[Guid("8B6EDB6B-2CF0-4eba-A756-B6E92A71A48B")]
[ClassInterface(ClassInterfaceType.None)]
public class Proxy : IProxy
{
public IOrder[] GetOrders() { return new[] {new Order(3), new Order(4)};        }
}

[ComVisible(true)]
[Guid("CCFF9FE7-79E7-463c-B5CA-B1A497843333")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOrder
{
long GetQuantity();
}

[ComVisible(true)]
[Guid("B0E866EB-AF6D-432c-9560-AFE7D171B0CE")]
[ClassInterface(ClassInterfaceType.None)]
public class Order : IOrder
{
private int m_quantity;
public Order(int quantity) { m_quantity = quantity; }
public long GetQuantity() { return m_quantity; }
}

Сервер должен быть собран и зарегистрирован Regasm, Для простоты я сделаю regasm /codebase /tlb $path чтобы избежать подписи и регистрации в GAC.

Код клиента должен выглядеть примерно так:

#import "Server.tlb" no_namespace // you should use namespaces! this is a demo

int _tmain(int argc, _TCHAR* argv[])
{
CoInitialize(NULL);      // init COM

IProxyPtr proxy(__uuidof(Proxy));         // instantiate the proxy
SAFEARRAY* orders = proxy->GetOrders();   // to return orders

LPUNKNOWN* punks;
HRESULT hr = SafeArrayAccessData(orders, (void**)&punks); // direct access to SA memory
if (SUCCEEDED(hr))
{
long lLBound, lUBound;  // get array bounds
SafeArrayGetLBound(orders, 1 , &lLBound);
SafeArrayGetUBound(orders, 1, &lUBound);

long cElements = lUBound - lLBound + 1;
for (int i = 0; i < cElements; ++i)  // iterate through returned objects
{
LPUNKNOWN punk = punks[i];     // for VARIANTs: punk = punks[i].punkVal
IOrderPtr order(punk);         // access the object via IOrder interface
long q = order->GetQuantity(); // and voila!
std::cout << "order " << i + 1 << ": Quantity " << q << std::endl;
}
SafeArrayUnaccessData(orders);
}
SafeArrayDestroy(orders);
return 0;
}

Пример проекта можно найти здесь. Обратите внимание, что вы должны вручную зарегистрировать .tlb при первой сборке, проект этого не делает, но вы можете добавить шаг после сборки, если хотите

2

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

Работа с SAFEARRAYS — это боль в шее. Там просто нет возможности обойти это.

Поскольку SAFEARRAY — это структура, вы не можете просто привести ее к удобному массиву IOrder * и работать с элементами так же, как в C #.

Вот несколько вещей, которые Google показал мне. Они выглядят довольно полезными.

http://edn.embarcadero.com/article/22016

http://digital.ni.com/public.nsf/allkb/7382E67B95238D2B862569AD005977F0

Если вы используете ATL в своем проекте C ++, у вас есть обертка CComSafeArray:

http://msmvps.com/blogs/gdicanio/archive/2011/02/04/simplifying-safearray-programming-with-ccomsafearray.aspx

0

По вопросам рекламы [email protected]