转换IUnknowns的/施放SAFEARRAY到接口指针的迭代数组 [英] Convert/cast SAFEARRAY of IUnknowns to an iterable array of interface pointers

查看:166
本文介绍了转换IUnknowns的/施放SAFEARRAY到接口指针的迭代数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C#下面的接口与一个相同的名称(不带I)实现它的类。

I have the following interface in C# with a class with a same name (without I) implementing it.

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



公共类的执行顺序:IOrder只是三个私有字段和构造函数需要3个参数。

The implementation of the public class Order : IOrder is just three private fields and a constructor with required 3 parameters.

别的地方,我有与我想要一个C ++非托管代码,通过COM和转移有内工作结果下面的方法。 TLB / .tlh文件。

Somewhere else, I have the following method with a result with which I want to work inside a C++ unmanaged code, transferred there via COM and .tlb/.tlh files.

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



我已经设法使用C ++非托管代码的基础工作C#的托管代码。

I've already managed to get the basics work between the C++ unmanaged code using the C# managed code.

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

But class arrays proved to be a different challange...

我承认对我来说,COM是新的,残酷的混乱和C ++早已被人遗忘......但我正在开发两个库,所以我不会放弃;我想C ++的DLL来一些方案和我的C#代码之间的代理工作。

I admit that for me, the COM is new and brutally confusing and C++ long forgotten ... , but I'm developing both libraries so I'm not giving up; I want the C++ DLL to work as a proxy between some program and my C# code.

澄清:我既没有使用MFC也不ATL 。我使用#import在C ++代码用于获取C#生成的接口和类指针和其他COM东西我不太明白呢。

Clarification: I'm using neither MFC nor ATL. I use #import in the C++ code for getting the C# generated interface and class pointers and other COM stuff I don't quite understand yet.

研发的小时后,我M只是去这里乞求帮助><

After hour of researching, I'm just going here and beg for help >.<

以下是我想要实现的C ++代码

The following is the C++ code of what I'm trying to achieve.

//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 *或东西,所以我可以整个数组遍历返回并调用类型的订单

Now at this point, I need some COM magic I don't yet understand to convert that SAFEARRAY* to IOrderPtr* or something so I can iterate over the whole array returned and call the methods of the type "Order"


  • GetQuantity()的方法

  • GetOrderType()

  • GetPositionType()

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

So for the first cycle, I'll get values 1,2,3 and for the second cycle, I'll get values 4,5,6.

由于我M C ++和C#库的作者,我可以跳过这一切COM疯狂的东西,使方法获取集合计数等方法来获得对某些指标属性的值。

Since I'm the author of both C++ and C# library, I can just skip all of this COM crazy stuff and make methods to get the collection count and other methods to get the value of property on certain index.

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

But that just doesn't seem nice. I suspect the mechanics of what I want are easy but all the answers I found on google are always missing something.

推荐答案

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

Without knowing whether you use MFC, ATL or some other library in your C++ client, it is hard to simplify it so I will use Win32 API (these libraries provide helper classes for simpler usage of safearrays)

不过,我会假设你使用过C#的lib #进口互操作类型库的,所以你可以使用生成的智能指针类。我也假设你返回IUnknowns的SAFEARRAY,而不是变体,其包含IUnknowns的SAFEARRAY - 这可以通过指定适当的编组进行修改,你的属性C#的接口,例如:

However, I will assume that you use you C# lib through #import of Interop type library, so you can use generated smart pointer classes. I will also assume that you return a SAFEARRAY of IUnknowns and not a SAFEARRAY of Variants which contain IUnknowns - this can be modified by specifying appropriate marshaling attributes on you C# interface, e.g:

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

下面是C#类型的实现(联系到样品溶液是在回答的底部)

Here are the implementations of C# types (link to a sample solution is at the bottom of the answer):

[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 /代码库/ TLB $ PATH 来避免签约并在GAC注册。

The server must be built and registered with Regasm. For simplicity, I will do regasm /codebase /tlb $path to avoid signing and registering in GAC.

客户端代码想是这样的:

The client code should like something like this:

#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第一次,该项目并没有做到这一点,但如果你愿意,你可以添加一个生成后步骤

The sample project can be found here. Please note that you must manually register the .tlb first time you build it, the project doesn't do it, but you can add a post-build step if you want

这篇关于转换IUnknowns的/施放SAFEARRAY到接口指针的迭代数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆