2013-02-07 77 views
3

注:有關背景信息,請參閱此相關的問題:How to get LINQPad to Dump() System.__ComObject references?如何從CLSID獲取Runtime Callable Wrapper類的System.Type?

我能夠檢索對應於從另一個COM對象獲得一個COM對象(RCW類的CLSID,通過我的代碼初始化)使用IPersist.GetClassID()

Type.GetTypeFromCLSID()總是返回弱類型的System.__ComObject,而不是強類型的RCW類。

我需要獲取強類型的RCW類的System.Type,以便能夠使用Marshal.CreateWrapperOfType()與它一起包裝COM對象。

由於COM互操作的方式,這是可能的還是這是不可行的?

+0

傳遞給CreateWrapperOfType的第二個參數需要在.NET空間中定義(使用ComImport屬性等),所以如果不以某種方式定義此類型(使用C#代碼,tlbimp或Reflection Emit),它將不起作用 –

+0

我有定義RCW的PIA(更多信息請參閱相關問題)。 – blah238

+0

是的,這不會改變答案:-)它仍然是你的工作提供的類型,因爲在.NET空間中可以有一組無限的類型定義相應的COM對象。所以,如果你有PIA,你可以瀏覽PIA命名空間中的所有類(例如使用Reflection),建立一個'Dictionary ',並且當你有了這個GUID時,就可以從這個字典中提供這個類型。 –

回答

0

那麼這裏就是我最終將作爲一個概念證明放在一起,只是一些擴展方法,真的。這依賴於實現IPersist的COM對象,並且在當前的AppDomain中加載的PIA中的一箇中具有RCW等級。

internal static class ExtensionMethods 
{ 
    internal static object ConvertToRCW(this object o) 
    { 
     var guid = o.GetCLSID(); 
     if (guid != Guid.Empty) 
     { 
      return Marshal.CreateWrapperOfType(o, o.GetTypeFromGuid(guid)); 
     } 
     else 
     { 
      return o; 
     } 
    } 

    internal static Guid GetCLSID(this object o) 
    { 
     Guid guid = Guid.Empty; 
     var p = o as IPersist; 
     if (p != null) 
      p.GetClassID(out guid); 
     return guid; 
    } 

    internal static Type GetTypeFromGuid(this object o, Guid guid) 
    { 
     var assemblies = AppDomain.CurrentDomain.GetAssemblies(); 
     foreach (var assembly in assemblies) 
     { 
      var types = assembly.GetLoadableTypes(); 
      foreach (var type in types) 
      { 
       if (type.GUID == guid) 
        return type; 
      } 
     } 
     return o.GetType(); 
    } 

    internal static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) 
    { 
     try 
     { 
      return assembly.GetTypes(); 
     } 
     catch (ReflectionTypeLoadException e) 
     { 
      return e.Types.Where(t => t != null); 
     } 
    } 
} 

像這樣來使用:

var point = new ESRI.ArcGIS.Geometry.Point(); 
point.PutCoords(1, 1); 
Console.WriteLine(point.GetType().FullName); 
Console.WriteLine(point.Envelope.GetType().FullName); 
Console.WriteLine(point.Envelope.ConvertToRCW().GetType().FullName); 

我得到以下輸出:

ESRI.ArcGIS.Geometry.PointClass 
System.__ComObject 
ESRI.ArcGIS.Geometry.EnvelopeClass

這是理想的結果。現在,使用LINQPad(我的original question)這個遊戲很好。

1

Type.GetTypeFromCLSID()只是返回一個動態的COM包裝。

強類型的RCW必須在.NET空間中定義。它必須是用ComImportAttribute裝飾的課程。 .NET不能自動創建這些類。它們是手動定義的(使用.NET代碼),PIA定義的,或者tlbimp定義的,或者是像所有.NET類型那樣通過反射發射機制定義的。 COM CLSID和.NET對應類之間沒有預設關係,因爲可能有多個.NET類對應相同的CLSID。

如果您有這些類型可用,您可以執行的操作是掃描一組定義的命名空間並從中構建一個Dictionary<Guid, Type>

+0

我只是想說這*是*有用的,謝謝。我認爲在我的情況下,CLSID被保證唯一地綁定到一個特定的強類型的RCW--但是如果COM對象實現了「IPersist」(一個有價值的庫I的百分比),我只能獲得CLSID,米工作,否則我不會打擾)。不過,如果有預定義的機制,那將會很好。 – blah238

+0

您還可以檢查的是,如果COM對象實現IProvideClassInfo:http://msdn.microsoft.com/fr-fr/library/windows/desktop/ms687303(v=vs.85).aspx。從那裏你可以得到一個ITypeInfo,並從那裏獲得類型GUID:http://stackoverflow.com/questions/4651869/given-a-com-dll-extract-all-classes-clsid-and-corresponding-interface-name ,但你必須在你的代碼中定義所有這些接口:-) –

+0

是的,不幸的是他們都沒有實現IProvideClassInfo,只有少數實現IDispatch。 – blah238

相關問題