转换IUnknowns的/施放SAFEARRAY到接口指针的一个可迭代阵列(Convert/cast

2019-07-31 14:12发布

我在C#中的以下接口与一个同名的类(无我)实现它。

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

公共类的执行顺序: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 ++非托管代码的基础工作。

但是级阵列被证明是一个不同的challange ...

我承认,对我来说,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疯狂的东西,使方法获取集合计数等方法获得的财产上的某些指标值。

但是,这只是似乎并不好看。 我怀疑什么,我想很容易的机制,但所有我对谷歌总是缺少的东西找到了答案。

Answer 1:

不知道您是否使用MFC,ATL或其他一些图书馆在C ++客户端,这是很难简化它,所以我会用的Win32 API(这些图书馆提供SAFEARRAYS的简单使用辅助类)

不过,我会假设你使用过C#的lib #import互操作类型库的,所以你可以使用生成的智能指针类。 我也假设你回到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第一次,该项目并没有做到这一点,但如果你愿意,你可以添加一个生成后步骤



Answer 2:

与SAFEARRAYS工作是在颈部疼痛。 还有它周围还有没有办法。

由于SAFEARRAY是你不能只是投以一个方便IOrder *阵列和工作与项目的结构,你在C#中会。

这里有几件事情谷歌拿给我。 他们看起来非常有益的。

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



文章来源: Convert/cast SAFEARRAY of IUnknowns to an iterable array of interface pointers