IThumbnailProvider Re-Visited
Note for the source downloads, see the bottom of the post.
About 3 months ago I posted about 64-bit versions of Thumbnailers written for Vista and Windows 7 using the new “IThumbnailProvider” class. Writing new functionality in managed code ( C# is what I used ), just made it very easy to get code up and running. Well, with Windows 7, there was a problem. The file was being locked by the sytem, and we couldn’t delete it, programmatically nor manually. In fact, when attempting to do it programmatically, Windows returned that the file had indeed been deleted, yet looking at the folder the file persisted. Almost mocking my attempts to delete it. Then, after some magical period of time ( usually between 10 seconds and 5 minutes ) the file would finally give up, and Windows would wisk the file away to Never Never Land.
This was all very confusing, and after a while I called up Microsoft. I got my case ID and was told that someone would call me or email me within a few days. Later that day I got a call from someone, but after showing them with a remote meeting the problem that was happening they determined they were the wrong group to handle it. This happened yet again, and I was passed along through 4 or 5 more people before I got to someone that I believe worked up in Washington, had a name that was easily pronounceable, and had no noticeable accent to someone that lived on the West Coast of the United States.
I was able to show him the problem, and then sent him the code that reproduced the problem. This back and forth with Microsoft started on 11/19/2009. On 1/5/2010 I finally got a resolution. Don’t do it like that. It seems that with the .NET Framework, that the stream is not released immediately. It is released when the thread dies, which who knows when this will happen. On Vista, the thread was terminated immediately, which is why we didn’t see the problem, but on 7 things changed enough for the problem to show up. For those interested, here is the email:
I am writing to give you an update on the status of your issue regarding why having a managed thumbnail provider for files generated by your application causes Windows Explorer on Windows 7 to open and hold onto a handle to the file prior to closing the file at some point later.
When calling a thumbnail provider, Windows Explorer creates an IStream that wraps the file by calling SHCreateStreamOnFileEx. The returned IStream object opens a handle to the file and only closes the handle when the IStream object is released. The IStream object is then used when calling the thumbnail provider’s IInitializeWithStream::Initialize method. What we found in debugging Explorer when using the managed thumbnail provider is that the reference count on the IStream object was being incremented when calling the Initialize method but never decremented when the call returned.
In discussing this issue with the .NET Framework development team, it appears that the dllhost.exe process that is hosting the managed thumbnail provider it terminating (because Explorer has released its references to the thumbnail provider) before the garbage collector has an opportunity to clean up the runtime callable wrapper (RCW) for the IStream object. Essentially, the problem is a result of different lifetime models in COM (deterministic, reference counted) and managed code (non-deterministic, garbage collected). The IStream object is essentially orphaned when the dllhost.exe process terminates.
Later, the Explorer thread that calls the thumbnail provider releases the remaining references on the IStream object by calling CoUninitialize prior to exiting the thread.
As this problem only occurs on Windows 7, I looked at the implementation of the code that handles calling the thumbnail provider on Windows 7 versus Windows Vista. The main difference is that in Windows Vista, the thread that handles calling the thumbnail provider exits almost immediately after calling the thumbnail provider. While the IStream is orphaned on Windows Vista when the dllhost.exe process terminates, the problem appears to not exist because the orphaned IStream is cleaned up when the thread exits. On Windows 7, the thread that calls the thumbnail provider waits for a period of time to see if there is any other work to be performed. The thread terminates after a period of time if there is no other work to be performed.
That said, our recommendation is that you implement your thumbnail provider using unmanaged code. Please let me know if you have any additional questions.
So, that left me having to re-implement the IThumbnailProvider again. As I haven’t had a whole lot of experience with COM, it was a bit of a challenge. I didn’t find a lot of example of the IThumbnailProvider online, so I thought I’d add my code here ( removing anything that is proprietary to the company I work for of course ) to help anyone else trying to get a Thumbnailer working under COM.
You will find attached in this post the empty project to use as the basis for the Thumbnail Provider. It will compile with 7 errors. Open Main.CPP and go to line 74 and remove the /*. Look for “CHANGE_THIS_LINE” and change “YOUR_EXTENSION_HERE” to “ABC” or whatever your file extension is, and remove the */ at the end of the line.
Go to Common.h. Go to Tools->Create GUID. Select option 2, click “New GUID” and press the “Copy” button. Paste at the bottom of the file, and you should get something like
// {C838F6C0-BC06-45da-911F-9330EE20D4E8}
DEFINE_GUID(<<name>>,
0xc838f6c0, 0xbc06, 0x45da, 0x91, 0x1f, 0x93, 0x30, 0xee, 0x20, 0xd4, 0xe8);
replace <<name>> with CLSID_SampleThumbnailProvider.
copy the first like that was created from the Create GUID tool and do this
#define szCLSID_SampleThumbnailProvider L"_PASTE_GUID_HERE"
You know how have unique Thumbnailer built off IThumbnailProvider. Now you just have to write the code to output the thumbnail.
Make sure you run this code to register your DLLs as necessary:
system32/regsvr32 ThumbnailProviderx64.dll
syswow64/regsvr32 ThumbnailProviderx86.dll
unregistering is like this:
system32/regsvr32 /u ThumbnailProviderx64.dll
syswow64/regsvr32 /u ThumbnailProviderx86.dll
Hope this helps people.
Update 5/6/2010: A gentleman by the name of Dan Clarke found my blog and had some problems with my sample code. After some emails back and forth we were able to determine that he wasn’t seeing the changes to his thumbnail due to the way that Windows will cache generated thumbnails, even invalid ones, and Windows 7 is even more relentless in it’s caching. The regeneration of Thumbnails is not triggered until the file is changed ( or a new file is created ). Simply copying & pasting the existing file will trigger the generation of the thumbnail for the new file, while the old file will remain unchanged.
Update 7/6/2011: I’m glad that a year later people are still finding this useful. Marko had asked Ralfn to link his source, but his post was about 4 months back and may not check back here. The .NET solution is also included, but as one of their engineers told me, this approach is NOT supported, so I don’t suggest you use it.
I have modified the C++ sample to include Paul’s code for the GetThumbnail implementation. This will hopefully help some with having a fully working sample.
File: IThumbnailProvider – C++
File: ThumbnailProvider – C# ( This approach is NOT supported by Microsoft )
56 Comments
I successfully implemented a ThumbnailProvider in C# by setting all references to IStream to null and calling GC.Collect after the image has been gathered from the stream. Works like a charm.
Ralfn,
Have you tried this on Windows 7? According to Microsoft, it “shouldn’t be written managed”. When is it you set your IStream to null?
Could you give us your code please?
Thanks
Thanks Jeremy! After trying numerous samples to try and get this to work, yours was the first one that actually did – and was very easy to use to boot!
One thing to note for people trying to use this, if it works the result is a completely blank icon (instead of the default blank page icon). I think it was you and Dan having a conversation on another page I found, and a slight variation on your suggested “Hello World” implementation of GetThumbnail is this:
*phbmp = NULL; *pdwAlpha = WTSAT_UNKNOWN;
ULONG_PTR token;
GdiplusStartupInput input;
if (Ok == GdiplusStartup(&token, &input, NULL))
{
//gcImage.LogBuffer();
Bitmap * pBitmap = new Bitmap(188, 141);
if( pBitmap )
{
Graphics xGraphics( pBitmap );
Font xFont( L"Arial", 12, FontStyleRegular, UnitPoint );
SolidBrush xBrush( Color( 0, 0, 0) );
xGraphics.DrawString(L"Hello!", 6, &xFont, PointF(0.0f,0.0f), &xBrush );
Color color(255, 0, 0);
pBitmap->GetHBITMAP(color, phbmp);
}
}
GdiplusShutdown(token);
if( *phbmp != NULL )
return NOERROR;
return E_NOTIMPL;
Not forgetting to add #include “Gdiplus.h” and link to “gdiplus.lib”.
Interestingly, Dan works at the same company as myself, and sits a few desks away – yet we discovered this page separately and for different purposes.
Paul, I’m glad you ( and Dan ) found this helpful. It really pleases me that people are still finding this article helpful even a year after I wrote it 🙂
Thanks a lot Jeremy. The post was straightforward and easy to adapt to. Great Writeup. 10/10.
-Cheers
MFC convert
Hi,
Great article!
Using your example I managed to create an opensource SVG Viewer extension. 🙂
As a reference: http://code.google.com/p/svg-explorer-extension/
Thanks!
I’m glad it was of some help 🙂
can we achieve this using the thumbnail handler which comes along when we try to add an ATL Simple Object. I am able to register the dll but i do not know why my DllGetClassObject() is never called.
Sekhar, from my understanding that implementation is now deprecated under windows vista and 7, I could be wrong, but I believe that’s the case.
Thanks for the reply.
Another doubt from me.. Is there anyway in which we can get full path of file selected in explorer. I am just able to retrieve filename from IStream object in Initialize() menthod?
I was trying it from IPersistStream but not much of use.
Thanks
Got the full path of the file using a different method.
Checked with SPY++ and i went down the windows hierarchy and got the folder path. Check the code below and you will get the full folder path
HWND hwnd = ::FindWindowEx(l_pExplorerhwnd, NULL, L”WorkerW”, NULL);
if(hwnd)
{
hwnd = ::FindWindowEx(hwnd, NULL, L”ReBarWindow32″, NULL);
if(hwnd)
{
hwnd = ::FindWindowEx(hwnd, NULL, L”Address Band Root”, NULL);
if(hwnd)
{
hwnd = ::FindWindowEx(hwnd, NULL, L”msctls_progress32″, NULL);
if(hwnd)
{
hwnd = ::FindWindowEx(hwnd, NULL, L”Breadcrumb Parent”, NULL);
if(hwnd)
{
hwnd = ::FindWindowEx(hwnd, NULL, L”ToolbarWindow32″, NULL);
if(hwnd)
::InternalGetWindowText (hwnd, l_szTempName, MAX_PATH);
else
return E_FAIL;
}
else
return E_FAIL;
}
else
return E_FAIL;
}
else
return E_FAIL;
}
else
return E_FAIL;
}
else
return E_FAIL;
}
else
return E_FAIL;
That’s great that you were able to find the full path to the file, but I do wonder why you need it when you’re given the file contents through the stream. Is the location of the file that important at that point?
For my requirement, I need to get full path of the file. Its a zip file and i need to extract one of the files within them.
Also, There is a better way, I got it from a reply to my question in MSDN forums:
http://blogs.msdn.com/b/oldnewthing/archive/2004/07/20/188696.aspx
Hi jeremy,
Does your sample code work on XP and Vista too. Or do we need to do something else for Vista and XP platforms ??
I know it works fine on Vista ( I used this code at work and we still have to support Vista ). I’m pretty sure it works on XP as well (we’re still supporting XP….for now), but I can check tomorrow and get back to you ( I don’t have an XP machine to test on at home ).
Thanks a lot. Please do check at your end too.
I have tried registering it XP. It fails. I am getting a message as “The specified Procedure could not be found”.
I have checked it using dependency walker. There is one specific dll{mpr.dll} which is shown in red color i.e it has errors. I suppose this dll is loaded by Shlwapi.dll.
Error Message:
”
Error: At least one module has an unresolved import due to a missing export function in an implicitly dependent module.
”
missing function is an export function MPR.dll i.e “WNetRestoreConnectionA”
I am using VS2010
Any idea regarding this.
Thinking about it, I seem to remember that XP exposes a different (more limited) interface for thumbnails. I’m not sure I have a sample that supports XP, but I’ll look around.
Ok, I just checked. The code I posted is not supported on XP as it uses a different interface for the thumbnails. You could try using something like this: http://www.walkingtarget.com/?p=20
Hi.
May be something to checl out when using managed code:
I recently asked online about the lockup stuff here http://social.technet.microsoft.com/Forums/sa/W8ITProPreRel/thread/ed3a112e-df02-41b7-aace-09707113ae47 – concerning the locked files.
Allthough Microsoft replied they already recorded this issue for Windows 8 somebody replied or reminded that thumbs.db are only created for shares today (since Vista). So all not beautiful – but I don’t like those “old” thumbs.db files all over using the file server … you can create a group policy or use the registry to disable the thumbs.db
However the thumbscache.db in \appdata\local\Microsoft\Windows\Explorer will still be used.
In effect the Lock problem using managed code could may be disappear – if it is assumed thumbs.db is legacy and should be disabled.
The point is the lock is “experienced” only when thumbs.db’s are in general use – and that only happens with (remote) shares. Except for that thumbs.db is never in use. It’s legacy.
And eventhough one decides to implement ones own thumbsnail provider, the thumbs.db can still be in use and Lock stuff. It’s not ones own thumbs provider problem – but a general problem. Put a video or jpeg there – and it will Lock up anymore – eventhough your own thumbsnail provider was implemented to take care special care of Microsoft implementation or design problem.
Since I have disabled thumbs.db on shares using the registry http://support.microsoft.com/kb/2025703 (“Turn off the caching of thumbnails in hidden thumbs.db files”) – I have not experienced Lock ups.
OK – the caching when using file shares have gone. But I am not really after getting the thumbs cached.
I am after getting a thumb rendered – having thumbnails functionality (I can live without the caching if it Means Explorer is blocked)
BTW The concern of the support article I referenced above matches the reply from Microsoft almost exactly:
http://support.microsoft.com/kb/2025703
The folder rename operation fails because thumbcache.dll still has an open handle to the local thumbs.db file and does not currently implement a mechanism to release the handle to the file in a more dynamic and timely fashion.
Steps to reproduce: Map a drive to a NETWORK SHARE that contains several sub-folders that contains images files or PDFs
🙂 So I would may be still reconsider and keep using managed code. This problem just does not go away making my own implementation running unmanaged code.
As I wrote above. If the user dumps a jpeg or a video back into that folder on the remote share, its locked anyway.
For the local file system this problem does not exist.
*Multiple comments from the same user merged by Jeremy
Computermensch,
The issue the managed thumbnail handler runs into doesn’t have anything ( specifically ) to do with Thumbs.db. The issue is that the Managed Thumbnail handler has a stray stream that Explorer doesn’t release immediately, which can leave the file that needs the thumbnail open & locked, unable to be deleted. I’m sure there are issues with Thumbs.db, but that is NOT what this post of mine was about. This happened on local non-shared folders, and this was on Vista & 7 where the Thumbs.db doesn’t exist ( for non-shared folders ).
Hello Jeremy,
If an extension already provides some thumbnail for a file extension (because a program has been associated with it), is there anyway to override that program’ thumbnail provider to use the new provider?
To override an existing Thumbnail provider you just have to overwrite the registry entry that tells it what class it uses. The class is defined by a GUID, if you download the sample here, you’ll see where it is referenced in code in Common.h. Assuming you register the DLL properly, the new provider should be used and not the old one. Now, if the old one had an installer and “fixes” itself, it could be harder to override it.
I also had one more query. How can I debug this thumbnail provider code?
Debugging is difficult, I ended up using a lot of writing to a log file. I’ve always had a hard time hooking into the windows processes to debug extension dlls.
you need to attach explorer.exe to you project. and then you can debug. Make sure that you attach the correct explorer instance.
Hi Jeremy! That’s amazing stuff really helped it and work. But at that time have a couple questions: I need to display the image in thumbnail (which exists in my .xxx file). This file has his own structure and I needed to pull this one which is Image and convert to Bitmap. Have you any idea or in which direction i need thinking? Thanks, best regards 😉
Andrey,
I’m not sure what direction you want here. In the GetThumbnail call, you’ll have to read the file ( from the stream already loaded ) and find your Image. Then convert that to a BMP to return to the system.
I had a similar requirement. I had a compound file which had one stream which was in fact an image. I ended up dumping the IStream object to a temp file and opened it as a compound file.
Yep, thanks boys! In this case i’ve had similar idea)
Do you have an any suggestion why IInitializeWithFile interface method never gets called?
The Remarks section from MSDN for IInitializeWithFile say the following
“Whenever possible, it is recommended that initialization be done through a stream using IInitializeWithStream. Benefits of this include increased security and stability.”
I’m going to guess that if the WIthStream interface exists it is always going to use that one instead.
Thanks Jeremy for help.
After ~25 attempts I finally find the way. All meaning was at registration stage & Thumbnail which implemented by IInitializationWithFile interface need describe and run like DisableProcessIsolation with 0x001 value. This is a key!
Could you please illustrate this with some example code, if possible. I only found a way to set this in the registry.
Thank you.
Jurgen,
Sorry it took so long to get back to you. There is sample source attached to the post (should be towards the bottom, and let me know if it doesn’t work). As per the registration for the thumbnail and the file extension, that association is only set in the registry.
Thanks to all of you boys, the job is done, and this stuff a very helpful. I wish you all good luck, cheers 😉
Is this code for client-side use, or can it also be used server-side so that a website could serve up thumbnails?
Brent,
This is only desktop solution, not a server side solution to be delivered to client connections. If you’re looking to do this over the web, I’m sure there are plenty of solutions to do so. What technology are you using to try to deliver thumbnails to a client page? I’ve done this more than my fair share of times.
Hi Jeremy,
Thanks for the quick reply!
Given a document stored in a db server and returned to me from a service as a stream, i’d like to send a thumbnail down to the client browser from our web app (iis7, sql server, c#, .net 4, asp.net) dynamically (so webserver-hosted office doc to image).
I see many libraries out that convert images to thumbnails, but not any .net libraries that convert client uploads (Microsoft office files, pdfs, text files, images) to thumbnails dynamically.
I’ve gotten a pdf to convert to a thumbnail using aspose.pdf.dll, but that’s just one file type, and it’s slow because it has to open the entire file before it can convert the 1st page to an image. I was hoping to leverage the IThumbnailProvider to accomplish this task since some office documents store a thumbnail inside of them.
The IThumbnailProvider is also a per file type solution ( meaning a different IThumbnailProvider has to be registered for each file type. This is definitely not the route you want to go though.
For office documents you might be able to do something like this: http://www.add-in-express.com/forum/read.php?FID=5&TID=11370
Hi Jeremy
I wanna know what happens if we want to write a thumbnail preview for zipped package I want to write my own thumbnail preview for a .xyz package that is contained 3 different folders inside it and in one of them there is a picture (a png file). The question is now how should I convert the stream (since here stream is refers to whole zip package not my picture, correct me if I am wrong) to a BMP to return to the system.
You’d have to unpack the stream to extract the file you want…or depending on the zip methodology, you may be able to just find the file in the stream and extract the data from it individually. At my place of employment, the file was a conglomeration of several different elements, with a BMP shoved somewhere towards the end (but not the very end, there was other data around it too), so I had to parse the stream to know where to look for what. Sounds like you’ll have to do something similar.
Hope that helps.
Tanx for your prompt answer,
you answer makes a lot of sense and I think yes that’s exactly what I should do. yet I have one naive question when we are talking about stream what exactly we are talking about? I mean in GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha)I don’t see any input to stream? I only understand that at the end I should set phbmp but how can I have access to the stream in order to parse it?
Hi
I used the sample code provided by you. Its not working. File doesn’t show the thumbnail 🙁
I am using Windows 7 64 bit.
And registred DLL using SysWOW64\regsvr32.exe
Previously I had written same COM dll to show thumbnail but that didn’t worked, so I tried yours but no luck.
I have already spend my 2 weeks to find problem but this fucking windows don’t want me to go ahead 🙁
Hi,
Here’s a complete app with installer based on this example:
https://svgextension.codeplex.com/
Maybe it helps…
Mahesh,
The syswow64 folder is for registering 32 bit applications.
Did you check to see if the registration put your file extension information in the registry? That’s how you can see if your DLL is registered correctly. Then once you’ve verified that, create a NEW file that has your extension and see if the thumbnail works.
Thanks Jeremy.
I tried the 64 bit build with System32\regsvr32.exe;
but still no luck 🙁
I even tested it by creating a test app which will dump the bitmap by calling the GetThumbnail interface of that COM dll. Test app works fine but don’t know why win explorer is not happy with that dll… 🙁
Is is any way to force explorer to generate thumbnails ? (like deleting shell cache. I tried deleting all files from \AppData\Local\Microsoft\Windows\Explorer but all files are locked)
@Jeremy, my mistake 🙂 I had changed the registry folder guid too.
After googling about it I found it is IID of IThumbnailProvider.
Its little cryptic to understand. May be we can replace
".YOUR_EXTN\\ShellEx\\{E357FCCD-A995-4576-B01F-234630154E96}" ...
with
LPOLESTR udd; // get the string from uuid of IThumbnailProvider
HRESULT res = StringFromCLSID(IID_IThumbnailProvider, &udd);
".YOUR_EXTN\\ShellEx\\" udd ...
Now your code sample works awesome 🙂
But I am still struggling with my app.
Its showing thumbnail if I change the file extension altogether in my application.
Are there any possibilities where explorer will not call my thumbnail provider and just use app executable icon as file icon? Any registry entries which will cause the explorer not to call thumbnail provider such as DefaultIcon (https://msdn.microsoft.com/en-us/library/windows/desktop/hh127427(v=vs.85).aspx)
If windows has already cached a bitmap for the file, your thumbnail provider will not be executed, and there’s not much else you can do about that except clear the cache using disk cleanup.
Hi Jeremy, I’m not a computer expert by any stretch, but your viewer is very popular in crafting groups as we could finally view svg thumbnails for our cutting machines (silhouette cameo, etc.); updated to windows 10 yesterday and now the svg thumnails turn black as soon as you open them or rename them, instead of maintaining the white (transparent?) background. No idea why, but there’s been a few “what the heck just happened” moments in our forums. Any ideas? Thanks 🙂
Hi Jeremy,
unfortunately I failed to bring my thumbnail provider for company own OCR alive on a Windows 7 64-bit OS.
Years ago I managed it to do this for the same data type under Windows XP with the older Windows interface IExtractImage.
I have downloaded your unmanaged version of code and compiled it succesfully for 32-bit and 64-bit with new GUID. For that I have added in each function or method a short call notice to log file. I only can see that register / unregister calls by regsrv32.exe.
But there is no log message for each other function or method if I use the explorer now to see an OCR thumbnail, even if I create a new OCR file by copy.
On the other hand the SVG Viewer Extension for Windows Explorer installation of Tibold works for 64-bit.
The registry keys and values of my DLL are comparable to the SVG provider.
Any idea? Thanks
Siegfried
Hi Jeremy,
I`ve just tried your sample on my Windows 10, following each step and building successfully in debug mode. However, I still have no icon showing (it appears a default Windows blank icon).
I wonder if there are some sort of limitations – is this still compatible to the new versions of Windows (since it was built for Windows 7…)? Or maybe it was Visual Studio version (I have VS2013 update 5, sometimes migration isn`t as good as expected – I got 13 warnings, no errors)?
Thanks in advance for your answer!
Best regards.
I’ve just made your sample work on Windows 10. I basically built the project in both platforms – x64 and win32, and registered both dlls generated.
hi! where i can get your prog shell extension for fb2 format of books for windows 10. with installer?
Jeremy, thanks big time for posting your thumbnail handler. I had finished writing mine, and I was getting mixed results: sometimes the thumbnails were fine, other times they were solid black. I was having a frustrating time trying to track it down.
I compared mine against yours, and the ONLY minor difference was in my return value for pdwAlpha. I was returning WTSAT_ARGB, which is what the Microsoft provided “recipe” thumbnail handler returns. I noticed that yours was returning WTSAT_UNKNOWN.
I changed mine to that, and now it works great. You saved me from having to open a support incident with Microsoft. Again, thanks very much!
Cary
ЛФК Р РћРњРђ против 2ДРОТСКУБОК СЕЛЕБРРРўР 5 РўРЈР