【问题标题】:Bring calculator window to front in Windows 10在 Windows 10 中将计算器窗口置于最前面
【发布时间】:2023-12-16 01:26:01
【问题描述】:

我想问你一个关于在 Windows 10 中将计算器窗口置于前面的问题。我已经测试了很多代码,但没有一个真正有效。我认为主要问题是,计算器是“ApplicationFrameHost”的一部分。 在我的应用程序(C# WinForm)中,如果系统计算器没有运行,我想启动它。如果它正在运行,即使它是否已最小化,也要将窗口置于前面。

public static class WindowHelper
{
    [DllImport("user32.dll")]
    private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    private const int ALT = 0xA4;
    private const int EXTENDEDKEY = 0x01;
    private const int KEYUP = 0x02;
    private const int SW_MINIMIZE = 0x06;
    private const int SW_RESTORE = 0x09;

    public static void BringProcessToFront(IntPtr mainWindowHandle)
    {
        // check if window has focus already
        //if (mainWindowHandle == GetForegroundWindow()) return;

        ShowWindow(mainWindowHandle, SW_RESTORE);

        // simulate ALT key down
        keybd_event((byte)ALT, 0x45, EXTENDEDKEY | 0, 0);
        // simulate ALT key up
        keybd_event((byte)ALT, 0x45, EXTENDEDKEY | KEYUP, 0);
        // bring window into foreground
        SetForegroundWindow(mainWindowHandle);
    }
}

private void btnCalc_Click(object sender, EventArgs e)
{
    // get all processes
    System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcesses();
    // get ApplicationFrameHost for win10
    System.Diagnostics.Process[] appFH = System.Diagnostics.Process.GetProcessesByName("ApplicationFrameHost");
    IntPtr mWHandle = IntPtr.Zero;


    foreach (System.Diagnostics.Process proc in processes)
    {
        if (proc.ProcessName == "calc" || proc.ProcessName == "Calculator" || proc.ProcessName == "win32calc")
        {
            // non-ApplicationFrameHost case
            mWHandle = proc.MainWindowHandle;

            if (appFH.Length > 0)
            {
                // if ApplicationFrameHost is running, find calculator MainWindowHandle
                foreach (System.Diagnostics.Process app in appFH)
                {
                    if ((app.MainWindowTitle == proc.MainWindowTitle) || (proc.MainWindowTitle.Length == 0))
                        mWHandle = app.MainWindowHandle;
                }
            }
            // bring window to front
            WindowHelper.BringProcessToFront(mWHandle);
            return;
        }
    }
    // calculator was not found, starts new one
    System.Diagnostics.Process.Start("calc");
}

此代码也适用于 Windows 7,但不适用于没有英语本地化的 10。有个问题,计算器最小化后无法恢复。

正如我所说,我尝试了很多代码,例如:

public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);

// this was working to bring focus on already displayed window
WindowHelper.FindWindowEx(app.MainWindowHandle, IntPtr.Zero, "Windows.UI.Core.CoreWindow", null);

主要问题是,我没有找到解决方案,这可能会使非英语本地化窗口上的窗口最小化。

你有一些例子,如何处理? 谢谢。

编辑: 经过一些测试,我已经确定,为什么不能把这个窗口放在前面。带有 en 的 Win7 和 Win10(可能与语言环境无关)即使已最小化或未最小化,仍然激活计算器。另一方面,第二个 Win10 在最小化时会暂停进程(参见图片附件)。 所以现在的问题是,如何取消挂起它。然后希望窗口可以被带到前面。

【问题讨论】:

  • 该帖子强调它在“非英语本地化 Windows”上存在问题 - 您能否澄清它是否适用于“en-US Windows”?
  • 是的,这就是我发现的。我的朋友有“斯洛伐克本地化”并报告说,那是行不通的。所以我设置了相同的设置,他有什么(在虚拟机中)。它也不适用于我。使用 EN 本地化,没有问题。
  • 在这种情况下,在 VM 上进行调试将是您尽快获得答案的最佳选择(如果您不想在其上安装任何大的东西,远程调试甚至可以打印每一步)。 .. 在您发布的代码中没有明显的字符串可以本地化 - 因此有人知道答案的可能性相对较低。

标签: c# windows-10 calculator


【解决方案1】:

如果您不是前台窗口,则无法强制使用前台窗口。实际上有很多条件决定何时可以强制前景窗口。来自the docs:(下一条适用于你)

系统限制哪些进程可以设置前台窗口。 只有满足以下条件之一时,进程才能设置前台窗口 条件为真:

  • 该进程是前台进程。
  • 进程由前台进程启动。
  • 进程收到最后一个输入事件。
  • 没有前台进程。
  • 正在调试进程。
  • 前台进程不是现代应用程序或开始屏幕。前景未锁定(参见 LockSetForegroundWindow)
  • 前台锁定超时已过期(请参阅 SystemParametersInfo 中的 SPI_GETFOREGROUNDLOCKTIMEOUT)。
  • 没有处于活动状态的菜单。
  • 当用户处于 使用另一个窗口。相反,Windows 会闪烁 窗口通知用户。

【讨论】:

  • 我期待,当我点击按钮时,我的应用程序将焦点转移到另一个窗口并失去了自己的。正如我已经说过的,在 Win7 上它可以按预期工作。不知道为什么win10有问题。
【解决方案2】:

一段时间后(被其他项目占用了一点点),我建立了对我有用的解决方案。

public static class WindowHelper
{
    public static Dictionary<IntPtr, String> appWins;

    public static bool ThreadWindows(IntPtr handle, IntPtr param)
    {
        int size = WindowHelper.GetWindowTextLength(handle);
        if (size > 0)
        {
            StringBuilder strbTitle = new StringBuilder(size + 1);
            WindowHelper.GetWindowText(handle, strbTitle, strbTitle.Capacity);
            if (strbTitle.Length > 0)
            {
                appWins.Add(handle, strbTitle.ToString());
                return true;
            }
        }
        return false;
    }

    public static void BringProcessToFront(IntPtr mainWindowHandle)
    {
        // check if window has focus already
        //if (mainWindowHandle == GetForegroundWindow()) return;

        ShowWindow(mainWindowHandle, SW_RESTORE);

        // simulate ALT key down
        keybd_event((byte)ALT, 0x45, EXTENDEDKEY | 0, 0);
        // simulate ALT key up
        keybd_event((byte)ALT, 0x45, EXTENDEDKEY | KEYUP, 0);
        // bring window into foreground
        SetForegroundWindow(mainWindowHandle);
    }

    //[DllImport("user32.dll")]
    //private static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern int GetWindowTextLength(IntPtr hWnd);
    [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    public delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
    [DllImport("user32.dll")]
    public static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
    [DllImport("User32.dll")]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
    [DllImport("user32.dll")]
    private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hWnd);
    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    private const int ALT = 0xA4;
    private const int EXTENDEDKEY = 0x01;
    private const int KEYUP = 0x02;
    private const int SW_RESTORE = 0x09;
}

private void btnCalc_Click(object sender, EventArgs e)
{
    // get all processes
    System.Diagnostics.Process[] processes = System.Diagnostics.Process.GetProcesses();
    // get ApplicationFrameHost for win10
    System.Diagnostics.Process[] appFH = System.Diagnostics.Process.GetProcessesByName("ApplicationFrameHost");
    IntPtr mWHandle = IntPtr.Zero;

    foreach (System.Diagnostics.Process proc in processes)
    {
        if (proc.ProcessName == "calc" || proc.ProcessName == "Calculator" || proc.ProcessName == "win32calc")
        {
            // save first handle
            mWHandle = proc.MainWindowHandle;

            // if ApplicationFrameHost is running, find calculator MainWindowHandle
            foreach (System.Diagnostics.Process app in appFH)
            {
                // calculator is already running
                if (mWHandle == (IntPtr)0x00)
                {
                    mWHandle = WindowHelper.FindWindowEx(app.MainWindowHandle, IntPtr.Zero, "Windows.UI.Core.CoreWindow", null);
                } else
                {
                    // create new windows dictionary
                    WindowHelper.appWins = new Dictionary<IntPtr, String>();

                    // enumerate all windows in all AFH threads
                    foreach (System.Diagnostics.ProcessThread thread in app.Threads)
                        WindowHelper.EnumThreadWindows(thread.Id, new WindowHelper.EnumThreadDelegate(WindowHelper.ThreadWindows), IntPtr.Zero);

                    // check if proc window was found
                    if (WindowHelper.appWins.ContainsValue(proc.MainWindowTitle))
                    {
                        IntPtr hwnd;
                        // get key from value
                        if ((hwnd = WindowHelper.appWins.First(x => x.Value == proc.MainWindowTitle).Key) != (IntPtr)0)
                            mWHandle = hwnd;
                    }

                    // clear list
                    WindowHelper.appWins.Clear();
                }
            }
            if (mWHandle != (IntPtr)(0x00))
            {
                // bring already running calc to front
                WindowHelper.BringProcessToFront(mWHandle);
                return;
            }
            // do not search for other processes
            break;
        }
    }
    // start new calc instance
    System.Diagnostics.Process.Start("calc");
}

此解决方案与语言环境无关。 感谢所有为我指明正确方向的人。

【讨论】: