Sous Windows XP, vous aviez surement remarqué le fichier « Thumbs.db » présent dans tous les dossiers contenant des images. Ce fichier n'est en fait rien d'autre que la base de données des vignettes (thumbnails) générées par le système d'exploitation.

Sous Windows Vista, l'approche est totalement différente. En effet, plus de fichier « Thumbs.db » dans chaque dossier, mais une gestion centralisée des vignettes. On notera aussi une grande nouveauté : chaque « Shell Item » possède ou peut posséder une vignette. On ici par Shell Item n'importe quel « objet » du système d'exploitation : fichier, dossier, disque dur…

L'objectif de cet article est d'expliquer comment récupérer la vignette associée à un Shell Item défini sous la forme d'une instance de la classe « ImageSource » pour l'exploiter directement en WPF (Windows Presentation Foundation).

Pour ce faire nous allons utiliser l'interface IShellItemImageFactory de l'API Win 32 Windows Vista et donc le développement COM. Avant toute chose, nous devons être capables de récupérer une instance dérivant de l'interface IShellItem pour un item shell donné du système.

Implémentation de l'interface IShellItem en C#

Afin de récupérer une instance de type IShellItem pour demander notre vignette, il nous faut tout d'abord définir ce qu'est cette interface. Pour cela jetons un rapide coup d'œil à sa définition présentée sur le site de la MSDN. Celle-ci présente 5 méthodes (en gras la correspondance des types que nous utiliseront en C#) :

  • BindToHandler
    • Lie l'item à son handler à partir de l'ID (BHID)
    • Paramètres:
      • IBindCtx *pbc è IntPtr
      • REFGUID rbhid è Guid
      • REFIID riid è Guid
      • void **ppvOut è out IntPtr
  • Compare
    • Compare 2 objets de type IShellItem
    • Paramètres :
      • IShellItem *psi è IShellItem
      • SICHINTF hint è uint
      • Int *piOrder è out int
  • GetAttributes
    • Récupère les attributs de l'objet IShellItem
    • Paramètres :
      • SFGAOF sfgaofMask è uint
      • SFGAOF *psfgaoAttribs è out int
  • GetDisplayName
    • Récupère le nom d'affichage de l'objet IShellItem
    • Paramètres :
      • SIGDN sigdnName : è SIGDN
      • LPWSTR *ppszName è IntPtr
  • GetParent
    • Récupère l'item shell parent de l'objet IShellItem
    • Paramètre :
      • IShellItem **ppsi è out IShellItem

Après cette description nous voyons qu'il nous faut créer une interface IShellItem, mais aussi une énumération SIGDN.

L'énumération SIGDN

/// <summary>

/// Représente le type de nom d'affichage que l'on souhaite récupérer

/// Voir MSDN : http://msdn.microsoft.com/en-us/library/bb762544(VS.85).aspx

/// </summary>

public enum SIGDN : uint

{

SIGDN_NORMALDISPLAY = 0x00000000,

SIGDN_PARENTRELATIVEPARSING = 0x80018001,

SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001,

SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,

SIGDN_PARENTRELATIVEEDITING = 0x80031001,

SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,

SIGDN_FILESYSPATH = 0x80058000,

SIGDN_URL = 0x80068000

}

voir pinvoke.net : http://pinvoke.net/default.aspx/Enums.SIGDN

L'interface IShellItem

[ComImport]

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]

public interface IShellItem

{

void BindToHandler(IntPtr pdc, [MarshalAs(UnmanagedType.LPStruct)]Guid rbhid, [MarshalAs(UnmanagedType.LPStruct)]Guid riid, out IntPtr ppvOut);

void Compare(IShellItem psi, uint hint, out int piOrder);

void GetAttributes(uint sfgaofMask, out uint psfgaoAttribs);

void GetDisplayName(SIGDN sigdnName, IntPtr ppszName);

}

voir pinvoke.net : http://www.pinvoke.net/default.aspx/Interfaces.IShellItem

Quelques précisions sur la programmation COM (Component Object Model) :

  • L'attribut [ComImport] : indique que le type possédant cet attribut a été définie précédemment dans COM.
  • L'attribut [InterfaceType] : indique le type d'interface. Ici, on précise que c'est une interface définie dans COM, et qui hérite de l'interface IUnknown de COM.
  • L'attribut [Guid] : précise le Guid explicite pour cette interface COM.

Maintenant que nous avons écrit l'interface IShellItem, nous allons passer à l'implémentation de l'interface IShellItemImageFactory.

L'interface IShellItemImageFactory

Cette interface shell de l'API Windows Vista va tout simplement nous permettre de récupérer l'image d'un item shell donné. Celle-ci ne possède qu'une seule méthode, GetImage, détaillée ci-dessous :

HRESULT GetImage(

SIZE size,

SIIGBF flags,

HBITMAP *phbm

);

Paramètres :

  • SIZE size : structure indiquant la taille de la vignette que l'on souhaite recevoir. Le cache de Vista stockant plusieurs tailles de vignette, il est donc possible de choisir !
  • SIIGBF flags : une valeur de l'énumération SIIGBD indiquant la manière de récupérer le thumbnail souhaité :
    • SIIGBF_RESIZETOFIT 
    • SIIGBF_BIGGERSIZEOK : on accepte de retourner une taille « standard » plus grande si la taille demandée n'est pas en cache.
    • SIIGBF_MEMORYONLY : retourne le thumbnail uniquement s'il est en mémoire.
    • SIIGBF_ICONONLY : retourne l'icône associé au shell item, pas la vignette.
    • SIIGBF_THUMBNAILONLY : retourne le thumbnail associé au shell item, pas l'icône.
    • SIIGBF_INCACHEONLY : puise dans le cache uniquement.
  • HBITMAP *phbp : quand la méthode retourne, contient le « handle » de la vignette récupérée.

Nous devons donc, au préalable écrire la structure SIZE, ainsi que l'énumération SIIGBF.

Structure SIZE

/// <summary>

/// Structure spécifiant la hauteur et la largeur d'un rectangle.

/// Voir MSDN : http://msdn.microsoft.com/en-us/library/ms532297(VS.85).aspx

/// </summary>

[StructLayout(LayoutKind.Sequential)]

public struct SIZE

{

public int cx;

public int cy;

public SIZE(int _cx, int _cy)

{

this.cx = _cx;

this.cy = _cy;

}

}

voir pinvoke.net : http://pinvoke.net/default.aspx/Structures.SIZE

Enumération SIIGBF :

[Flags]

public enum SIIGBF

{

SIIGBF_RESIZETOFIT = 0x00,

SIIGBF_BIGGERSIZEOK = 0x01,

SIIGBF_MEMORYONLY = 0x02,

SIIGBF_ICONONLY = 0x04,

SIIGBF_THUMBNAILONLY = 0x08,

SIIGBF_INCACHEONLY = 0x10,

}

voir pinvoke.net : http://pinvoke.net/default.aspx/Enums.SIIGBF

L'interface IShellItemImageFactory

[ComImport]

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

[Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")]

public interface IShellItemImageFactory

{

void GetImage([In, MarshalAs(UnmanagedType.Struct)] SIZE size,[In] SIIGBF flags,[Out]out IntPtr phbm);

}

Voir pinvoke.net : http://www.pinvoke.net/default.aspx/Interfaces.IShellItemImageFactory

Nous avons maintenant tous les éléments en main pour écrire notre classe ThumbnailsManager.cs qui nous permettra de récupérer la vignette associée à un item shell et la convertir en ImageSource WPF.

La classe ThumbnailsManager

C'est dans cette classe que nous allons récupérer l'objet de type IShellItem souhaité, faire appel à la méthode GetImage de l'interface IShellItemImageFactory et donc récupérer la vignette associée à notre item shell.

Afin de récupérer une instance de IShellItem, nous utiliseront le p/invoke, et plus précisement la méthode SHCreateItemFromParsingName disponible dans la librairie « shell32.dll ».

La méthode SHCreateItemFromParsingName

Je ne vous présenterai plus le site PInvoke.net permettant de récupérer la définition des méthodes disponibles pour le plateform-invoke en C#.

Voilà ce qui est présenté pour la méthode souhaitée : (http://pinvoke.net/default.aspx/shell32.SHCreateItemFromParsingName)

[DllImport("shell32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]

public static extern void SHCreateItemFromParsingName(

[In][MarshalAs(UnmanagedType.LPWStr)] string pszPath,

[In] IntPtr pbc,

[In][MarshalAs(UnmanagedType.LPStruct)] Guid riid,

[Out][MarshalAs(UnmanagedType.Interface, IidParameterIndex = 2)] out IShellItem ppv);

A présent nous pouvons écrire la méthode GetThumbnail, prenant en paramètre le chemin d'accès complet au fichier pour lequel nous souhaitons récupérer le thumbnail.

La méthode GetThumbnail(string _path)

Cette méthode statique retourne une instance de la classe ImageSource si elle parvient à récupérer le thumbnail, lève une excéption sinon.

     public static ImageSource GetThumbnail(string _path)

{

IShellItem ppv = null;

IntPtr phbm = IntPtr.Zero;

Guid riid = new Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe");

try

{

SHCreateItemFromParsingName(_path, IntPtr.Zero, riid, out ppv);

((IShellItemImageFactory)ppv).GetImage(new SIZE(256, 256), 0x0, out phbm);

}

catch { }

if (phbm != IntPtr.Zero)

{

BitmapSource bmpSource = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(

phbm,

IntPtr.Zero,

Int32Rect.Empty,

System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()

);

Marshal.ReleaseComObject(ppv);

Marshal.Release(phbm);

return bmpSource;

}

else

{

Marshal.ReleaseComObject(ppv);

Marshal.Release(phbm);

throw new Exception(string.Format("Impossible de récupérer le thumbnail associé au fichier {0}", _path));

}

}

Cet article est à présent terminé ! Nous avons vu dans un premier temps comment récupérer une instance dérivant de l'interface IShellItem, afin de pouvoir utiliser l'interface IShellItemImageFactory afin de récupérer la vignette associée à n'importe quel shell item du système.

Pour l'exploiter, il vous suffit de créer un projet WPF Application, d'ajouter un contrôle de type Image et de lier la propriété "Source" de ce contrôle au retour de la méthode GetThumbnail !

Sources du projet : ThumbnailsManager.zip (67,75 kb) Wink

Liens :

Au total, 0 commentaire(s) posté(s) ! Poster un commentaire

comments Ajouter un commentaire

(Affichera votre icône Gravatar)  
[b][/b] - [i][/i] - [u][/u]- [quote][/quote]