我在使用COM Interop时遇到了一些问题,情况如下:
32位COM Exe服务器(用C ++编程)提供了一个类,其中包含一些处理第三方硬件的成员函数(此硬件也将COM Exe Server绑定到32位,因为制造商没有支持64位)。
我想在64位.NET(C#)应用程序中使用32位COM Exe Server ...首先,我尝试在Visual Studio 2010中添加对Exe Server的引用,并创建了一个Interop- DLL。这个Interop-DLL为我提供了必要的功能,其中一个被声明为:
int Initialize(ref string callingApplicationPath);
C ++中的原始声明如下所示:
LONG Class::Initialize(BSTR* callingApplicationPath)
......在IDL中也是如此:
[id(1)] LONG Initialize([in] BSTR* callingApplicationPath);
但是,当我想通过Interop-DLL从C#调用此函数时,它会抛出BadImageFormatException。看起来Interop-DLL是一个32位DLL(也许有可能生成64位DLL?)。
我的下一次尝试是使用以下代码实例化Exe Server:
Type type = Type.GetTypeFromProgID("OurCompany.Class");
Object o = Activator.CreateInstance(type);
Object[] args = { Marshal.StringToBSTR(str) };
Object result = type.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, o, args);
另一方面,这段代码会抛出一个TargetInvocationException(更具体地说:0x80020005(DISP_E_TYPEMISMATCH))。不幸的是我无法找到我必须从C#传递给函数的类型...我在Marshal类中尝试了所有的StringToXXX函数但似乎没有任何工作:/我想我在这里缺少一些简单的东西,但我看不出来。
非常感谢任何帮助!
最好的问候
基督教
答案 0 :(得分:1)
默认情况下,.NET字符串由COM Interop编译到C ++中的LPTSTR。因此,您必须使用MarshalAs属性将任何其他类型的非托管字符串(包括BSTR)显式封送到.NET字符串中。 尝试
int Initialize([MarshalAs(UnmanagedType.BStr)] ref string callingApplicationPath);
答案 1 :(得分:1)
IDL声明
[id(1)] LONG Initialize([in] BSTR* str);
毫无意义。当您将BSTR
作为in
参数传递时,只需将其“按值”传递:
[id(1)] LONG Initialize([in] BSTR str);
然后你不需要在C#代码中做任何特殊的事情 - 只需在那里传递string
并且编组将自动完成。
当然,您还必须更改方法实现签名。
答案 2 :(得分:0)
由于.net使用的公共语言运行库,只有少数情况下您必须使用托管代码区分32位和64位。然而,这仅适用于.net envoirement。如果您尝试访问非托管资源,则位格式很重要,因为所有地址(导出的接口)都是静态的,而不是为64位编译。
你仍然可以使用一个非常简单的构造来完成你的任务;
创建一个32位的.net包装器并通过wcf将它连接到你的64位应用程序。我建议为你的com / unmanaged服务器创建一个混合模式的c ++包装器,并将一个基于wcf的层写在“pure”clr(c#,vb.net等)中作为你的主应用程序的连接点。