IShellWindows::FindWindowSW returning S_FALSE

梦想与她 提交于 2019-12-08 07:59:30

问题


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?


回答1:


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!