我正在尝试获取所有"文件资源管理器";当前正在运行的实例。得到一个包括所有实例的列表是相当直接的,但我发现自己遇到了一堵砖墙,将该列表过滤为仅"文件资源管理器";实例。
下面的代码检索所有Explorer实例;文件资源管理器";以及";Internet Explorer":
#include <comdef.h>
#include <ExDisp.h>
#include <ShlGuid.h>
#include <Windows.h>
#include <cstdio>
using _com_util::CheckError;
using std::puts;
_COM_SMARTPTR_TYPEDEF(IShellWindows, __uuidof(IShellWindows));
int main()
{
CheckError(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
// Acquire IShellWindows interface
IShellWindowsPtr spShellWindows{};
CheckError(spShellWindows.CreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_LOCAL_SERVER));
// Request iterator
IUnknownPtr spEnum{};
CheckError(spShellWindows->_NewEnum(&spEnum));
IEnumVARIANTPtr spEnumVariant{};
CheckError(spEnum.QueryInterface(__uuidof(spEnumVariant), &spEnumVariant));
// Iterate over shell windows ...
while (true) {
variant_t var{};
// ... one element at a time
HRESULT hr = spEnumVariant->Next(1, &var, nullptr);
CheckError(hr);
// Iterator depleted?
if (hr == S_FALSE) break;
// Did we get the expected `IDispatch` interface?
if (var.vt != VT_DISPATCH) continue;
IDispatchPtr spDisp{};
spDisp.Attach(var.pdispVal, true);
puts("Got suspect; need ID");
// That was easy; now on to some real challenges
}
}
显而易见的尝试
我对这个问题的第一个看法是去掉所有不是的东西;文件资源管理器";。请求IWebBrowser2
接口肯定只会得到来自实际上是web浏览器的对象的肯定响应。在上面的代码中添加以下内容:
_COM_SMARTPTR_TYPEDEF(IWebBrowser2, __uuidof(IWebBrowser2));
// ...
int main()
{
// ...
IWebBrowser2Ptr spWebBrowser{};
hr = spDisp.QueryInterface(__uuidof(spWebBrowser), &spWebBrowser);
if (SUCCEEDED(hr)) puts("Implements IWebBrowser2");
// ...
在做出改变并运行代码之后;Internet Explorer";实例正在运行,生成所需的输出。然而,运行代码时;文件资源管理器";实例正在运行时会产生相同的输出!这是一个惊喜,同时也是一个失望。
更健壮,用处更小
可以被识别为";而不是文件资源管理器";没有成功。让我们尝试只包括可以被标识为"的对象;文件资源管理器";相反这听起来更加明显,但正如我们所了解到的;明显的";以及";而不是";当涉及到Windows外壳程序时,要齐头并进。
我实际上还没有实现这一点,但IShellWindows
接口提供了一个Item
方法,该方法只能返回与特定ShellWindowTypeConstants
匹配的对象(例如SWC_EXPLORER
或SWC_BROWSER
(。或者返回窗口集合中特定索引处的对象。但不是两者都有!
所以,是的,(潜在地(更健壮,但也不那么有用,因为它不能满足我的要求,只要有一个以上的";文件资源管理器";正在运行。真倒霉
附带证据
虽然以上两件事都没有任何进展,但我重新开始,进行了全面的调查,寻找线索。由于";文件资源管理器";浏览Shell命名空间时,该帐户可能有内容。以下概述了该方法,基于Raymond Chen的一篇文章,题为"一个大大小小的程序:监控Internet Explorer和Explorer窗口,第1部分:枚举:">
- 从上面的
IDispatch
接口开始,请求ID为SID_STopLevelBrowser
的服务以获得IShellBrowser
接口 - 调用
IShellBrowser::QueryActiveShellView
得到一个IShellView
接口 - 询问
IShellView
是否实现了Shell-namespace-y,例如IPersistIDList
- 如果是,则得出结论,我们持有对";文件资源管理器";例如
这似乎产生了所需的结果,尽管我不清楚这是否或何时停止工作。抛开这看起来有多复杂不谈,我担心它的可靠性。
问题
什么是推荐的/稳健的/可靠的方式来识别所有";文件资源管理器";IShellWindows
集合中的实例?我会支持基于官方文档的解决方案,尽管我知道这是Windows Shell,几乎没有文档。
这里有一种可能性。。。
#include <comdef.h>
#include <ExDisp.h>
#include <ShlGuid.h>
#include <Windows.h>
#include <cstdio>
#include <atlbase.h>
#include <string>
using _com_util::CheckError;
using std::puts;
using std::string;
_COM_SMARTPTR_TYPEDEF(IShellWindows, __uuidof(IShellWindows));
//nicked from StackOverflow
std::string ProcessIdToName(DWORD_PTR processId)
{
std::string ret;
HANDLE handle = OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
FALSE,
processId /* This is the PID, you can find one from windows task manager */
);
if (handle)
{
DWORD buffSize = 1024;
CHAR buffer[1024];
if (QueryFullProcessImageNameA(handle, 0, buffer, &buffSize))
{
ret = buffer;
}
else
{
printf("Error GetModuleBaseNameA : %lu", GetLastError());
}
CloseHandle(handle);
}
else
{
printf("Error OpenProcess : %lu", GetLastError());
}
return ret;
}
int main()
{
CheckError(::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
// Acquire IShellWindows interface
IShellWindowsPtr spShellWindows{};
CheckError(spShellWindows.CreateInstance(CLSID_ShellWindows, nullptr, CLSCTX_LOCAL_SERVER));
// Request iterator
IUnknownPtr spEnum{};
CheckError(spShellWindows->_NewEnum(&spEnum));
IEnumVARIANTPtr spEnumVariant{};
CheckError(spEnum.QueryInterface(__uuidof(spEnumVariant), &spEnumVariant));
// Iterate over shell windows ...
while (true) {
variant_t var{};
// ... one element at a time
HRESULT hr = spEnumVariant->Next(1, &var, nullptr);
CheckError(hr);
// Iterator depleted?
if (hr == S_FALSE) break;
// Did we get the expected `IDispatch` interface?
if (var.vt != VT_DISPATCH) continue;
IDispatchPtr spDisp{};
spDisp.Attach(var.pdispVal, true);
puts("Got suspect; need ID");
// That was easy; now on to some real challenges
CComPtr<IWebBrowser2> lpWB;
spDisp->QueryInterface(&lpWB);
SHANDLE_PTR hWnd{ 0 };
lpWB->get_HWND(&hWnd);
if (hWnd)
{
DWORD pid = 0;
GetWindowThreadProcessId((HWND)hWnd, &pid);
if (pid != 0)
{
puts("pid");
auto s = ProcessIdToName((DWORD_PTR)pid);
puts(s.c_str());
}
}
}
}