A previoius article extends CFileDialog MFC class, in order to make a File Open or a File Save dialog that automatically filters files, according to available WIC (Windows Imaging Component) codecs. This article presents how to show a similar File Open dialog using IFileOpenDialog interface. This way, it can be used not only in an aplication that uses MFC, but also in an ATL one, even in a raw Win32 or console application that uses basic ATL stuff.
Let’s see an example.
Using IFileOpenDialog to show a WIC File Open Dialog
HRESULT ShowWICFileOpenDialog(HWND hWndOwner, CStringW& strFile) { // create IFileOpenDialog instance. CComPtr<IFileOpenDialog> pIFileOpenDialog; HRESULT hr = pIFileOpenDialog.CoCreateInstance(CLSID_FileOpenDialog); // get an array of WIC decoders friendly names and extensions COMDLG_FILTERSPEC* pFilterSpecArray = NULL; UINT cbFilterSpecCount = 0; if(SUCCEEDED(hr)) { hr = GetWICFileOpenDialogFilterSpecs(pFilterSpecArray, cbFilterSpecCount); } // set the filter if(SUCCEEDED(hr)) { hr = pIFileOpenDialog->SetFileTypes(cbFilterSpecCount, pFilterSpecArray); } // show the file open dialog, and get the chosen file if(SUCCEEDED(hr)) { hr = pIFileOpenDialog->Show(hWndOwner); } // get the choosen full path and file name if(SUCCEEDED(hr)) { CComPtr<IShellItem> pIShellItem; hr = pIFileOpenDialog->GetResult(&pIShellItem); if(SUCCEEDED(hr)) { LPWSTR pszName = NULL; hr = pIShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName); strFile = pszName; CoTaskMemFree(pszName); } } // do cleanup for(UINT nIndex = 0; nIndex < cbFilterSpecCount; nIndex++) { delete []pFilterSpecArray[nIndex].pszName; delete []pFilterSpecArray[nIndex].pszSpec; } delete []pFilterSpecArray; return hr; }
Enumerating the WIC decoders
HRESULT GetWICFileOpenDialogFilterSpecs(COMDLG_FILTERSPEC*& pFilterSpecArray, UINT& cbFilterSpecCount) { ATLASSERT(NULL == pFilterSpecArray); cbFilterSpecCount = 1; // we'll use the last one for "All WIC files" CStringW strAllSpecs; // create IWICImagingFactory instance CComPtr<IWICImagingFactory> pIWICImagingFactory; HRESULT hr = pIWICImagingFactory.CoCreateInstance(CLSID_WICImagingFactory); // create WIC decoders enumerator CComPtr<IEnumUnknown> pIEnum; if(SUCCEEDED(hr)) { DWORD dwOptions = WICComponentEnumerateDefault; hr = pIWICImagingFactory->CreateComponentEnumerator(WICDecoder, dwOptions, &pIEnum); } if(SUCCEEDED(hr)) { CComPtr<IUnknown> pElement; ULONG cbActual = 0; // count enumerator elements while(S_OK == pIEnum->Next(1, &pElement, &cbActual)) { ++cbFilterSpecCount; pElement = NULL; } // alloc COMDLG_FILTERSPEC array pFilterSpecArray = new COMDLG_FILTERSPEC[cbFilterSpecCount]; // reset enumaration an loop again to fill filter specs array pIEnum->Reset(); COMDLG_FILTERSPEC* pFilterSpec = pFilterSpecArray; while(S_OK == pIEnum->Next(1, &pElement, &cbActual)) { CComQIPtr<IWICBitmapDecoderInfo> pIWICBitmapDecoderInfo = pElement; // get necessary buffer size for friendly name and extensions UINT cbName = 0, cbFileExt = 0; pIWICBitmapDecoderInfo->GetFriendlyName(0, NULL, &cbName); pIWICBitmapDecoderInfo->GetFileExtensions(0, NULL, &cbFileExt); // get decoder friendly name (*pFilterSpec).pszName = new WCHAR[cbName]; pIWICBitmapDecoderInfo->GetFriendlyName(cbName, (WCHAR*)(*pFilterSpec).pszName, &cbName); // get extensions; wee need to replace some characters according to the specs CStringW strSpec; pIWICBitmapDecoderInfo->GetFileExtensions(cbFileExt, CStrBuf(strSpec, cbFileExt), &cbFileExt); strSpec.Replace(L',', L';'); strSpec.Replace(L".", L"*."); size_t size = strSpec.GetLength() + 1; (*pFilterSpec).pszSpec = new WCHAR[size]; wcscpy_s((wchar_t*)(*pFilterSpec).pszSpec, size, strSpec.GetString()); // append to "All WIC files" specs strSpec += L";"; strAllSpecs += strSpec; ++pFilterSpec; pElement = NULL; } // set "All WIC files" specs strAllSpecs.TrimRight(_T(';')); (*pFilterSpec).pszName = new WCHAR[wcslen(L"All WIC files") + 1]; wcscpy_s((wchar_t*)(*pFilterSpec).pszName, wcslen(L"All WIC files") + 1, L"All WIC files"); (*pFilterSpec).pszSpec = new WCHAR[strAllSpecs.GetLength() + 1]; wcscpy_s((wchar_t*)(*pFilterSpec).pszSpec, strAllSpecs.GetLength() + 1, strAllSpecs.GetString()); } return S_OK; }
Demo project
The demo project is a simple Win32 console application that uses the above functions.
Download: WIC_File_Open_Dialog_Win32_Demo.zip (1496 downloads)

// stdafx.h // ... // ATL #include <atlbase.h> #include <atlstr.h> // Shell #include <Shobjidl.h> // WIC #include <wincodec.h> #pragma comment(lib, "Windowscodecs.lib")
// Demo.cpp // ... int main() { // initialize COM HRESULT hr = ::CoInitialize(NULL); if(SUCCEEDED(hr)) { CStringW strFile; hr = ShowWICFileOpenDialog(NULL, strFile); if(SUCCEEDED(hr)) { ::MessageBoxW(NULL, strFile, L"Demo WIC Open File Dialog", MB_OK); } // release COM ::CoUninitialize(); } return hr; }
Notes
- In a similar way, we can make a File Save dialog using IFileSaveDialog interface.
- This article presents just proof of concepts. IFileOpenDialog offers much more methods to customize the dialog according to specific needs. And of course, a more elegant job can be done by writing some C++ wrapper class(es).
- For the future, I intend to integrate WIC File Dialogs in WIC Wrapper Library also presented in this blog.
- The built-in WIC decoders deal with the following image file formats: BMP, GIF, ICO, JPEG, PNG, TIFF and WMPhoto. However there are many other decoders that can befound over the internet. Just to mention two free ones: Microsoft Camera Codec Pack and Adobe DNG Codec for Windows.
Resources
- MSDN: IFileOpenDialog interface
- MSDN: Common Item Dialog
- MSDN: Windows Imaging Component
Thanks for this very useful code! However, I found that in my case hr = pIFileOpenDialog->Show(hWndOwner); often takes very long to respond after you have chosen a file and clicked ok in the dialog. It takes several seconds for this method to return a result.Funny thing is, sometimes it works instantly. It’s independent on what file I chose (codec/format, etc). Do you have any idea what this could be about?