还原另一个应用程序的最小化窗口

本文关键字:最小化 窗口 应用程序 另一个 还原 | 更新日期: 2023-09-27 18:22:04

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

如果窗口处于某种可见状态,那么API的SetForegroundWindow方法就像对待一样工作(根据公司政策,如果外部应用程序正在运行,则不应将其最小化)。然而,如果它被最小化(特殊但重要的是,在这种情况下,我的应用程序似乎什么都不做),则无论是该方法还是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[Anc]时,任务栏按钮甚至没有高亮显示。因此,我现在知道,无论是我的应用程序,还是我正在调用的外部应用程序,都无法在Win7中以编程方式更改不同实例的窗口状态。这是怎么回事?

还原另一个应用程序的最小化窗口

使用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()来使用它。

我总是有一个应用程序实例在运行,所以如果你可以同时有几个打开的实例,你可能想稍微改变一下逻辑。

。。。显然,你不能相信流程提供给你的信息。

Process.MainWindowHandle返回应用程序创建的第一个窗口的窗口句柄,该窗口通常是该应用程序的主要顶层窗口。然而,在我的情况下,对FindWindow()的调用显示,我要恢复的实际窗口的句柄不是MainWindowHandle所指向的。在这种情况下,进程中的窗口句柄似乎是程序加载主窗体时显示的启动屏幕的句柄。

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

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

总之,如果你发现自己陷入了我的困境,请致电FindWindow,将外部应用程序主窗口的已知名称传递给它,以获得所需的句柄。

我也遇到了同样的问题。我找到的最好的解决方案是使用标志SW_MINIMIZE调用ShowWindow,然后使用SW_RESTORED

另一种可能的解决方案:

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

托盘调用ShowWindow(句柄,SW_RESTORE);在SetForegroundWindow(句柄)之后;

这可能会解决你的问题。

听起来你正在尝试执行一个与alt选项卡操作结果相同的操作,该操作会在最小化窗口的同时"记住"窗口是否最大化。

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名称可以以编程方式找到。)

我知道已经太晚了,但我的工作代码如下,以便稍后有人可以获得快速帮助:)

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是这里的关键,当应用程序在系统托盘中静默运行时,以及当应用程序最小化时,此方法都会正确收集窗口句柄。