C# 将IUnknowns的SAFEARRAY转换/强制转换为接口指针的iterable数组
我在C#中有以下接口,其中有一个同名的类(没有I)实现了它C# 将IUnknowns的SAFEARRAY转换/强制转换为接口指针的iterable数组,c#,c++,com,safearray,iunknown,C#,C++,Com,Safearray,Iunknown,我在C#中有以下接口,其中有一个同名的类(没有I)实现了它 [ComVisible(true)] [Guid("B2B134CC-70A6-43CD-9E1E-B3A3D9992C3E")] public interface IOrder { long GetQuantity(); long GetOrderType(); long GetPositionType(); } 公共类Order:IOrder的实现只是三个私有字段和一个具有所需3个参数的构造函数 其他地方,
[ComVisible(true)]
[Guid("B2B134CC-70A6-43CD-9E1E-B3A3D9992C3E")]
public interface IOrder
{
long GetQuantity();
long GetOrderType();
long GetPositionType();
}
公共类Order:IOrder的实现只是三个私有字段和一个具有所需3个参数的构造函数
其他地方,我有以下方法,我想在C++中使用一个C++非托管代码,通过COM和.TBB/.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++代码中使用了“导入”来获取C生成的接口和类指针以及其他我还不太理解的COM。经过一个小时的研究,我正要到这里来寻求帮助>使用SAFEARRAYS是一件痛苦的事。这是没有办法的 因为SAFEARRAY是一种结构,所以您不能像在C#中那样将其转换为一个方便的IOrder*数组并处理这些项 以下是谷歌向我展示的一些东西。它们看起来很有用
如果你在C++项目中使用ATL,你有一个CCOM安全包包装器:
< P>不知道你是否在C++客户端中使用MFC、ATL或其他库,很难简化它,所以我将使用Win32 API(这些库为HaveLead的简单使用提供帮助类) 但是,我假设您通过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#type的实现(答案底部是指向示例解决方案的链接):
必须使用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;
}
#导入“Server.tlb”无命名空间//您应该使用命名空间!这是一个演示
int _tmain(int argc,_TCHAR*argv[]
{
协同初始化(NULL);//初始化COM
IProxyPtr代理(uuidof(proxy));//实例化代理
SAFEARRAY*orders=proxy->GetOrders();//返回订单
LPUNKNOWN*朋克;
HRESULT hr=SafeArrayAccessData(orders,(void**)和punks);//直接访问SA内存
如果(成功(hr))
{
long lLBound,lUBound;//获取数组边界
SafeArrayGetBound(订单、1和lLBound);
SafeArrayGetUBound(订单、1和lUBound);
长芹菜=lUBound-lLBound+1;
for(int i=0;i获取数量();//瞧!
std::你说得对吗,我既不使用MFC也不使用ATL,我通过regasm生成C#lib,然后导入tlb文件。现在,我正在尝试使用你的代码,但调用proc GetQuantity时,我会遇到异常:(您能提供关于异常和HRESULT的详细信息吗?调用是否到达.NET组件端?我现在正在调试它-在IOrderPtr order(朋克);“order”行之后,指向{0x00000000}…是这样吗?根本不会调用.NET组件…procCall=order->GetQuantity((long-long*)q)==>这会引发异常,并且HRESULT procCall仍然未初始化。我在try{}catch(…){}中有它因为我甚至不知道我必须捕获哪种类型的异常…嗯,不是。在实例化order
之前,punk
的值是多少?cElements的值是多少?cElements
?cElements=2;…在实例化order之前,punk的类型是IUnknown*并且指向一个名为u vfptr的东西,它指向我猜这三个指针就是IOrder中的三个函数…这给了我一点希望:)但是在调用IOrderPtr order(朋克)之后,朋克只指向零。我用另一个问题解决了这个问题-
[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; }
}
#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;
}