2013-02-07 2 views
3

Note: Pour plus d'information s'il vous plaît voir cette question connexe: How to get LINQPad to Dump() System.__ComObject references?Comment obtenir le System.Type d'une classe Wrapper Runtime Callable à partir de son CLSID?

Je suis en mesure de récupérer le CLSID de la classe RCW correspondant à un objet COM (obtenu à partir d'un autre objet COM, pas initialisé par mon code) en utilisant IPersist.GetClassID().

Type.GetTypeFromCLSID() retourne toujours le System.__ComObject faiblement typé, pas la classe RCW fortement typée.

Je dois obtenir le System.Type de la classe RCW fortement typée pour pouvoir envelopper l'objet COM avec lui en utilisant Marshal.CreateWrapperOfType().

Est-ce possible ou est-ce un non-démarreur en raison du fonctionnement de COM Interop?

+0

Le deuxième paramètre transmis à CreateWrapperOfType doit être défini dans l'espace .NET (avec les attributs ComImport, etc.). Il ne fonctionnera donc pas sans définir ce type (en utilisant C# code, tlbimp ou Reflection Emit) –

+0

J'ai des PIA avec les RCW définis (voir la question connexe pour plus d'informations). – blah238

+0

Oui, cela ne change pas la réponse :-) il est toujours votre travail pour fournir le type, comme il peut y avoir un ensemble infini de types dans l'espace .NET qui définissent l'objet COM correspondant. Donc, si vous avez des PIAs, vous pouvez parcourir toutes les classes dans l'espace de noms PIAs (en utilisant Reflection par exemple), construire un 'Dictionary ', et fournir le type de ce dictionnaire quand vous avez le guid. –

Répondre

0

Eh bien voici ce que j'ai fini par mettre en place comme preuve de concept, juste une poignée de méthodes d'extension, vraiment. Cela repose sur l'objet COM et la mise en œuvre IPersist ayant une classe RCW dans l'un des PIA chargés dans le AppDomain courant.

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

Utilisé comme ceci:

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

Je reçois la sortie suivante:

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

Quel a été le résultat souhaité. Maintenant, pour rendre ce jeu agréable avec LINQPad (mon original question).

1

Type.GetTypeFromCLSID() renvoie simplement un wrapper COM dynamique.

RCW fortement typé doit être défini dans l'espace .NET. Ce doit être des classes qui sont décorées avec le ComImportAttribute. .NET ne peut pas créer ces classes automatiquement ex-hihilo. Ils sont définis manuellement (dans le code .NET), ou par PIA, ou par tlbimp, ou par le mécanisme Reflection Emit par exemple, comme tous les types .NET. Il n'y a pas de relation prédéfinie entre un CLSID COM et des classes correspondantes .NET pour la raison qu'il peut y avoir plusieurs classes .NET correspondant au même CLSID.

Si vous avez ces types disponibles, ce que vous pouvez faire est d'analyser un ensemble défini d'espaces de noms et d'en construire un Dictionary<Guid, Type> par exemple.

+0

Je voulais juste dire que c'est * est * utile, et merci. Je pense que dans mon cas le CLSID est garanti être lié uniquement à un RCW particulier fortement typé - mais je ne peux obtenir le CLSID qu'en premier lieu si l'objet COM implémente 'IPersist' (un pourcentage intéressant de la bibliothèque I ' Je travaille avec, sinon je ne voudrais pas déranger). Ce serait bien s'il y avait un mécanisme prédéfini pour cela. – blah238

+0

Vous pouvez également vérifier si l'objet COM implémente IProvideClassInfo: http://msdn.microsoft.com/fr-fr/library/windows/desktop/ms687303(v=vs.85).aspx. De là, vous pouvez obtenir un ITypeInfo, et à partir de là le type GUID: http://stackoverflow.com/questions/4651869/given-a-com-dll-extract-all-classes-clsid-and-corresponding-interface-name , mais vous devrez définir toutes ces interfaces dans votre code :-) –

+0

Oui, malheureusement, aucune d'entre elles n'implémente IProvideClassInfo, et seulement une poignée d'IDispatch. – blah238

Questions connexes