Wednesday, May 26, 2010

Experiment Extracting Windows Thumbnails (XP and Windows 2003 only)

Windows thumbnails have existed for eons. But in all that time I never really used the windows explorer thumbnail view. Yes, pictures are nice. But invariably I found myself needing the detail view with its listing of file types, sizes and dates.

Well, this week I came across an old stackoverflow thread which mentioned a C# tool for reading and extracting images from the windows thumbnail cache (ie thumbs.db). At least on older XP and Windows 2003 systems. (Vista and later use a slightly different format.) While there are tools galore in this category, the idea of a small DLL that could be called from CF was appealing. So I decided to give it a quick whirl with CF8, under XP.

After compiling the source with Visual C# Express, I changed my explorer settings so I actually had a thumbnails file to test. Next, I created an instance of the ThumbDB class and initialized it by passing in the path my thumbnails database. Once initialized, I used the GetThumbFiles() method to grab an array of all file names within that database.

<cfset util = createObject(".net", "ThumbDBLib.ThumbDB", "c:/test/ThumbDBLib.dll")>
<cfset util.init( "C:/docs/thumbs.db" )>
<cfset fileNames = util.GetThumbFiles()>
<cfdump var="#fileNames#" label="Top 25 Files in Thumbs.db" top="25">

Next, I selected one of the file names and used the GetThumbData() method to retrieve the image bytes. I was hoping to create a CF image object from the bytes and display it in my browser. But every time I called ImageNew(), ColdFusion kept complaining about my argument type.

<!--- this does NOT work --->
<!---  grabbing an arbitrary file from the listing for testing ...--->
<cfset thumbnailName = fileNames[1] />
<cfset bytes = util.GetThumbData( thumbnailName )>
<cfset img = ImageNew(bytes) />

That is when it hit me. A byte in C# is not the same as byte in Java. Unlike Java, C# has signed and unsigned types. So where C# has two data types, sbyte (-128 to 127) and byte (0 to 255), java has only one, byte (range -128 to 127). So according to the data type conversion matrix, the C# byte array was being converted to a short array.

Thanks to a tip from, I added a new method called GetJavaThumbData(), which converts the C# byte array into one that is compatible with Java. Using the new method, I was then able to display the thumbnail perfectly.

C# Code:
public sbyte[] GetJavaThumbData(string fileName)
        byte[] data = GetThumbData(fileName);
        if (data != null)
        sbyte[] jvData  = Array.ConvertAll(data, delegate(byte b) { return unchecked((sbyte)b); });
        return jvData;
        return null;

ColdFusion Code:
<!---  grabbing an arbitrary file from the listing for testing ...--->
<cfset thumbnailName = fileNames[1] />
<cfset bytes = util.GetJavaThumbData( thumbnailName )>
<cfset img = ImageNew( bytes )>
<cfimage action="writeToBrowser" source="#img#">

It is worth noting the ThumbDB class also has a method named GetThumbnailImage(), which returns a C# image object. You can use its properties to display things like height, width, resolution. The class also has several methods for saving the image to a file. Out of curiosity, I tried several of the overloaded save() methods. But I only had success with the simplest form: I am not sure why. Though with CF's own image functions, they are not really needed anyway.

<cfset img = util.GetThumbnailImage( thumbnailName ) />
    <strong>Thumbnail: #thumbnailName#</strong> <hr />
    Height      = #img.Get_Height()#
    Width       = #img.Get_Width()# 
    PixelFormat = #img.Get_PixelFormat()#
    Resolution  = #img.Get_HorizontalResolution()# /

Supposedly the Windows API Code pack can extract thumbnails on later systems like Windows 7. If anyone has used it, or something similar, on Windows 7, let me know. I would be interested in hearing your experiences with it.

  © Blogger templates The Professional Template by 2008

Header image adapted from atomicjeep