为什么无法获取已启动进程的主窗口句柄?

发布于 2024-10-13 06:19:18 字数 1616 浏览 3 评论 0原文

我遇到过这样的情况:我正在代码中启动一个进程以设置 IPC 通道。我正在启动的进程是一个没有 CLR 支持的 MFC 应用程序。我从中启动此过程的应用程序是 WPF 应用程序中的 C# 模块(我认为这对我的问题并不重要)。这适用于支持 CLR 的应用程序版本,并且它适用于除部署目标(安装 Windows 7 的触摸屏计算机)之外的每台计算机。但由于某种原因,当我在这个确切的场景中尝试它时,Process 对象永远不会解析主窗口句柄 (Process.MainWindowHandle)。还有另一种(也许甚至是pinvoke)方法可以做到这一点吗?这是安全问题吗?我是那个盯着这个过程的人。该进程的主窗口句柄确实存在。我不明白有什么问题。

如果有帮助,这是我的代码。

        _applicationProcess = new Process();
        _applicationProcess.StartInfo.FileName = _strProcessPath;
        _applicationProcess.StartInfo.Arguments = _strProcessArguments;
        _applicationProcess.Start();

        long nTicks = Environment.TickCount;
        if (_applicationProcess.WaitForInputIdle(1 /*minute(s)*/ * 60000))
        {
            try
            {
                do
                {
                    // Don't let total processing take more than 1 minute(s).
                    if (Environment.TickCount > nTicks + 1 /*minute(s)*/ * 60000)
                        throw new ApplicationException("MFCApplication.Startup failed! The main window handle is zero!");

                    _applicationProcess.Refresh();
                }
                while (_applicationProcess.MainWindowHandle.ToInt32() == 0);

                _applicationHandle = new IntPtr(_applicationProcess.MainWindowHandle.ToInt32());
            }
            catch (Exception ex)
            {
                //Do some stuff...
                throw;
            }
        }
        else
        {
            // Do exception handling.
        }

在尝试获取非零的主窗口句柄一分钟后,发生了 ApplicationException

I have a situation where I'm starting a process in my code in order to set up an IPC channel. The process I'm starting is an MFC application with no CLR support. The application from which I am starting this process is a C# module in a WPF application (thought I don't think that that is consequential to my problem). This works with a version of the application that does support CLR, and it works on every computer except the deployment target, a touch screen computer with Windows 7. But for some reason, when I try it with this exact scenario, the Process object never resolves a main window handle (Process.MainWindowHandle). Is there another (perhaps even pinvoke) method of doing this? Is this a security thing? I'm the one staring the process. The process's main window handle does exist. I don't see what could be wrong.

If it helps, here is my code.

        _applicationProcess = new Process();
        _applicationProcess.StartInfo.FileName = _strProcessPath;
        _applicationProcess.StartInfo.Arguments = _strProcessArguments;
        _applicationProcess.Start();

        long nTicks = Environment.TickCount;
        if (_applicationProcess.WaitForInputIdle(1 /*minute(s)*/ * 60000))
        {
            try
            {
                do
                {
                    // Don't let total processing take more than 1 minute(s).
                    if (Environment.TickCount > nTicks + 1 /*minute(s)*/ * 60000)
                        throw new ApplicationException("MFCApplication.Startup failed! The main window handle is zero!");

                    _applicationProcess.Refresh();
                }
                while (_applicationProcess.MainWindowHandle.ToInt32() == 0);

                _applicationHandle = new IntPtr(_applicationProcess.MainWindowHandle.ToInt32());
            }
            catch (Exception ex)
            {
                //Do some stuff...
                throw;
            }
        }
        else
        {
            // Do exception handling.
        }

The ApplicationException is hit after a minute of trying to get a main window handle other than zero.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

夏至、离别 2024-10-20 06:19:18

不幸的是,您从 Process.MainWindowHandle 获得的值是一个猜测。程序没有可用的 API 函数来告诉 Windows“这是我的主窗口”。它使用的规则已记录,它是进程启动时创建的第一个窗口。如果第一个窗口是登录窗口或启动屏幕,这会引起麻烦。

您对此无能为力,您必须更多地了解程序的行为方式才能找到真实主窗口。只要第一个窗口是在与主窗口相同的线程上创建的,使用 EnumThreadWindows() 枚举窗口就可以帮助您找到它。如果情况并非如此,则需要更详细的 EnumWindows()。

The value you get out of Process.MainWindowHandle is unfortunately a guess. There is no API function available to a program that lets it tell Windows "this is my main window". The rule it uses is documented, it is the first window that's created by a process when it gets started. That causes trouble if that first window is, say, a login window or a splash screen.

Not much you can do about this, you have to know more about how the program behaves to find that real main window. Enumerating windows with EnumThreadWindows() could help you find it, as long as the first window was created on the same thread as the main window. A more elaborate EnumWindows() will be necessary if that is not the case.

高跟鞋的旋律 2024-10-20 06:19:18

我的习惯是循环调用EnumWindows并结合GetWindowThreadProcessId来查找窗口句柄。

C Code, adapt to your language


DWORD TargetHWND;

//...
    while (EnumWindows(EnumWndProc, (LPARAM)(DWORD)pid)) {
        Sleep(100);
    }


//...

BOOL EnumWndProc(HWND hWnd, LPARAM lParam) {
    DWORD pid = (DWORD)-1;
    GetWindowThreadProcessId(hWnd, &pid);
    if (pid == (DWORD)lParam) {
        TargetHWND = hWnd;
        return FALSE;
    }
    return TRUE;
}

My habit is to call EnumWindows in a loop combined with GetWindowThreadProcessId to find the window handle.

C Code, adapt to your language


DWORD TargetHWND;

//...
    while (EnumWindows(EnumWndProc, (LPARAM)(DWORD)pid)) {
        Sleep(100);
    }


//...

BOOL EnumWndProc(HWND hWnd, LPARAM lParam) {
    DWORD pid = (DWORD)-1;
    GetWindowThreadProcessId(hWnd, &pid);
    if (pid == (DWORD)lParam) {
        TargetHWND = hWnd;
        return FALSE;
    }
    return TRUE;
}
通知家属抬走 2024-10-20 06:19:18

为了通过您的进程获取 MainWindowHandle,请确保您的 WPF 应用程序显示在任务栏上,即 ShowInTaskbar="True" 并设置 Application.Current .MainWindow 属性到您想要设置为主窗口的窗口。

如果我在 WPF 主窗口中执行下面的代码而不设置 ShowInTaskbar="True",我的 MainWindowHandle 总是为 0,因为我的 WPF 窗口是全屏的并且未显示在任务栏。

    Application.Current.MainWindow = this;
    var Query = System.Diagnostics.Process.GetProcessesByName("ProcessName");

    if (Query.Any())
    {
        Query.FirstOrDefault().Refresh();
        MessageBox.Show(Query.FirstOrDefault().MainWindowHandle.ToInt32().ToString());
    }

In order to get MainWindowHandle by means of your process, please make sure your WPF application is shown on the taskbar i.e ShowInTaskbar="True" and set Application.Current.MainWindow property to the window that you'd like to set as your main window.

If I execute code below in my WPF main window without setting ShowInTaskbar="True" I always got 0 as the MainWindowHandle because my WPF window was full screen and not displayed on the taskbar.

    Application.Current.MainWindow = this;
    var Query = System.Diagnostics.Process.GetProcessesByName("ProcessName");

    if (Query.Any())
    {
        Query.FirstOrDefault().Refresh();
        MessageBox.Show(Query.FirstOrDefault().MainWindowHandle.ToInt32().ToString());
    }
蓝戈者 2024-10-20 06:19:18

我不知道为什么它可能会有所不同,但在创建进程后,尝试执行以下操作:

Process[] allProcesses = Process.GetProcessesByName("YourWindowTitle");

并查看返回的进程是否有 MainWindowHandle。

I don't know why it could be different, but after you create the process, try doing:

Process[] allProcesses = Process.GetProcessesByName("YourWindowTitle");

and see if any of the processes returned have a MainWindowHandle.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文