代码之家  ›  专栏  ›  技术社区  ›  subham

将正在后台运行的wpf应用程序带到前台?[副本]

  •  1
  • subham  · 技术社区  · 7 年前

    我正在为一个应用程序添加一些代码,如果它还没有运行,它将启动另一个应用程序,或者如果它已经运行,则将它放到最前面。这需要少量的interop/WinAPI代码,我已经从其他站点获得了这些代码的示例,但似乎无法在Win7中工作。

    如果窗口处于某个可见状态,那么API的SetForegroundWindow方法的工作方式与treat类似(这是主要情况,根据公司策略,如果外部应用程序正在运行,则不应将其最小化)。但是,如果它被最小化(例外但很重要,因为在这种情况下我的应用程序看起来什么都不做),那么这个方法和ShowWindow/ShowWindowAsync实际上都不会从任务栏中恢复窗口;所有的方法都只是高亮显示任务栏按钮。

    下面是代码;大部分都可以正常工作,但是对ShowWindow()的调用(我也尝试过ShowWindowAsync)永远不会实现我想要的,无论我发送的命令是什么:

    [DllImport("user32.dll")]
        private static extern int SetForegroundWindow(IntPtr hWnd);
    
        private const int SW_SHOWNORMAL = 1;
        private const int SW_SHOWMAXIMIZED = 3;
        private const int SW_RESTORE = 9;
    
        [DllImport("user32.dll")]
        private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
    
    ...
    
    //The app is named uniquely enough that it can't be anything else,
    //and is not normally launched except by this one.
    //so this should normally return zero or one instance
    var processes = Process.GetProcessesByName("ExternalApp.exe");
    
            if (processes.Any()) //a copy is already running
            {
                //I can't currently tell the window's state,
                //so I both restore and activate it
                var handle = processes.First().MainWindowHandle;
                ShowWindow(handle, SW_RESTORE); //GRR!!!
                SetForegroundWindow(handle);
                return true;
            }
    
            try
            {
                //If a copy is not running, start one.
                Process.Start(@"C:\Program Files (x86)\ExternalApp\ExternalApp.exe");
                return true;
            }
            catch (Exception)
            {
                //fallback for 32-bit OSes
                Process.Start(@"C:\Program Files\ExternalApp\ExternalApp.exe");
                return true;
            }
    

    我试过SHOWNORMAL(1)、SHOWMAXIMIZED(3)、RESTORE(9)和其他几个调整大小的命令,但似乎都没用。思想?

    编辑: 我发现了一个问题,与其他一些代码,我认为是工作。对GetProcessesByName()的调用找不到进程,因为我要查找的是可执行文件名,而不是进程名。这导致我认为正在运行的代码实际上根本无法执行。我认为这是工作,因为外部应用程序显然也会检测到一个副本已经在运行,并试图激活当前的实例。我从我搜索的进程名中删除了“.exe”,现在代码开始执行;但是这似乎是一种倒退,因为现在调用ShowWindow[Async]时,任务栏按钮甚至没有突出显示。所以,我现在知道无论是我的应用程序,还是我正在调用的外部应用程序,都不能在Win7中以编程方式更改不同实例的窗口状态。这是怎么回事?

    0 回复  |  直到 14 年前
        1
  •  21
  •   Ivan Yurchenko    8 年前

    工作代码使用 FindWindow 方法:

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

    你可以打电话来用 BringWindowToFront() .

        2
  •  13
  •   KeithS    14 年前

    ... 显然,你不能相信一个过程给你的信息。

    如果我在FindWindow返回的句柄上调用ShowWindow,它就可以正常工作。

    更不寻常的是,当窗口打开时,调用SetForegroundWindow(),当给定进程的MainWindowHandle(窗口关闭时该句柄应无效)时,工作正常。很明显,这个句柄有一些有效性,只是当窗口最小化时就没有了。

        3
  •  11
  •   Christian Rondeau    11 年前

    我也有同样的问题。我找到的最好办法就是打电话 ShowWindow 带着国旗 SW_MINIMIZE SW_RESTORE

    另一种可能的解决方案:

    // 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 
    

    来自评论: http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx

        4
  •  4
  •   bops    14 年前

    这也许能解决你的问题。

        5
  •  2
  •   Anthony Spendlove    7 年前

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

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

    我希望这能提供一个更简单的解决方案。

    (一般规则:如果字符串值是序数,即它属于某个对象,而不仅仅是一个值,那么最好通过编程方式获取它。你换东西会省去很多麻烦。在本例中,我假设安装位置可以转换为全局常量,并且可以通过编程方式找到.exe名称。)

        6
  •  1
  •   Ronniee    6 年前

    我知道太晚了,我的工作代码如下,以便以后有人能得到快速帮助:)

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

    实际上,FindWindowByCaption是这里的关键,当应用程序在系统托盘中静默运行时,以及当应用程序最小化时,此方法正确收集窗口句柄。