3

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

但是类数组被证明是一个不同的挑战......

我承认,对我来说,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”类型的方法

  • 获取数量()
  • 获取订单类型()
  • 获取位置类型()

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

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

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

4

2 回答 2

3

在不知道您是否在 C++ 客户端中使用 MFC、ATL 或其他库的情况下,很难简化它,因此我将使用 Win32 API(这些库提供帮助类以更简单地使用安全数组)

但是,我将假设您通过#importInterop 类型库使用 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,项目不会这样做,但您可以根据需要添加构建后步骤

于 2012-09-14T08:17:25.107 回答
0

与 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

于 2012-09-14T01:55:04.550 回答