将IUnknowns的SAFEARRAY转换/转换为可迭代的接口指针数组

我在C#中有以下接口,其中一个具有相同名称的类(没有我)实现它。

[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 ++非托管代码之间进行基础工作。

但是类arrays被certificate是一个不同的挑战……

我承认,对我来说,COM是新的,残酷的混淆和C ++很久以前被遗忘……但是我正在开发这两个库,所以我不放弃; 我希望C ++ DLL作为一些程序和我的C#代码之间的代理。

澄清:我既不使用MFC也不使用ATL。 我在C ++代码中使用#import来获取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 *或其他东西,所以我可以迭代返回的整个数组并调用“Order”类型的方法

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

因此,对于第一个周期,我将获得值1,2,3,对于第二个周期,我将获得值4,5,6。

由于我是C ++和C#库的作者,我可以跳过所有这些COM疯狂的东西并制作方法来获取集合计数和其他方法来获取某些索引上的属性值。

但那似乎并不好看。 我怀疑我想要的机制很简单但我在谷歌上找到的所有答案总是缺少一些东西。

在不知道您是否在C ++客户端中使用MFC,ATL或其他库的情况下,很难对其进行简化,因此我将使用Win32 API(这些库提供帮助程序类以简化安全arrays的使用)

但是,我假设您通过Interop类型库的#import使用C#lib,因此您可以使用生成的智能指针类。 我还假设你返回一个IUnknowns的SAFEARRAY,而不是一个包含IUnknowns的SAFEARRAY变量 – 这可以通过在你的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,项目不会这样做,但如果您需要,可以添加构建后步骤

与SAFEARRAYS合作是一个痛苦的问题。 没有办法绕过它。

由于SAFEARRAY是一种结构,因此您不能像使用C#一样使用方便的IOrder *数组进行处理。

以下是谷歌向我展示的一些内容。 他们看起来很有帮助。

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

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

如果您在C ++项目中使用ATL,那么您将获得一个CComSafeArray包装器:

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