How to host a preview handler in a dialog

637 views Asked by At

I'm attempting to host a file preview handler within a dialog. I've set up an event sink for selection changes in Explorer. When the selection changes, I feed the selected shell item to the dialog, which in turn feeds it to a function that prepares the preview frame.

In general, it successfully loads the correct handler and displays the contents of the file, but for certain file types (namely, Excel and Word files), it runs into various problems like focus-loss or flashing. Here's a demo of the Excel preview handler messing up focus (and by mess up, I mean it wrongfully steals the focus from Explorer, which I'd like to maintain focus):

enter image description here

Word files may load successfully once, but they'll subsequently fail, especially if Word is opened.

As for the code:

For starters, here's my function for obtaining the preview handler from the file extension. This seems to work fine:

HRESULT PreviewHandlerFromExt(LPCWSTR pszExt, IPreviewHandler** ppph)
{
    WCHAR szCLSID[CLSID_LEN] = { 0 };
    DWORD cchOut = CLSID_LEN;
    HRESULT hr = AssocQueryString(  ASSOCF_INIT_DEFAULTTOSTAR,
                                    ASSOCSTR_SHELLEXTENSION,
                                    pszExt,
                                    L"{8895b1c6-b41f-4c1c-a562-0d564250836f}",
                                    szCLSID,
                                    &cchOut );
    if (FAILED(hr))
    {
        return hr;
    }

    CLSID clsid;
    hr = CLSIDFromString(szCLSID, &clsid);
    if (FAILED(hr))
    {
        return hr;
    }

    CComPtr<IUnknown> punk;
    hr = punk.CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER);
    if (FAILED(hr))
    {
        return hr;
    }

    CComPtr<IPreviewHandler> pPrevHandler;
    hr = punk->QueryInterface(&pPrevHandler);
    if (FAILED(hr) || !pPrevHandler)
    {
        return hr;
    }

    return pPrevHandler.CopyTo(ppph);
}

And now here's the function in my dialog that prepares the preview, given a shell item (m_pPreviewHandler is the active preview handler, IDC_PREVIEWFRAME is a placeholder in the dialog for the preview pane, and m_mapExtsToPreviewHandlers is just a map for storing preview handlers as the user comes across them):

void CMyDialog::ShowPreview(IShellItem* pShItem)
{
    HRESULT hr;

    if (m_pPreviewHandler)
    {
        m_pPreviewHandler->Unload();
        m_pPreviewHandler.Release();
    }

    CComHeapPtr<WCHAR> pszPath;
    hr = pShItem->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
    if (FAILED(hr))
    {
        return;
    }

    LPWSTR pszExt = CharLower(PathFindExtension(pszPath));

    auto it = m_mapExtsToPreviewHandlers.find(pszExt);
    if (it == m_mapExtsToPreviewHandlers.end())
    {
        hr = PreviewHandlerFromExt(pszExt, &m_pPreviewHandler);
        if (FAILED(hr) || !m_pPreviewHandler)
        {
            return;
        }

        m_mapExtsToPreviewHandlers[pszExt] = m_pPreviewHandler;
    }

    else
    {
        m_pPreviewHandler = m_mapExtsToPreviewHandlers[pszExt];
    }

    CComPtr<IInitializeWithFile> pInitWithFile;
    hr = m_pPreviewHandler->QueryInterface(&pInitWithFile);
    if (SUCCEEDED(hr))
    {
        hr = pInitWithFile->Initialize(pszPath, STGM_READ);
        if (FAILED(hr))
        {
            return;
        }
    }

    else
    {
        CComPtr<IInitializeWithStream> pInitWithStream;
        hr = m_pPreviewHandler->QueryInterface(&pInitWithStream);
        if (SUCCEEDED(hr))
        {
            CComPtr<IStream> pStream;
            hr = SHCreateStreamOnFile(pszPath, STGM_READ, &pStream);
            if (FAILED(hr) || !pStream)
            {
                return;
            }

            hr = pInitWithStream->Initialize(pStream, STGM_READ);
            if (FAILED(hr))
            {
                return;
            }
        }
    }

    CWindow wndPreviewFrame( GetDlgItem(IDC_PREVIEWFRAME) );
    CRect rectPreviewFrame;
    wndPreviewFrame.GetClientRect(&rectPreviewFrame);

    hr = m_pPreviewHandler->SetWindow(wndPreviewFrame, &rectPreviewFrame);
    if (FAILED(hr))
    {
        return;
    }

    hr = m_pPreviewHandler->DoPreview();
    if (FAILED(hr))
    {
        return;
    }

    hr = m_pPreviewHandler->SetRect(&rectPreviewFrame);
    if (FAILED(hr))
    {
        return;
    }
}

Does anyone know what I'm doing wrong or what might fix these focus problems?

I've also tried placing LockSetForegroundWindow at various places, but no lock.

Also, this is what the dialog resource looks like:

enter image description here

0

There are 0 answers