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):
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: