2013-05-01 85 views
0

我正在努力研究如何訪問由C++應用程序提供的COM接口並從C#.NET應用程序中使用它。無法爲COM互操作包裝器投射Marshal.GetActiveObject的結果

我試圖從我的C#應用​​程序訪問我的COM對象(被正在運行的進程提供)這樣的:

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

obj值不爲null,但是當我嘗試將其強制轉換我得到System.InvalidCastException

我試着實現一個自定義的包裝,並鏈接到由TlbImp.exe產生的DLL。兩者都產生了相同的例外。

似乎有很多關於interop的文檔,但它對我來說沒什麼意義 - 主要是因爲我不懂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); 
    } 
} 

有人可以給我一個手理解?也許指出我犯了一個完全明顯的愚蠢錯誤? =)

+0

看看這個,它可能會幫助你:http://stackoverflow.com/a/16209219/2095843 – mtsiakiris 2013-05-01 07:03:55

回答

1

您的示例C++代碼通過IDispatch使用後期綁定。這是正確的方法,你的COM服務器不支持早期綁定。您可以從IDL知道,它使用調制解調器接口而不是接口

C#中的等效方式是使用C#dynamic關鍵字。像這樣:

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