2012-05-21 43 views
0

首先,我必須說我是C#,.NET和COM Interop的新手。如何聲明一個COM接口,以便它可以被轉換?

我收到以下錯誤消息時我嘗試投了COM對象的接口類型我寫道:

錯誤消息:無法投類型的COM對象「系統.__ ComObject」的接口類型「觀察員。 IObserver」。此操作失敗,因爲IID爲「{13478219-8C3B-4849-99D9-27CEF1A49A55}」的接口的COM組件上的QueryInterface調用由於以下錯誤而失敗:沒有支持此接口(異常來自HRESULT:0x80004002(E_NOINTERFACE)) 。

我使用VS2010(.NET Framework 3.5的)在Windows 7

這裏是我的界面(觀察員類庫項目):

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.InteropServices; 

namespace Observer 
{ 
    [ComImport] 
    [Guid("2B2D0BC7-A7C6-4924-A3DE-42F7075E5947")] 
    public interface IObservable 
    { 
     void attach(IObserver observer); 
     void detach(IObserver observer); 

     void notify(); 
    } 

    [ComImport] 
    [Guid("13478219-8C3B-4849-99D9-27CEF1A49A55")] 
    public interface IObserver 
    { 
     void update(IObservable observable); 
    } 
} 

當建立這個dll,我有這樣的警告:
Observer.dll不包含任何可以爲COM Interop註冊的類型(當然,因爲這些接口是在其他dll中實現的)。
IObserver未出現在我的註冊表中(不存在於HKEY_CLASSES_ROOT \ Interface中)。

這裏是失敗碼(合成類庫項目):

// Arguments de la méthode permettant de récupérer un objet selon son chemin 
Object[] args; 
// Objet représentant l'état technique de la synthèse 
Observer.IObserver pEtatTechniqueSynthese; 

args = new Object[] { m_sFullName + "/Etat_Technique" }; 

pEtatTechniqueSynthese = m_typeSite.InvokeMember("FindObject2", 
         BindingFlags.InvokeMethod, 
         null, m_pSite, args) as Observer.IObserver; 

//Here pEtatTechniqueSynthese is null 

//This call works fine without casting when pEtatTechniqueSynthese's type is Object 
//but I need to cast it because my Observer.IObservable's attach method waits for an Observer.IObserver 
//If I don't cast I get a "Exception has been thrown by the target of an invocation" 
//=> InnerException : "La valeur n'est pas comprise dans la plage attendue" 
//I don't know the exact translation, but it sort of means "Value not in expected range" 
pEtatTechniqueSynthese = (Observer.IObserver)m_typeSite.InvokeMember("FindObject2", 
         BindingFlags.InvokeMethod, 
         null, m_pSite, args); 

//Exception raised 

的pEtatTechniqueSynthese真正的類型,Etat_Technique(合成類庫項目),實現Observer.IObserver:

public class Etat_Technique : CODRA.SDK.DotNetUtils.COM.IObjectWithSite, Observer.IObservable, Observer.IObserver, ICalculateurEtatTechnique 

我的全部組件是COM可見的。 我的觀察者項目構建選項「註冊COM Interop」被選中,我用一個強名稱密鑰文件在我的程序集上簽名。

我沒有訪問COM服務器代碼(第三方組件),但我確定這件事來自我的代碼。
有沒有人對我失蹤的線索有所瞭解?

=============================================

有關第三方軟件的更多信息:

該軟件管理對象。
數據結構似乎是一個對象樹,根節點被稱爲站點。
當一個dll類想要訪問一個對象時,它必須從該站點調用「FindObject2」方法,並將其傳遞給對象路徑。這個方法顯然返回一個COM對象,所以我們可以調用方法,獲取屬性,...

我可以開發自己的對象類型並將它們添加到軟件中,描述類(指定程序集,類,dll ,屬性...)。

在那裏,我想獲得一個我開發的對象,並將它從COM對象轉換回它實現的接口。
將對象聲明爲Object並調用它的方法工作得很好。
在那裏我需要一個Observer將它附加到Observable上,所以我必須施放。
一個解決方案肯定會使attach方法的參數成爲一個Object,但是如果我這樣做了,那麼使用接口就沒有興趣了。

=============================================

基於Michael Edenfield's answer更多信息:

[ComImport括號開]

的ComImport屬性是一個猜測。我使用它是因爲我可以訪問爲與第三方代碼交互而使用的utils文件。
這是在此文件中定義的接口。當我通過第三方軟件文檔的方式實現它時,我可以訪問站點根對象。
我猜他們是否使用過它,我也應該使用它。

[ComVisible(true)] 
[ComImport] 
[Guid("FC4801A3-2BA9-11CF-A229-00AA003D7352") ] 
[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] 
public interface IObjectWithSite 
{ 
    void SetSite([MarshalAs(UnmanagedType.IUnknown)] 
     [In] object pSite); 
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")] 
    void GetSite([In] ref Guid riid, [Out] out IntPtr pvSite); 
} 

我會刪除這個屬性,你明白我比我更好的方式是什麼的基礎。此外,問題是一樣的,所以這個屬性是無用的。

[ComImport圓括號關]

嚴重,FindObject2有效地返回{系統.__ ComObject},所以鑄造不工作,我堅持了一個InvalidCastException,又一次,又一次,又一次,..
好的一點是我知道它應該是什麼基礎類。

我的部分作品,即「關於第三方軟件的更多信息」中談到使得附加方法獲取對象參數,然後管理對象,而不是IObservers解決方法。 這樣做我調用IObserver的更新方法使用反射:

foreach (Object observer in m_observers) 
{ 
    Object[] args = new Object[] { this }; 
    observer.GetType().InvokeMember("update", 
          BindingFlags.InvokeMethod, 
          null, observer, args); 
} 

Eeew,不是嗎?但是如果我沒有找到更清潔的東西,這種糟糕的事情會使工作完成。

+1

它看起來像COM對象的類型根本不支持'GUID'。 –

+0

確保你的界面有GUID。你不需要讓你的應用程序COM =可見或註冊COM互操作就可以使用第三方COM組件。 –

+0

你的意思是已經使用了GUID?我認爲用guidgen生成它會檢查它...你稱爲COM對象是什麼? – Rifu

回答

1

我不是你想實現什麼真正清楚,但它看起來像你在這裏混淆幾種不同的COM互操作的概念。

導入使用ComImportAttribute一個COM接口當接口在外部類型庫已定義某處纔有意義。如果這是您在C#中爲您自己的類創建的新接口,那麼將其聲明爲COM Import屬性將無濟於事。這是因爲沒有實際的COM對象會實現你的接口,所以你永遠不會得到一個指向QueryInterface的指針。從你對問題的描述中,ComImport完全是錯誤的路要走。

如果您需要公開自己的接口,COM客戶端,你只需要給他們一個GUID。如果您忽略ComImport屬性,則任何ComVisible類或接口都將被註冊爲interop,假設您已啓用該類。但是,COM客戶端需要了解您的界面並重新編譯以實現它。我也不認爲這會解決你的問題。

我不知道你的第三方庫究竟是如何實例化自定義類;如果這是通過COM來完成的,那麼你將需要導出你的CoClass和接口定義來工作。它要求彙編和課程信息的事實使我懷疑這不會被需要。

這聽起來像你正在獲得一個託管的C#對象創建的地方,它實現您的託管C#接口,你只需要得到它。如果FindObject方法實際上是返回您的類型的實例,那麼您應該首先將返回值轉換爲具體的C#類型,然後嘗試將其返回到您的界面。如果您嘗試使用ComObject上的類型轉換運算符來轉換爲接口,它將運行QueryInterface,這幾乎肯定會失敗。

我從來沒有試過這個,但作爲第一次猜測,我會建議首先對System.Object進行類型轉換。從那裏開始,C#應該使用託管類型元數據來確定您的接口是否可用並進行適當轉換。

當然,如果FindObject返回一個實際的COM對象,它以某種方式包裹在你的C#類中,你需要弄清楚如何首先從返回值中獲取底層類。

請注意,包含在第三方軟件文檔中的接口是IObjectWithSite。這是Windows定義的一個衆所周知的界面,所以如果你打算實現這個界面,你需要使用[ComImport]來做。在這種情況下,站點根對象將在您的自定義對象上調用IObjectWithSite::SetSite()並自行傳入,這是嵌入對象「瞭解」其容器的方式。由於該接口是外部定義的,因此您需要「導入」它,以便每個人都實施相同的接口。

+0

是的,你明白我想要做什麼。我知道這是一個複雜的案例,我是新手,所以我不擅長解釋它。我得到了由第三方代碼創建的託管C#對象,該代碼實現了我的託管C#接口,並且我只需要獲取它想要的類型。問題是第三方軟件文檔沒有完成這個主題。我根據這個答案編輯了這個問題。謝謝你試圖幫助我。 – Rifu

+0

查看我的編輯IObjectWithSite的詳細信息 –

+0

好的。所以ComImport被用來獲得外部定義的COM接口。謝謝你的解釋。我認爲我現在對C#和COM太新了。在碰到這種東西之前,我應該掌握基本知識。但這不是我的老闆的意見(「你知道C++嗎?所以你知道C#!C#,.NET,COM,它們都是一樣的!」)!我想我暫時仍然會用我骯髒的解決方案。這裏是一個COM新手的鏈接:[最終了解更改燈泡後的COM](http://www.moserware.com/2008/01/finally-understanding-com-after.html)。這並沒有解決我的問題,但它澄清了情況。 – Rifu

相關問題