You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取新启动Firefox窗口的HWND?启动进程已退出

如何获取新启动Firefox窗口的HWND?

在以下示例代码中,我打开一个新的Firefox窗口,等待一段时间让窗口出现,然后想要获取它的HWND。但问题在于,PID为pi.dwProcessId的进程在启动后立即终止,因此无法枚举它的所有窗口。我不想通过枚举CreateProcessW()前后所有Firefox的HWND并对比的方式解决,希望得到更稳健的方案。

#include <exception>
#include <stdio.h>
#include "Windows.h"


static HWND GetFirefoxWindow(DWORD createdFirefoxPID)
{
    wchar_t buffer[64];
    swprintf_s(buffer, L"PID of the created Firefox: %lu.\n", createdFirefoxPID);
    OutputDebugStringW(buffer);

    return NULL;
}


// Signature taken from
// C:\Program Files (x86)\Windows Kits\10\Include\10.0.26100.0\um\WinBase.h
int WINAPI wWinMain(_In_     HINSTANCE hInstance,
                    _In_opt_ HINSTANCE hPrevInstance,
                    _In_     LPWSTR    lpCmdLine,
                    _In_     int       nShowCmd)
{
    STARTUPINFOW si = { sizeof(si) };
    PROCESS_INFORMATION pi = {};

    wchar_t commandLine[2048] =
        LR"("C:\Program Files\Mozilla Firefox\firefox.exe")"
        LR"( -new-window https://example.com)";

    if (!CreateProcessW(NULL, commandLine, NULL, NULL, FALSE,
                        0, NULL, NULL, &si, &pi))
    {
        return 1;
    }

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    Sleep(5000);

    const HWND hwnd = GetFirefoxWindow(pi.dwProcessId);

    return ERROR_SUCCESS;
}

可行的稳健解决方案

方法1:通过Job对象跟踪Firefox关联进程

Firefox的启动进程只是临时启动器,真正的主窗口由后续衍生的主进程创建。可以通过Windows的Job对象跟踪所有关联进程,再从中定位Firefox主窗口:

  1. 创建Job对象并配置允许子进程脱离但保持跟踪;
  2. 以挂起状态创建启动进程,将其分配到Job后恢复运行;
  3. 等待临时启动进程退出,枚举Job中的所有关联进程;
  4. 对每个进程枚举窗口,匹配Firefox主窗口的类名MozillaWindowClass并筛选可见窗口。

修改后的核心代码示例:

#include <exception>
#include <stdio.h>
#include <vector>
#include "Windows.h"

int WINAPI wWinMain(_In_     HINSTANCE hInstance,
                    _In_opt_ HINSTANCE hPrevInstance,
                    _In_     LPWSTR    lpCmdLine,
                    _In_     int       nShowCmd)
{
    STARTUPINFOW si = { sizeof(si) };
    PROCESS_INFORMATION pi = {};

    wchar_t commandLine[2048] =
        LR"("C:\Program Files\Mozilla Firefox\firefox.exe")"
        LR"( -new-window https://example.com)";

    // 创建Job对象
    HANDLE hJob = CreateJobObjectW(NULL, NULL);
    if (hJob == NULL) return 1;

    // 配置Job允许子进程脱离但仍被跟踪
    JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = {0};
    jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
    if (!SetInformationJobObject(hJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)))
    {
        CloseHandle(hJob);
        return 1;
    }

    // 以挂起状态创建进程
    if (!CreateProcessW(NULL, commandLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
    {
        CloseHandle(hJob);
        return 1;
    }

    // 将进程分配到Job
    if (!AssignProcessToJobObject(hJob, pi.hProcess))
    {
        ResumeThread(pi.hThread);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
        CloseHandle(hJob);
        return 1;
    }

    // 恢复进程运行
    ResumeThread(pi.hThread);

    // 等待临时启动进程退出
    WaitForSingleObject(pi.hProcess, INFINITE);

    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);

    // 获取Job中所有进程ID
    DWORD bytesReturned = 0;
    QueryInformationJobObject(hJob, JobObjectBasicProcessIdList, NULL, 0, &bytesReturned);
    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
    {
        CloseHandle(hJob);
        return 1;
    }

    std::vector<BYTE> buffer(bytesReturned);
    PJOBOBJECT_BASIC_PROCESS_ID_LIST pProcessIds = reinterpret_cast<PJOBOBJECT_BASIC_PROCESS_ID_LIST>(buffer.data());
    if (!QueryInformationJobObject(hJob, JobObjectBasicProcessIdList, pProcessIds, bytesReturned, &bytesReturned))
    {
        CloseHandle(hJob);
        return 1;
    }

    Sleep(2000); // 等待主窗口创建完成

    HWND targetHwnd = NULL;
    // 枚举每个进程的窗口,查找Firefox主窗口
    for (DWORD i = 0; i < pProcessIds->NumberOfProcessIds; ++i)
    {
        DWORD currentPid = pProcessIds->ProcessIdList[i];
        EnumWindows([](HWND hwnd, LPARAM lParam) -> BOOL {
            auto data = reinterpret_cast<DWORD*>(lParam);
            DWORD windowPid = 0;
            GetWindowThreadProcessId(hwnd, &windowPid);
            
            if (windowPid == data[0])
            {
                wchar_t className[256];
                GetClassNameW(hwnd, className, sizeof(className)/sizeof(wchar_t));
                // 匹配Firefox主窗口类名且窗口可见
                if (wcscmp(className, L"MozillaWindowClass") == 0 && IsWindowVisible(hwnd))
                {
                    *reinterpret_cast<HWND*>(&data[1]) = hwnd;
                    return FALSE; // 找到后停止枚举
                }
            }
            return TRUE;
        }, reinterpret_cast<LPARAM>(&currentPid));
        
        if (targetHwnd != NULL) break;
    }

    CloseHandle(hJob);

    // 输出找到的HWND
    if (targetHwnd != NULL)
    {
        wchar_t buffer[64];
        swprintf_s(buffer, L"Found Firefox window HWND: %p\n", targetHwnd);
        OutputDebugStringW(buffer);
    }

    return ERROR_SUCCESS;
}

方法2:利用Firefox远程调试接口

启动Firefox时添加--remote-debugging-port=9222参数,通过HTTP请求获取已打开窗口的调试信息,再结合窗口标题匹配到目标HWND:

  1. 修改启动命令:
    wchar_t commandLine[2048] =
        LR"("C:\Program Files\Mozilla Firefox\firefox.exe")"
        LR"( -new-window https://example.com --remote-debugging-port=9222)";
    
  2. 向http://localhost:9222/json/list发送GET请求,解析返回的JSON数据,找到对应新窗口的条目;
  3. 用条目中的title字段调用FindWindowW或EnumWindows匹配到目标HWND。

这种方法无需底层系统调用,但需要处理HTTP请求和JSON解析,适合对系统API不熟悉的场景。

内容的提问来源于stack exchange,提问作者Dr. Gut

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.02 02:47:29