2

I would like to ask you a question about bringing calculator window to front in Windows 10. I have tested a lot of codes, but nothing is really working. I think the main problem is, that Calculator is part of "ApplicationFrameHost". In my application (C# WinForm), I would like to start system Calculator, if it's not running. If it's running bring the window to front even if it's minimized or not.

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");
}

This code is working on Windows 7 as well, but not for 10 with no-english localization. There is a problem, when Calculator is minimized, it can't be restored.

As I said, I have tried lot of codes like:

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);

The main problem is, that I didn't find the solution, which could unminimize window on non-english localized windows.

Do you have some example, how to deal with that? Thank you.

EDIT: After some testing, I have identified, why is not possible to bring this window to front. Win7 and Win10 with en (Maybe it isn't related to locale) has Calculator still activated even if it's minimized or not. On the other hand a second Win10 has process suspended when it's minimized (see picture attachment). enter image description here So now the question is, how to un-suspend it. Then hopefully window could be bring to front.

Andy
  • 74
  • 9
  • The post emphasizes that it has issues "on *non-english localized* Windows" - could you please clarify if it works on "en-US Windows" ? – Alexei Levenkov Jan 08 '20 at 18:54
  • Yes, this is what I catch. My friend has "slovak localization" and reported, that is not working. So I have set-up the same settings, what he has (in virtual machine). It's also not working on my side. With EN localization, there is no problem. – Andy Jan 08 '20 at 19:02
  • In that case debugging on the VM would be your best option to get answer soon (remote debug would do or even just printing on every step if you don't want to install anything big on it)... There are no obvious strings that would be localized in code you've posted - so chances that someone know answer are relatively low. – Alexei Levenkov Jan 08 '20 at 19:33
  • https://stackoverflow.com/a/32513438/17034 – Hans Passant Jan 09 '20 at 07:43

2 Answers2

2

You cannot force the foreground window if you are not the foreground window. There are actually a bunch of conditions that determine when you can force the foreground window. From the docs: (bottom one applies to you)

The system restricts which processes can set the foreground window. A process can set the foreground window only if one of the following conditions is true:

  • The process is the foreground process.
  • The process was started by the foreground process.
  • The process received the last input event.
  • There is no foreground process.
  • The process is being debugged.
  • The foreground process is not a Modern Application or the Start Screen. The foreground is not locked (see LockSetForegroundWindow)
  • The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
  • No menus are active.
  • An application cannot force a window to the foreground while the user is working with another window. Instead, Windows flashes the taskbar button of the window to notify the user.
JP Alioto
  • 44,864
  • 6
  • 88
  • 112
  • I am expecting, that when I click on button, my application give focus to another window and lost own. As I already said, on Win7 it works as expected. I am not sure, why Win10 has a problem. – Andy Jan 08 '20 at 18:52
2

After some time (little bit occupied by another projects) I have founded the solution, which is working for me.

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");
}

This solution is locale independent. Thanks to everyone who pointing me to the right direction.

Andy
  • 74
  • 9