IShellWindows::FindWindowSW 返回 S_FALSE [英] IShellWindows::FindWindowSW returning S_FALSE

查看:47
本文介绍了IShellWindows::FindWindowSW 返回 S_FALSE的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用 IShellWindows::FindWindowSW;但是,我似乎无法哄骗该方法返回 S_FALSE 以外的任何内容.

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

是的,我确定有一个资源管理器窗口打开,位置为C:\temp".

Yes, I am sure there is an explorer window open with the location "C:\temp".

一个大的小程序:监控Internet Explorer 和 Explorer 窗口,第 1 部分:枚举 枚举所有已注册的窗口并检查它们的位置(我认为 FindWindowSW 无论如何在内部都会这样做)复制了该功能.这基本上是维多利亚的回答.

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

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

But has anyone successfully used FindWindowSW to obtain an IDispatch * to an explorer window registered with IShellWindows?

推荐答案

我认为 MSDN 是错误的,您不能仅仅将 PIDL 分配给 VARIANT,因为 IShellWindows 不在进程中并且 PIDL 将不会被正确编组.

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.

正确的做法是使用 ILGetSize 获取大小,然后调用 SafeArrayCreateVector 来创建一个 VT_UI1 SAFEARRAY 和 memcpy 将 PIDL 数据放入数组中.将 VARIANT 类型设置为 VT_ARRAY |VT_UI1parray 到您创建的 SAFEARRAY.我相信 InitVariantFromBuffer 辅助函数将为您完成大部分工作(Vista+).

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

当我这样做时它似乎工作正常,但我仍然更喜欢使用枚举方法,以便您可以调用 IShellFolder::CompareIDs 而不是由 调用的 ILIsEqual*查找窗口软件.这假设您不关心 SWC_* 值.

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.

如果您仍想遵循文档并使用 VT_VARIANT |VT_BYREF 那么你必须添加一个无意义的间接引用,其中一个 VARIANT 指向另一个 VARIANT,而这个 VARIANT 是 SAFEARRAY...

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...

这篇关于IShellWindows::FindWindowSW 返回 S_FALSE的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆