IShellWindows::FindWindowSW返回S_FALSE问题求助
我之前也碰到过这个坑,折腾了好半天才搞明白问题出在哪。咱们一步步来排查和修复你的代码:
核心问题1:VARIANT 参数完全设置错了
你现在给vtLoc设置的VT_VARIANT | VT_BYREF类型完全不对,这根本不是传递文件夹位置的正确方式。其实你完全可以跳过PIDL的复杂处理,直接用字符串路径作为VT_BSTR类型的VARIANT,简单又不容易出错。
核心问题2:FindWindowSW 的参数和标志没传对
要获取资源管理器窗口的IDispatch*,你必须指定SWFO_NEEDDISPATCH标志,同时要把sFolder参数设为SWC_EXPLORER,告诉函数只查找资源管理器窗口,而不是其他Shell窗口。
修正后的完整代码示例
OleInitialize(nullptr); CComPtr<IShellWindows> spWindows; HRESULT hr = spWindows.CoCreateInstance(CLSID_ShellWindows); if (FAILED(hr)) { // 处理初始化失败的情况 OleUninitialize(); return; } // 直接用路径字符串作为查找条件,比PIDL简单太多 CComVariant vtTargetFolder(L"C:\\temp"); CComVariant vtEmpty; long windowHandle = 0; CComPtr<IDispatch> spExplorerDispatch; // 调用FindWindowSW,注意参数顺序和关键标志 hr = spWindows->FindWindowSW( &vtTargetFolder, // 要匹配的文件夹路径 &vtEmpty, // 无需指定特定Shell文件夹类 SWC_EXPLORER, // 只筛选资源管理器窗口 &windowHandle, // 返回找到的窗口句柄(可选,不需要可以传nullptr) SWFO_NEEDDISPATCH, // 必须传这个标志才能获取IDispatch* &spExplorerDispatch // 输出的资源管理器窗口IDispatch ); if (SUCCEEDED(hr) && spExplorerDispatch) { // 可以把IDispatch转换成更实用的接口,比如IWebBrowser2或IShellBrowser CComPtr<IWebBrowser2> spWebBrowser; hr = spExplorerDispatch->QueryInterface(&spWebBrowser); if (SUCCEEDED(hr)) { // 举个例子:获取窗口的URL(也就是文件夹路径) BSTR locationUrl; spWebBrowser->get_LocationURL(&locationUrl); // 这里可以处理获取到的路径,比如弹窗显示 MessageBox(nullptr, locationUrl, L"找到的窗口路径", MB_OK); SysFreeString(locationUrl); // 记得释放BSTR } } else if (hr == S_FALSE) { // S_FALSE表示没找到匹配的窗口,确认一下C:\temp是不是真的在资源管理器里打开着 MessageBox(nullptr, L"没找到打开的C:\\temp资源管理器窗口", L"提示", MB_OK); } // 一定要清理初始化的OLE环境 OleUninitialize();
如果你坚持要用PIDL的话(不推荐,没必要)
如果因为某些原因必须用PIDL作为查找条件,那你需要把PIDL包装成IDataObject,再放到VARIANT里,代码大概是这样:
LPITEMIDLIST pidl = ILCreateFromPath(L"C:\\temp"); if (!pidl) { OleUninitialize(); return; } CComPtr<IDataObject> spDataObj; hr = SHCreateDataObject(pidl, 1, nullptr, nullptr, IID_PPV_ARGS(&spDataObj)); if (SUCCEEDED(hr)) { CComVariant vtLoc; vtLoc.vt = VT_UNKNOWN; vtLoc.punkVal = spDataObj; vtLoc.punkVal->AddRef(); // VARIANT会持有这个引用,所以要手动AddRef // 然后调用FindWindowSW,参数和之前一样 hr = spWindows->FindWindowSW(&vtLoc, &vtEmpty, SWC_EXPLORER, &windowHandle, SWFO_NEEDDISPATCH, &spExplorerDispatch); // 用完VARIANT一定要清理 VariantClear(&vtLoc); } ILFree(pidl); // 必须释放PIDL,不然会内存泄漏
额外注意事项
- 确保你要找的
C:\temp确实是直接打开的资源管理器窗口,如果是通过快捷方式、库或者其他方式打开的,可能路径匹配不上。 - 64位程序只能找64位资源管理器窗口,32位程序只能找32位的,跨位数是获取不到的。
- 调用
OleInitialize后必须对应调用OleUninitialize,不然会有资源泄漏。
内容的提问来源于stack exchange,提问作者cburn11
相关产品推荐
相关产品推荐

