# VBForums CodeBank > CodeBank - C# >  Extract thumbnail image from a file without reading the whole file

## MrPolite

You could use Image.GetThumbnailImage() to get a thumbnail of an already loaded image, but if you have a lot of images and you do that, it'd be fairly slow because you would have to load all the images. 
Alternatively most images taken with cameras have a thumbnail of the image stored in the image metadata, and that can be accessed without loading the image as a whole.
If you have .NET 1.1 installed then you can do this:

*.NET 1.1 version only*


```
// Please do not remove :)
// Written by Kourosh Derakshan
// 
using System.IO;
using System.Drawing.Imaging;

private const int THUMBNAIL_DATA = 0x501B;


/// <summary>
/// Gets the thumbnail from the image metadata. Returns null of no thumbnail
/// is stored in the image metadata
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
Image GetThumbnail (string path)
{
	FileStream fs = File.OpenRead (path);
	// Last parameter tells GDI+ not the load the actual image data
	Image img = Image.FromStream (fs, false, false);
	
	
	// GDI+ throws an error if we try to read a property when the image
	// doesn't have that property. Check to make sure the thumbnail property
	// item exists.
	bool propertyFound = false;
	for (int i=0; i<img.PropertyIdList.Length; i++)
		if (img.PropertyIdList[i] == THUMBNAIL_DATA)
		{
			propertyFound = true;
			break;
		}
	
	if (!propertyFound)
		return null;

	PropertyItem p = img.GetPropertyItem (THUMBNAIL_DATA);
	fs.Close();
	img.Dispose();

	
	// 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 = p.Value;
	MemoryStream stream = new MemoryStream (imageBytes.Length);
	stream.Write (imageBytes, 0, imageBytes.Length);

	return Image.FromStream(stream);
}
```




If you want support for .NET 1.0 also, you can try this code below (or download the source file, right click and save target ExifThumbReader.cs):


```
// Please do not remove :)
// Written by Kourosh Derakshan
// 
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

/// <summary>
/// Allows reading of embedded thumbnail image from the EXIF information in an image.
/// </summary>
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 const int THUMBNAIL_DATA = 0x501B;

	/// <summary>
	/// Reads the thumbnail in the given image. If no thumbnail is found, returns null
	/// </summary>
	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);
		}
	}

	/// <summary>
	/// Generates an exception depending on the GDI+ error codes (I removed some error
	/// codes)
	/// </summary>
	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);
	}



	/// <summary>
	/// Converts the IntPtr buffer to a property item and then converts its 
	/// value to a Drawing.Image item
	/// </summary>
	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);
	}

	/// <summary>
	/// 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.)
	/// </summary>
	[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;
			}
		}
	}
}
```

----------


## MrPolite

I uploaded the second solution as a csharp file  :Smilie:

----------


## nant98

Sorry OOT...

i have a question...

i build an application using VB.NET. in that application there's a method that load an image file from network (fully shared folder). Today i found out that i got an Out of Memory error message when trying to load the image to some image object in VB.NET. The image file (in TIF format) was generated by scanning some document and saved it in TIF format, i dont know what software they were using. If i tried to preview the image file with windows thumbnail it only shows ACDSee icon. But when i cropped that image file using image editor from ACDSee, without resizing the image resolution, the error message didnt occured.

Does anyone know what is the cause of that? and how to resolve it?
Is there any connection with windows hotfix updates ??? 
i use windows XP and VS.NET 2003 framework v1.1

TIA

----------


## MrPolite

hmm this doesn't directly relate to this post you know :Big Grin: 
but I'd say possibly the software you used didn't write somethign correctly to the image? (Or possibly used a TIFF convention that isn't used normally so windows and .NET wouldnt recognize it but ACDSee would?)

----------


## saimatkong

i got ur function but how to display the thumbnail out ?
thanks

----------

