我在C#中使用以下具有相同名称的类(没有我)实现它的接口(interface)。

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

公共(public)类Order的实现:IOrder只是三个私有(private)字段,并且是一个带有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。我在C++代码中使用#import来获取C#生成的接口(interface)和类指针以及其他我不太了解的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疯狂的知识,并使用方法来获取集合计数,而使用其他方法来获取特定索引上的属性值。

    但这似乎并不好。我怀疑自己想要的机制很简单,但我在Google上找到的所有答案始终缺少某些内容。

    最佳答案

    不知道您是在C++客户端中使用MFC,ATL还是其他库,很难对其进行简化,因此我将使用Win32 API(这些库提供了用于简化safearrays的帮助器类)

    但是,我将假定您通过Interop类型库的#import使用C#库,因此可以使用生成的智能指针类。我还将假设您返回的是IUnknowns的SAFEARRAY而不是包含IUnknowns的Variants的SAFEARRAY-可以通过在C#接口(interface)上指定适当的封送处理属性来对其进行修改,例如:

    [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;
    }
    

    示例项目can be found here。请注意,您必须在首次构建.tlb时手动注册它,但项目不会这样做,但是如果您愿意,可以添加一个构建后步骤。

    关于c# - 将IUnknowns的SAFEARRAY转换/广播为接口(interface)指针的可迭代数组,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12412533/

    10-09 02:40