IShellWindows :: FindWindowSW返回S_FALSE

时间:2018-04-04 05:13:07

标签: windows winapi windows-explorer

我正在尝试使用IShellWindows::FindWindowSW获取开放资源管理器窗口的IDispatch *;但是,我似乎无法哄骗方法返回S_FALSE以外的任何内容。

我使用的代码基本上是:

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

是的,我确信有一个名为“C:\ temp”的资源管理器窗口打开。

稍微修改来自A big little program: Monitoring Internet Explorer and Explorer windows, part 1: Enumeration的代码,该代码枚举所有已注册的窗口并检查它们的位置(这是我假设FindWindowSW在内部执行的操作)复制该函数。这基本上是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;
}

但有没有人成功使用FindWindowSW获取IDispatch *到IShellWindows注册的资源管理器窗口?

1 个答案:

答案 0 :(得分:0)

我认为MSDN错了,你不能只将PIDL分配给VARIANT,因为IShellWindows不在进程中,并且PIDL不会被正确封送。

执行此操作的正确方法是使用ILGetSize获取大小,然后调用SafeArrayCreateVector以在阵列中创建VT_UI1 SAFEARRAY和memcpy PIDL数据。将VARIANT类型设置为VT_ARRAY | VT_UI1parray到您创建的SAFEARRAY。我相信InitVariantFromBuffer辅助函数将为您完成大部分工作(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);

当我这样做时似乎工作正常,但我仍然希望使用枚举方法,因此您可以调用IShellFolder::CompareIDs而不是ILIsEqual*调用FindWindowSW。这假设您不关心SWC_*值。

如果您仍想关注文档并使用VT_VARIANT | VT_BYREF,则必须添加无意义的间接,其中一个VARIANT指向另一个VARIANT,此VARIANT是SAFEARRAY ......