2010-12-15 130 views
2

Est-il possible (avec le nouveau mot-clé dynamique en C# 4.0) d'utiliser des interfaces (comme IShellItem ou d'autres interfaces WinAPI) sans les définir dans mon C# code source? Ou au moins ne pas définir les membres de l'interface?C# 4.0 objet dynamique et interfaces WinAPI comme IShellItem (sans les définir dans la source C#)

J'essaie quelque chose comme:

 const string IShellItemGuid = "43826D1E-E718-42EE-BC55-A1E261C37BFE"; 
     Guid guid = new Guid(IShellItemGuid); 
     dynamic nativeShellItem = null; 

     // Create and initializes a Shell item object (IShellItem) from a parsing name 
     SHCreateItemFromParsingName(@"M:\TEST.TXT", IntPtr.Zero, guid, out nativeShellItem); 
     if (nativeShellItem != null) 
     { 
      MessageBox.Show(nativeShellItem.GetDisplayName(0)); 
     } 

[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)] 
    static extern void SHCreateItemFromParsingName(
    [In][MarshalAs(UnmanagedType.LPWStr)] string pszPath, 
    [In] IntPtr pbc, 
    [In][MarshalAs(UnmanagedType.LPStruct)] Guid iIdIShellItem, 
    [Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out dynamic iShellItem); 

Le SHCreateItemFromParsingName fonctionne très bien et je reçois un objet si le fichier existe (et un message d'erreur correct si le fichier n'existe pas), mais, en essayant d'appeler nativeShellItem.GetDisplayName me donne une exception d'exécution:

Microsoft.CSharp.RuntimeBinder.RuntimeBinderException était non gérée: 'System .__ ComObject' ne contient pas de définition pour 'GetDisplayName'

bien IShellItem a une méthode appelée GetDisplayName, et mon nativeShellItem objet dynamique renvoyé par SHCreateItemFromParsingName doit implémenter cette interface.

Répondre

3

Oui, cela ne fonctionne pas, le DLR n'a pas d'informations de type disponibles sur l'objet COM. C'est un problème notoire dans la programmation de shell, il utilise des interfaces qui sont dérivées de IUnknown, pas IDispatch donc la liaison tardive n'est pas supportée. Et il n'y a pas de bibliothèque de type disponible pour eux qui permet à .NET de générer facilement une bibliothèque d'interopérabilité pour les types d'interface.

IShellItem est déclaré dans ShObjIdl.idl, il est rempli de cpp_quote(). Ce qui détruit toute tentative d'utilisation de midl.exe pour générer une bibliothèque de types, seul un fichier .h peut être créé. Déjà fourni btw, ShObjIdl.h. Seul un compilateur C++ peut l'utiliser.

Re-déclarer l'interface en C# est techniquement possible avec [ComImport], [Guid] et [PreserveSig] attributs. Vous devez le faire très soigneusement, heureusement IShellItem dérive directement de IUnknown afin que vous puissiez esquiver la balle d'héritage multiple. Ce qui n'aide pas, c'est que la méthode d'interface utilise des types C natifs qui ne se marient pas automatiquement. Notamment GetDisplayName() prend un LPWSTR, un pointeur brut vers une chaîne Unicode. Pas un BSTR, le type de chaîne compatible avec l'automatisation. Cela vous oblige à jouer avec des pointeurs dangereux dans votre code C#. La meilleure chose à faire est de le déclarer IntPtr, d'allouer un morceau de mémoire avec Marshal.AllocCoTaskMem() et de marshaler la chaîne après l'appel avec Marshal.PtrToStringUni(). Yuck, la programmation shell est un pita complet. Google le diable hors de cela afin que vous puissiez copier/coller quelque chose qui est connu pour fonctionner. Si cela apparaît vide, l'utilisation de C++/CLI pour pouvoir utiliser ShObjIdl.h est définitivement la meilleure solution. Bonne chance.