我正在尝试使用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注册的资源管理器窗口?
答案 0 :(得分:0)
我认为MSDN错了,你不能只将PIDL分配给VARIANT,因为IShellWindows不在进程中,并且PIDL不会被正确封送。
执行此操作的正确方法是使用ILGetSize
获取大小,然后调用SafeArrayCreateVector
以在阵列中创建VT_UI1
SAFEARRAY和memcpy
PIDL数据。将VARIANT类型设置为VT_ARRAY | VT_UI1
和parray
到您创建的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 ......