无法为COM互操作包装器转换Marshal.GetActiveObject的结果 [英] Failed to cast result of Marshal.GetActiveObject for COM interop wrapper

查看:254
本文介绍了无法为COM互操作包装器转换Marshal.GetActiveObject的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在努力研究如何访问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屋!

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