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 :
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)
Liens :