无法为COM互操作包装器转换Marshal.GetActiveObject的结果 [英] Failed to cast result of Marshal.GetActiveObject for COM interop wrapper
问题描述
我正在努力研究如何访问C ++应用程序提供的COM接口并从C#.NET应用程序中使用它。
I'm struggling to work out how to access a COM interface provided by a C++ application and use it from a C# .NET application.
我尝试访问我的C#应用中的COM对象(由运行中的进程提供)是这样的:
I try to access my COM object (which is provided by a running process) from my C# app like this:
object obj = Marshal.GetActiveObject("MyLibrary.Application");
MyLibrary.IMainApp app = (MyLibrary.IMainApp)obj;
obj
的值不为空,但是当我尝试对其进行强制转换时,会得到 System.InvalidCastException
。
The value of obj
is non-null, but when I try to cast it I get System.InvalidCastException
.
我尝试实现自定义包装,并且链接到 TlbImp.exe
生成的DLL。两者都产生了相同的异常。
I tried implementing a custom wrapper and also linked against a DLL produced by TlbImp.exe
. Both produced the same exception.
关于互操作的文档似乎很多,但这对我来说没有意义-主要是因为我不精通COM (我坚持维护其他人开发的代码),并通过深入研究进入C#、. NET和WPF的可怕世界。
There seems to be a lot of documentation regarding interop, but it's not making sense to me -- mostly because I'm not COM-savvy (I am stuck maintaining code that someone else developed), and am entering the scary world of C#, .NET and WPF by diving headlong into it.
这就是我所拥有的在C ++端。...
Here's what I have on the C++ end....
IDL文件
实际上,扩展名是 .odl
。我不知道那是另一回事。请注意,我已经用 GUID-1
, GUID-2
etc等占位符替换了GUID。 em> ...
Actually, the extension is .odl
. I don't know if that's a different thing. Note that I've replaced the GUIDs with placeholders like GUID-1
, GUID-2
etc...
import "oaidl.idl";
[ uuid(GUID-1), helpstring("My Type Library"), version(1.1) ]
library MyLibrary
{
importlib("stdole32.tlb");
// Primary dispatch interface for CMainApp
[ uuid(GUID-2) ]
dispinterface IMainApp
{
properties:
[id(1)] BOOL Mode;
methods:
[id(2)] void Quit();
[id(3)] void LogEventMessage(BSTR szMessageType, BSTR szMessage);
[id(4)] BOOL GetNoteDetails(BSTR szQualifiedName, BSTR* lpbstrOperator, DATE* lpdtDate);
};
// Class information for CMainApp
[ uuid(GUID-3) ]
coclass Application
{
[default] dispinterface IMainApp;
};
};
示例用法
当另一个C ++应用程序要调用函数( eg Quit
)时,他们会这样做:
When another C++ application wants to call a function (eg Quit
), they do this:
CLSID clsid;
HRESULT hr;
LPUNKNOWN lpUnk;
LPDISPATCH lpDisp;
BOOL bReturn = FALSE;
// Get Class ID
if (CLSIDFromProgID( OLESTR("MyLibrary.Application"), &clsid ) == S_OK )
{
// Get Active Object from ROT
if ( ( hr = GetActiveObject( clsid, NULL, &lpUnk ) ) == S_OK )
{
hr = lpUnk->QueryInterface( IID_IDispatch, (LPVOID*)&lpDisp );
lpUnk->Release();
if ( hr == S_OK )
{
CMainApp oMainApp;
oMainApp.AttachDispatch( lpDisp );
TRY
{
oMainApp.Quit();
bReturn = TRUE;
}
CATCH( CException, e )
{
bReturn = FALSE;
}
END_CATCH
}
}
}
类 CMainApp
似乎是由IDL编译器自动生成的。该文件被编译到要使用的项目中,并且基本上执行了许多 InvokeHelper
调用。我很高兴接受这只是它的工作方式,并且希望C#不会引起混淆。
The class CMainApp
seems to have been auto-generated by the IDL compiler. That file is compiled into the project that wants to use it, and basically does a whole lot of InvokeHelper
calls. I'm happy enough to accept this is just the way it works, and I hope that C# is less convoluted.
试图用C#包装 >
我遵循了C#中有关COM互操作的MSDN指南。它建议我可以使用 TlbImp.exe
,但这引发了足够的警告让我失望了。此外,我不想分发另一个DLL。因此,我采用了另一种编写包装器的方法。
I followed an MSDN guide on COM interop in C#. It suggested I could use TlbImp.exe
but that threw up enough warnings to put me off. Besides, I don't want to distribute another DLL. So I took the other approach of writing a wrapper.
这就是我写的:
using System.Runtime.InteropServices;
namespace MyLibrary
{
// Declare MainApp COM coclass
[ComImport, Guid("GUID-3")]
class MainApp
{
}
[Guid("GUID-2"), InterfaceType(ComInterfaceType.InterfaceIsDual)]
interface IMainApp
{
public void Quit();
public void LogEventMessage(
[In, MarshalAs(UnmanagedType.BStr)] string szMessageType,
[In, MarshalAs(UnmanagedType.BStr)] string szMessage );
public bool GetNoteDetails(
[In, MarshalAs(UnmanagedType.BStr)] string szQualifiedName,
[Out, MarshalAs(UnmanagedType.BStr)] out string lpbstrOperator );
out DateTime lpdtDate );
}
}
有人可以帮我理解吗?也许指出我犯了一个完全显而易见的愚蠢错误? =)
Can someone gimme a hand to understand this? Perhaps point out a totally obvious stupid mistake that I've made? =)
推荐答案
您的示例C ++代码通过IDispatch使用 late-binding 。正确的做法是,您的COM服务器不支持早期绑定。您可以从IDL看出,它使用 dispinterface 而不是 interface 。
Your sample C++ code is using late-binding through IDispatch. Which is the correct way to do it, your COM server doesn't support early binding. You can tell from the IDL, it uses dispinterface instead of interface.
C#中的等效方法是使用C# dynamic
关键字。像这样:
The equivalent way in C# is by using the C# dynamic
keyword. Like this:
dynamic obj = Marshal.GetActiveObject("MyLibrary.Application");
obj.Quit();
这篇关于无法为COM互操作包装器转换Marshal.GetActiveObject的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!