WIC File Open Dialog Using IFileOpenDialog Interface

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 (1402 downloads)

WIC File Open Dialog - Demo
WIC File Open Dialog – Demo

 

// 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

Free WIC codecs download

See also

1 thought on “WIC File Open Dialog Using IFileOpenDialog Interface”

  1. 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?

    Reply

Leave a Comment