// Please do not remove :) // Written by Kourosh Derakshan // using System; using System.IO; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; /// /// Allows reading of embedded thumbnail image from the EXIF information in an image. /// public abstract class ExifThumbReader { // GDI plus functions [DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)] internal static extern int GdipGetPropertyItem(IntPtr image, int propid, int size, IntPtr buffer); [DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)] internal static extern int GdipGetPropertyItemSize(IntPtr image, int propid, out int size); [DllImport("gdiplus.dll", CharSet=CharSet.Unicode, ExactSpelling=true)] internal static extern int GdipLoadImageFromFile(string filename, out IntPtr image); [DllImport("gdiplus.dll", EntryPoint="GdipDisposeImage", CharSet=CharSet.Unicode, ExactSpelling=true)] private static extern int GdipDisposeImage(IntPtr image); // EXIT tag value for thumbnail data. Value specified by EXIF standard private static int THUMBNAIL_DATA = 0x501B; /// /// Reads the thumbnail in the given image. If no thumbnail is found, returns null /// public static Image ReadThumb (string imagePath) { const int GDI_ERR_PROP_NOT_FOUND = 19; // Property not found error const int GDI_ERR_OUT_OF_MEMORY = 3; IntPtr hImage = IntPtr.Zero; IntPtr buffer = IntPtr.Zero; // Holds the thumbnail data int ret; ret = GdipLoadImageFromFile (imagePath, out hImage); try { if (ret!=0) throw createException (ret); int propSize; ret = GdipGetPropertyItemSize (hImage, THUMBNAIL_DATA, out propSize); // Image has no thumbnail data in it. Return null if (ret==GDI_ERR_PROP_NOT_FOUND) return null; if (ret!=0) throw createException (ret); // Allocate a buffer in memory buffer = Marshal.AllocHGlobal(propSize); if (buffer == IntPtr.Zero) throw createException (GDI_ERR_OUT_OF_MEMORY); ret = GdipGetPropertyItem (hImage, THUMBNAIL_DATA, propSize, buffer); if (ret!=0) throw createException (ret); // buffer has the thumbnail data. Now we have to convert it to // an Image return convertFromMemory (buffer); } finally { // Free the buffer if (buffer != IntPtr.Zero) Marshal.FreeHGlobal (buffer); GdipDisposeImage (hImage); } } /// /// Generates an exception depending on the GDI+ error codes (I removed some error /// codes) /// private static Exception createException (int gdipErrorCode) { switch (gdipErrorCode) { case 1: return new ExternalException("Gdiplus Generic Error", -2147467259); case 2: return new ArgumentException("Gdiplus Invalid Parameter"); case 3: return new OutOfMemoryException("Gdiplus Out Of Memory"); case 4: return new InvalidOperationException("Gdiplus Object Busy"); case 5: return new OutOfMemoryException("Gdiplus Insufficient Buffer"); case 7: return new ExternalException("Gdiplus Generic Error", -2147467259); case 8: return new InvalidOperationException("Gdiplus Wrong State"); case 9: return new ExternalException("Gdiplus Aborted", -2147467260); case 10: return new FileNotFoundException("Gdiplus File Not Found"); case 11: return new OverflowException("Gdiplus Over flow"); case 12: return new ExternalException("Gdiplus Access Denied", -2147024891); case 13: return new ArgumentException("Gdiplus Unknown Image Format"); case 18: return new ExternalException("Gdiplus Not Initialized", -2147467259); case 20: return new ArgumentException("Gdiplus Property Not Supported Error"); } return new ExternalException("Gdiplus Unknown Error", -2147418113); } /// /// Converts the IntPtr buffer to a property item and then converts its /// value to a Drawing.Image item /// private static Image convertFromMemory (IntPtr thumbData) { propertyItemInternal prop = (propertyItemInternal)Marshal.PtrToStructure (thumbData, typeof (propertyItemInternal)); // The image data is in the form of a byte array. Write all // the bytes to a stream and create a new image from that stream byte[] imageBytes = prop.Value; MemoryStream stream = new MemoryStream (imageBytes.Length); stream.Write (imageBytes, 0, imageBytes.Length); return Image.FromStream(stream); } /// /// Used in Marshal.PtrToStructure(). /// We need this dummy class because Imaging.PropertyItem is not a "blittable" /// class and Marshal.PtrToStructure only accepted blittable classes. /// (It's not blitable because it uses a byte[] array and that's not a blittable /// type. See MSDN for a definition of Blittable.) /// [StructLayout(LayoutKind.Sequential)] private class propertyItemInternal { public int id=0; public int len=0; public short type=0; public IntPtr value=IntPtr.Zero; public byte[] Value { get { byte[] bytes = new byte[(uint)len]; Marshal.Copy(value, bytes, 0, len); return bytes; } } } }