0

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

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

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

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

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

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

这就是我在 C++ 端所拥有的......

IDL 文件

实际上,扩展名是.odl. 我不知道这是否是另一回事。请注意,我已将 GUID 替换为GUID-1GUID-2 占位符...

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

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

4

1 回答 1

1

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

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

dynamic obj = Marshal.GetActiveObject("MyLibrary.Application");
obj.Quit();
于 2013-05-01T09:38:32.900 回答