简体   繁体   English

您如何识别“IShellWindows”集合中的“文件资源管理器”实例?

[英]How do you identify "File Explorer" instances in an `IShellWindows` collection?

I'm trying to get a list of all "File Explorer" instances currently running.我正在尝试获取当前正在运行的所有“文件资源管理器”实例的列表。 It's fairly straight forward getting a list that includes all instances, but I find myself running into a brick wall filtering that list to only "File Explorer" instances.获得一个包含所有实例的列表是相当直接的,但我发现自己遇到了一个砖墙过滤,该列表仅包含“文件资源管理器”实例。

The following piece of code retrieves all Explorer instances, meaning both "File Explorer" and "Internet Explorer":以下代码检索所有 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
    }
}

The obvious attempt显而易见的尝试

My first take at the problem was to just get rid of everything that isn't "File Explorer".我对这个问题的第一个看法是摆脱所有不是“文件资源管理器”的东西。 Asking for the IWebBrowser2 interface would certainly only get an affirmative response from objects that actually are web browsers.请求IWebBrowser2接口肯定只会从实际上是 web 浏览器的对象中得到肯定的响应。 Adding the following to the code above:在上面的代码中添加以下内容:

_COM_SMARTPTR_TYPEDEF(IWebBrowser2, __uuidof(IWebBrowser2));

// ...

int main()
{
    // ...

        IWebBrowser2Ptr spWebBrowser{};
        hr = spDisp.QueryInterface(__uuidof(spWebBrowser), &spWebBrowser);
        if (SUCCEEDED(hr)) puts("Implements IWebBrowser2");

    // ...

After making the changes and running the code while an "Internet Explorer" instance is running produces the desired output.在“Internet Explorer”实例运行时进行更改并运行代码后,会生成所需的 output。 However, running the code while a "File Explorer" instance is running produces the same output, That's a surprise and a disappointment.但是,在“文件资源管理器”实例运行时运行代码会产生相同的 output,这既令人惊讶又令人失望。 all at the same time.同时。

More robust, less useful更健壮,更少用处

Exluding objects that can be identified as "not File Explorer" didn't work out.排除可以识别为“非文件资源管理器”的对象没有成功。 Let's try to only include objects that can be identified as "File Explorer" instead.让我们尝试只包含可以识别为“文件资源管理器”的对象。 That sounds even more obvious, but as we've learned, "obvious" and "not" go hand in hand when it comes to the Windows Shell.这听起来更加明显,但正如我们所了解到的,当谈到 Windows Shell 时,“明显”和“不” go 齐头并进。

I haven't actually implemented this, but the IShellWindows interface provides an Item method that can return only objects that match a particular ShellWindowTypeConstants (eg SWC_EXPLORER or SWC_BROWSER ).我实际上并没有实现这一点,但IShellWindows接口提供了一个Item方法,该方法只能返回与特定ShellWindowTypeConstants匹配的对象(例如SWC_EXPLORERSWC_BROWSER )。 Or return an object at a particular index in the window collection.或者在 window 集合中的特定索引处返回 object。 But not BOTH!但不是两者都有!

So, yes, (potentially) more robust, but also less useful as it doesn't meet my requirements as soon as more than one instances of "File Explorer" are running.所以,是的,(可能)更强大,但也不太有用,因为一旦运行多个“文件资源管理器”实例,它就不能满足我的要求。 Bummer.真可惜。

Circumstantial evidence旁证

While neither of the above led anywhere, I started over and went on a full-blown investigation looking for hints.虽然以上都没有导致任何结果,但我重新开始并进行了全面调查以寻找线索。 Since "File Explorer" browses the Shell namespace, there may be something to that account.由于“文件资源管理器”浏览 Shell 命名空间,因此该帐户可能存在某些问题。 The following outlines the approach, based on an article by Raymond Chen titled A big little program: Monitoring Internet Explorer and Explorer windows, part 1: Enumeration :以下概述了该方法,基于 Raymond Chen 的一篇题为“ 一个大的小程序:监控 Internet Explorer 和 Explorer windows,第 1 部分:枚举”的文章:

  1. Starting from the IDispatch interface above, ask for a service with ID SID_STopLevelBrowser to get an IShellBrowser interface.从上面的IDispatch接口开始,请求一个 ID 为SID_STopLevelBrowser的服务来获得一个IShellBrowser接口。
  2. Call IShellBrowser::QueryActiveShellView to get an IShellView interface.调用IShellBrowser::QueryActiveShellView以获取IShellView接口。
  3. Ask the IShellView whether it implements something Shell-namespace-y, eg IPersistIDList .询问IShellView它是否实现了 Shell-namespace-y,例如IPersistIDList
  4. If it does, conclude that we're holding a reference to a "File Explorer" instance.如果是这样,则得出结论,我们持有对“文件资源管理器”实例的引用。

This appears to produce the desired result, though it's not clear to me future-proof this is or when it stops working.这似乎产生了预期的结果,尽管我不清楚这是未来的证明或它何时停止工作。 Leaving aside how overly convoluted this appears, I'm concerned about its reliability.撇开这看起来过于复杂,我担心它的可靠性。

Question问题

What's the recommended/robust/reliable way to identify all "File Explorer" instances in an IShellWindows collection?识别IShellWindows集合中所有“文件资源管理器”实例的推荐/稳健/可靠方法是什么? I will favor solutions based on official documentation, though I understand that this is the Windows Shell and there's next to no documentation at all.我会支持基于官方文档的解决方案,尽管我知道这是 Windows Shell 并且几乎没有文档。

Here's a possibility...这里有一个可能...

#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());
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM