I am trying to get the IDispatch * of an open explorer window using IShellWindows::FindWindowSW; however, I cannot seem to coax the method to return anything other than S_FALSE.
The code I am using is basically:
OleInitialize(nullptr);
CComPtr<IShellWindows> spWindows;
auto hr = spWindows.CoCreateInstance(CLSID_ShellWindows);
auto pidl = ILCreateFromPath(L"C:\\temp");
VARIANT vtLoc;
vtLoc.vt = VT_VARIANT | VT_BYREF;
vtLoc.pbVal = (BYTE *) pidl;
CComVariant vtEmpty;
long lhwnd;
CComPtr<IDispatch> spdisp;
hr = spWindows->FindWindowSW(&vtLoc, &vtEmpty,
SWC_EXPLORER, &lhwnd, SWFO_NEEDDISPATCH | SWFO_INCLUDEPENDING,
&spdisp);
Yes, I am sure there is an explorer window open with the location "C:\temp".
Slightly modifying the code from A big little program: Monitoring Internet Explorer and Explorer windows, part 1: Enumeration which enumerates over all registered windows and examines their locations (which is what I assume FindWindowSW does internally anyway) replicates the function. Which is basically what the answer by Victoria does.
bool ImageViewerMainWindow::GetFolderViewFromPath(const WCHAR * szPath, IFolderView2 ** ppfv) {
if( !m_spWindows ) return false;
if( !szPath ) return false;
if( !ppfv ) return false;
*ppfv = nullptr;
CComPtr<IUnknown> spunkEnum;
HRESULT hr = m_spWindows->_NewEnum(&spunkEnum);
if( S_OK != hr ) return false;
CComQIPtr<IEnumVARIANT> spev(spunkEnum);
for( CComVariant svar; spev->Next(1, &svar, nullptr) == S_OK; svar.Clear() ) {
if( svar.vt != VT_DISPATCH ) continue;
CComPtr<IShellBrowser> spsb;
hr = IUnknown_QueryService(svar.pdispVal, SID_STopLevelBrowser, IID_PPV_ARGS(&spsb));
if( S_OK != hr ) continue;
CComPtr<IShellView> spsv;
hr = spsb->QueryActiveShellView(&spsv);
if( S_OK != hr ) continue;
CComQIPtr<IPersistIDList> sppidl(spsv);
if( !sppidl ) continue;
CComHeapPtr<ITEMIDLIST_ABSOLUTE> spidl;
hr = sppidl->GetIDList(&spidl);
if( S_OK != hr ) continue;
CComPtr<IShellItem> spsi;
hr = SHCreateItemFromIDList(spidl, IID_PPV_ARGS(&spsi));
if( S_OK != hr ) continue;
CComHeapPtr<WCHAR> pszLocation;
hr = spsi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszLocation);
if( S_OK != hr ) continue;
if( wcscmp(pszLocation, szPath) != 0 ) continue;
hr = spsv->QueryInterface(IID_PPV_ARGS(ppfv));
if( hr != S_OK ) continue;
return true;
}
return false;
}
But has anyone successfully used FindWindowSW to obtain an IDispatch * to an explorer window registered with IShellWindows?
I think MSDN is wrong, you cannot just assign the PIDL to the VARIANT because IShellWindows is out of process and the PIDL will not be marshaled correctly.
The correct way to do this is to get the size with ILGetSize
and then call SafeArrayCreateVector
to create a VT_UI1
SAFEARRAY and memcpy
the PIDL data into the array. Set the VARIANT type to VT_ARRAY | VT_UI1
and parray
to the SAFEARRAY you created. I believe the InitVariantFromBuffer
helper function will do most of the work for you (Vista+).
ULONG cb = ILGetSize(pidl);
SAFEARRAY *psa = SafeArrayCreateVector(VT_UI1, 0, cb);
if (!psa) return;
memcpy(psa->pvData, pidl, cb);
V_VT(&vtLoc) = VT_ARRAY | VT_UI1, V_UNION(&vtLoc, parray) = psa;
hr = pSW->FindWindowSW(&vtLoc, &vtEmpty, SWC_EXPLORER, &hWnd, SWFO_NEEDDISPATCH | SWFO_INCLUDEPENDING, &pDisp);
printf("%#x %p %d\n", hr, pDisp, hWnd);
It seemed to work correctly when I did this but I would still prefer to use the enumeration method so you can call IShellFolder::CompareIDs
instead of ILIsEqual*
called by FindWindowSW
. This assumes you don't care about the SWC_*
value.
If you still want to follow the docs and use VT_VARIANT | VT_BYREF
then you have to add a pointless indirection where one VARIANT points to another VARIANT and this VARIANT is the SAFEARRAY...
来源:https://stackoverflow.com/questions/49643257/ishellwindowsfindwindowsw-returning-s-false