A customer asked, “What is the correct way to retrieve the icon associated with a file extension? We are writing a shell namespace extension that holds virtual file content, and we want to show the icon that would have been shown if the file were a physical file on disk rather than a virtual one. We tried using SHGetFileInfo
, expecting it to return the icon location and index, but the szDisplayName
comes out as a blank string. (See sample program attached.) What’s the right way to get the location so we can return it in our own GetUIObjectOf(IExtractIcon)
handler?”
#include <windows.h> #include <iostream> int main() { SHFILEINFOW info; ::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); ::SHGetFileInfoW(L".txt", FILE_ATTRIBUTE_NORMAL, &info, sizeof(info), SHGFI_ICONLOCATION | SHGFI_USEFILEATTRIBUTES); std::wcout << info.szDisplayName << std::endl; std::wcout << info.iIcon << std::endl; return 0; }
The location is coming out blank because the file location returned is GIL_NOTFILENAME
so there is no file name to return.
But let’s look past the question to the problem. The problem is that you want to implement IShellFolder::GetUIObjectOf(IExtractIcon)
for your shell namespace extension. Your plan is to create a custom implementation of IExtractIcon
and tell it to report the information you obtained from SHGetFileInfo
. The catch is that this information is lossy because IExtractIcon::GetIconLocation
returns additional information that is not captured by SHGetFileInfo
.
Avoid the loss of fidelity by removing the middle man. Just ask for the standard icon extractor and return that.
We start with a helper function that takes its inspiration from GetUIObjectOfFile
but applies a little seasoning from CreateSimplePidl
:
HRESULT GetUIObjectOfVirtualFile(HWND hwnd, LPCWSTR pszPath, REFIID riid, void **ppv) { *ppv = nullptr; WIN32_FIND_DATAW fd = {}; fd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; CComHeapPtr<ITEMIDLIST_ABSOLUTE> spidlSimple; HRESULT hr = CreateSimplePidl(&fd, pszPath, &spidlSimple); if (FAILED(hr)) return hr; CComPtr<IShellFolder> spsf; PCUITEMID_CHILD pidlChild; hr = SHBindToParent(spidlSimple, IID_PPV_ARGS(&spsf), &pidlChild); if (FAILED(hr)) return hr; return spsf->GetUIObjectOf(hwnd, 1, &pidlChild, riid, NULL, ppv); }
This helper function is like GetUIObjectOfFile
except that it uses a simple pidl to get the UI object for a file that doesn’t actually exist.
We can use this function to get the icon extractor for an arbitrary file extension.
HRESULT GetIconExtractorForExtension( HWND hwnd, PCWSTR pszExtension, REFIID riid, void **ppv) { *ppv = nullptr; wchar_t szPath[MAX_PATH]; HRESULT hr = StringCchPrintfW(szPath, ARRAYSIZE(szPath), L"C:\\a%ls", pszExtension); if (FAILED(hr)) return hr; return GetUIObjectOfVirtualFile(hwnd, szPath, riid, ppv); }
and then use this function when handling the request for IExtractIcon
.
if (interfaceId == IID_IExtractIconW || interfaceId == IID_IExtractIconA) { return GetIconExtractorForExtension(hwnd, L".txt", riid, ppv); }
0 comments