【问题标题】:Failed to cast result of Marshal.GetActiveObject for COM interop wrapper无法为 COM 互操作包装器转换 Marshal.GetActiveObject 的结果
【发布时间】:2013-04-25 03:17:57
【问题描述】:

我正在努力研究如何访问 C++ 应用程序提供的 COM 接口并从 C# .NET 应用程序中使用它。

我尝试从我的 C# 应用程序访问我的 COM 对象(由正在运行的进程提供),如下所示:

object obj = Marshal.GetActiveObject("MyLibrary.Application");
MyLibrary.IMainApp app = (MyLibrary.IMainApp)obj;

obj 的值不为空,但是当我尝试转换它时,我得到了System.InvalidCastException

我尝试实现自定义包装器并链接到由TlbImp.exe 生成的 DLL。两者都产生了相同的异常。

似乎有很多关于互操作的文档,但这对我来说没有意义——主要是因为我不精通 COM(我一直在维护其他人开发的代码),并且正在进入可怕的世界深入研究 C#、.NET 和 WPF。

这是我在 C++ 端的内容......

IDL 文件

其实扩展名是.odl。我不知道这是否是另一回事。请注意,我已将 GUID 替换为 GUID-1GUID-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++ 应用程序想要调用一个函数时(例如 Quit),他们会这样做:

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# 不那么复杂。

尝试用 C# 换行

我遵循了关于 C# 中 COM 互操作的 MSDN 指南。它建议我可以使用TlbImp.exe,但这引发了足够多的警告让我望而却步。此外,我不想分发另一个 DLL。所以我采取了另一种写包装器的方法。

这是我写的:

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 );
    }
}

有人能帮我理解一下吗?也许指出我犯的一个完全明显的愚蠢错误? =)

【问题讨论】:

标签: c# com-interop idl


【解决方案1】:

您的示例 C++ 代码正在通过 IDispatch 使用 late-binding。这是正确的方法,您的 COM 服务器不支持早期绑定。从 IDL 可以看出,它使用 dispinterface 而不是 interface

C# 中的等效方法是使用 C# dynamic 关键字。像这样:

dynamic obj = Marshal.GetActiveObject("MyLibrary.Application");
obj.Quit();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-22
    • 2011-10-28
    • 2011-04-27
    • 2011-02-04
    相关资源
    最近更新 更多