Jan 282010
This code will use the IExtractImage interface in the shell to get an HBITMAP of the shell representation of a thumbnail. This is what shows up in the Explorer as a preview of a file. It works for any file type that has a filter installed for the Explorer. I cobbled this code together based on other articles that I read. I couldn’t find anything callable in C++/MFC so this might be useful to someone else. The application that produces the preview in Explorer will need to be installed on the system where this code is called.
//
// Attempt to get a thumbnail from the Windows shell. This is faster than getting the bitmap bits
// using DirectShow and also gives us the same thing the user sees in the Explorer. It will only
// work if there is an IExtractImage filter registered for the file type.
//
HRESULT CVideoInfo::CreateThumbnail(LPCTSTR szPath, LPCTSTR szFile, DWORD dwWidth,
DWORD dwHeight, HBITMAP *pThumbnail)
{
LPITEMIDLIST pidList = NULL;
DWORD dwPriority = 0;
DWORD dwFlags = IEIFLAG_ASPECT;
HRESULT hr = E_NOINTERFACE;
SIZE size;
WCHAR szBuffer[MAX_PATH];
IExtractImage *peiURL = NULL;
IShellFolder *psfWorkDir = NULL;
IShellFolder *psfDesktop = NULL;
size.cx = dwWidth;
size.cy = dwHeight;
int nTries = 0;
try {
// We need to try more than once in case the video file returns a false error
while(!SUCCEEDED(hr) && nTries++ < NUM_VIDEO_TRIES) {
CoInitialize(NULL);
hr = SHGetDesktopFolder(&Desktop);
if(SUCCEEDED(hr)) {
hr = psfDesktop->ParseDisplayName(NULL, NULL, (LPWSTR)szPath, NULL, &pidList, NULL);
if(SUCCEEDED(hr)) {
hr = psfDesktop->BindToObject(pidList, NULL, IID_IShellFolder, (void **)&psfWorkDir);
if(SUCCEEDED(hr)) {
hr = psfWorkDir->ParseDisplayName(NULL, NULL, (LPWSTR)szFile, NULL, &pidList, NULL);
if(SUCCEEDED(hr)) {
LPCITEMIDLIST pidl = pidList;
hr = psfWorkDir->GetUIObjectOf(NULL, 1, &pidl, IID_IExtractImage, NULL,
(void **)&peiURL);
if(SUCCEEDED(hr)) {
hr = peiURL->GetLocation(szBuffer, MAX_PATH, &dwPriority, &size, 16, &dwFlags);
if(SUCCEEDED(hr) || hr == E_PENDING) {
hr = peiURL->Extract(pThumbnail);
}
}
}
}
}
}
IMallocFree(pidList);
if(peiURL != NULL)
peiURL->Release();
if(psfDesktop != NULL)
psfDesktop->Release();
if(psfWorkDir != NULL)
psfWorkDir->Release();
CoUninitialize();
}
}
catch(...) {
return E_ABORT;
}
return hr;
}
// Attempt to get a thumbnail from the Windows shell. This is faster than getting the bitmap bits
// using DirectShow and also gives us the same thing the user sees in the Explorer. It will only
// work if there is an IExtractImage filter registered for the file type.
//
HRESULT CVideoInfo::CreateThumbnail(LPCTSTR szPath, LPCTSTR szFile, DWORD dwWidth,
DWORD dwHeight, HBITMAP *pThumbnail)
{
LPITEMIDLIST pidList = NULL;
DWORD dwPriority = 0;
DWORD dwFlags = IEIFLAG_ASPECT;
HRESULT hr = E_NOINTERFACE;
SIZE size;
WCHAR szBuffer[MAX_PATH];
IExtractImage *peiURL = NULL;
IShellFolder *psfWorkDir = NULL;
IShellFolder *psfDesktop = NULL;
size.cx = dwWidth;
size.cy = dwHeight;
int nTries = 0;
try {
// We need to try more than once in case the video file returns a false error
while(!SUCCEEDED(hr) && nTries++ < NUM_VIDEO_TRIES) {
CoInitialize(NULL);
hr = SHGetDesktopFolder(&Desktop);
if(SUCCEEDED(hr)) {
hr = psfDesktop->ParseDisplayName(NULL, NULL, (LPWSTR)szPath, NULL, &pidList, NULL);
if(SUCCEEDED(hr)) {
hr = psfDesktop->BindToObject(pidList, NULL, IID_IShellFolder, (void **)&psfWorkDir);
if(SUCCEEDED(hr)) {
hr = psfWorkDir->ParseDisplayName(NULL, NULL, (LPWSTR)szFile, NULL, &pidList, NULL);
if(SUCCEEDED(hr)) {
LPCITEMIDLIST pidl = pidList;
hr = psfWorkDir->GetUIObjectOf(NULL, 1, &pidl, IID_IExtractImage, NULL,
(void **)&peiURL);
if(SUCCEEDED(hr)) {
hr = peiURL->GetLocation(szBuffer, MAX_PATH, &dwPriority, &size, 16, &dwFlags);
if(SUCCEEDED(hr) || hr == E_PENDING) {
hr = peiURL->Extract(pThumbnail);
}
}
}
}
}
}
IMallocFree(pidList);
if(peiURL != NULL)
peiURL->Release();
if(psfDesktop != NULL)
psfDesktop->Release();
if(psfWorkDir != NULL)
psfWorkDir->Release();
CoUninitialize();
}
}
catch(...) {
return E_ABORT;
}
return hr;
}