Restore a minimized window of another application

前端 未结 6 1145
情深已故
情深已故 2020-11-27 17:00

I\'m adding some code to an app that will launch another app if it isn\'t already running, or if it is, bring it to the front. This requires a small amount of interop/WinAPI

相关标签:
6条回答
  • 2020-11-27 17:32

    It sounds like you're trying to perform an action that has the same result as alt-tabbing, which brings the window back if it was minimized while "remembering" if it was maximized.

    NativeMethods.cs:

    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    
    // Specify your namespace here
    namespace <your.namespace>
    {
        static class NativeMethods
        {
            // This is the Interop/WinAPI that will be used
            [DllImport("user32.dll")]
            static extern void SwitchToThisWindow(IntPtr hWnd, bool fUnknown);
        }
    }
    

    Main code:

    // Under normal circumstances, only one process with one window exists
    Process[] processes = Process.GetProcessesByName("ExternalApp.exe");
    if (processes.Length > 0 && processes[0].MainWindowHandle != IntPtr.Zero)
    {
        // Since this simulates alt-tab, it restores minimized windows to their previous state
        SwitchToThisWindow(process.MainWindowHandle, true);
        return true;
    }
    // Multiple things are happening here
    // First, the ProgramFilesX86 variable automatically accounts for 32-bit or 64-bit systems and returns the correct folder
    // Secondly, $-strings are the C# shortcut for string.format() (It automatically calls .ToString() on each variable contained in { })
    // Thirdly, if the process was able to start, the return value is not null
    try { if (Process.Start($"{System.Environment.SpecialFolder.ProgramFilesX86}\\ExternalApp\\ExternalApp.exe") != null) return true; }
    catch
    {
        // Code for handling an exception (probably FileNotFoundException)
        // ...
        return false;
    }
    // Code for when the external app was unable to start without producing an exception
    // ...
    return false;
    

    I hope this provides a much simpler solution.

    (General Rule: If a string value is ordinal, i.e. it belongs to something and isn't just a value, then it is better to get it programmatically. You'll save yourself a lot of trouble when changing things. In this case, I'm assuming that the install location can be converted to a global constant, and the .exe name can be found programmatically.)

    0 讨论(0)
  • 2020-11-27 17:33

    Tray calling ShowWindow(handle, SW_RESTORE); after SetForegroundWindow(handle);

    This might solve your problem.

    0 讨论(0)
  • 2020-11-27 17:46

    ... Apparently you cannot trust the information a Process gives you.

    Process.MainWindowHandle returns the window handle of the first window created by the application, which is USUALLY that app's main top-level window. However, in my case, a call to FindWindow() shows that the handle of the actual window I want to restore is not what MainWindowHandle is pointing to. It appears that the window handle from the Process, in this case, is that of the splash screen shown as the program loads the main form.

    If I call ShowWindow on the handle that FindWindow returned, it works perfectly.

    What's even more unusual is that when the window's open, the call to SetForegroundWindow(), when given the process's MainWindowHandle (which should be invalid as that window has closed), works fine. So obviously that handle has SOME validity, just not when the window's minimized.

    In summary, if you find yourself in my predicament, call FindWindow, passing it the known name of your external app's main window, to get the handle you need.

    0 讨论(0)
  • 2020-11-27 17:49

    I had the same problem. The best solution I have found is to call ShowWindow with the flag SW_MINIMIZE, and then with SW_RESTORE. :D

    Another possible solution:

    // Code to display a window regardless of its current state
    ShowWindow(hWnd, SW_SHOW);  // Make the window visible if it was hidden
    ShowWindow(hWnd, SW_RESTORE);  // Next, restore it if it was minimized
    SetForegroundWindow(hWnd);  // Finally, activate the window 
    

    from comments at: http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx

    0 讨论(0)
  • 2020-11-27 17:52

    Working code using FindWindow method:

    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(string className, string windowTitle);
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags);
    
    [DllImport("user32.dll")]
    private static extern int SetForegroundWindow(IntPtr hwnd);
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool GetWindowPlacement(IntPtr hWnd, ref Windowplacement lpwndpl);
    
    private enum ShowWindowEnum
    {
        Hide = 0,
        ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
        Maximize = 3, ShowNormalNoActivate = 4, Show = 5,
        Minimize = 6, ShowMinNoActivate = 7, ShowNoActivate = 8,
        Restore = 9, ShowDefault = 10, ForceMinimized = 11
    };
    
    private struct Windowplacement
    {
        public int length;
        public int flags;
        public int showCmd;
        public System.Drawing.Point ptMinPosition;
        public System.Drawing.Point ptMaxPosition;
        public System.Drawing.Rectangle rcNormalPosition;
    }
    
    private void BringWindowToFront()
    {
        IntPtr wdwIntPtr = FindWindow(null, "Put_your_window_title_here");
    
        //get the hWnd of the process
        Windowplacement placement = new Windowplacement();
        GetWindowPlacement(wdwIntPtr, ref placement);
    
        // Check if window is minimized
        if (placement.showCmd == 2)
        {
            //the window is hidden so we restore it
            ShowWindow(wdwIntPtr, ShowWindowEnum.Restore);
        }
    
        //set user's focus to the window
        SetForegroundWindow(wdwIntPtr);
    }
    

    You can use it by calling BringWindowToFront().

    I always have one instance of the application running so if you can have several open instances simultaneously you might want to slightly change the logic.

    0 讨论(0)
  • 2020-11-27 17:54

    I know its too late, still my working code is as follows so that someone later can get quick help :)

    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    [DllImport("user32.dll")]
    static extern bool SetForegroundWindow(IntPtr hWnd);
    
    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    
    [DllImport("user32.dll", EntryPoint = "FindWindow")]
    public static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
    
    private static void ActivateApp(string processName)
    {
        Process[] p = Process.GetProcessesByName(processName);
    
        if (p.Length > 0)
        {
            IntPtr handle = FindWindowByCaption(IntPtr.Zero, p[0].ProcessName);
            ShowWindow(handle, 9); // SW_RESTORE = 9,
            SetForegroundWindow(handle);
        }
    } 
    
    ActivateApp(YOUR_APP_NAME);
    

    Actually, FindWindowByCaption is the key here, this method collects the window handle correctly when app is running silently in the system tray and also when app is minimized.

    0 讨论(0)
提交回复
热议问题