简体   繁体   English

恢复另一个应用程序的最小化窗口

[英]Restore a minimized window of another application

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 code, which I've gotten examples for from other sites but can't seem to get to work in Win7.这需要少量的互操作/WinAPI 代码,我已经从其他站点获得了这些示例,但似乎无法在 Win7 中工作。

If the window is in some visible state, then the API's SetForegroundWindow method works like a treat (and this would be the main case, as per company policy if the external app is running it should not be minimized).如果窗口处于某种可见状态,则 API 的 SetForegroundWindow 方法就像一种处理方式(这将是主要情况,根据公司政策,如果外部应用程序正在运行,则不应将其最小化)。 However, if it is minimized (exceptional but important as my app will appear to do nothing in this case), neither this method nor ShowWindow/ShowWindowAsync will actually bring the window back up from the taskbar;但是,如果它被最小化(例外但很重要,因为在这种情况下我的应用程序似乎什么都不做),无论是这个方法还是 ShowWindow/ShowWindowAsync 实际上都不会从任务栏恢复窗口; all of the methods simply highlight the taskbar button.所有方法都只是突出显示任务栏按钮。

Here's the code;这是代码; most of it works just fine, but the call to ShowWindow() (I've also tried ShowWindowAsync) just never does what I want it to no matter what the command I send is:它的大部分工作都很好,但是对 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;
        }

I've tried SHOWNORMAL (1), SHOWMAXIMIZED (3), RESTORE (9), and a couple other sizing commands, but nothing seems to do the trick.我尝试了 SHOWNORMAL (1)、SHOWMAXIMIZED (3)、RESTORE (9) 和其他几个大小调整命令,但似乎没有任何作用。 Thoughts?想法?

EDIT: I found an issue with some of the other code I had thought was working.编辑:我发现我认为可以工作的其他一些代码存在问题。 The call to GetProcessesByName() was not finding the process because I was looking for the executable name, which was not the process name.对 GetProcessesByName() 的调用没有找到进程,因为我正在寻找可执行文件名称,这不是进程名称。 That caused the code I thought was running and failing to actually not execute at all.这导致我认为正在运行的代码实际上根本没有执行。 I thought it was working because the external app will apparently also detect that a copy is already running and try to activate that current instance.我认为它是有效的,因为外部应用程序显然也会检测到一个副本已经在运行并尝试激活该当前实例。 I dropped the ".exe" from the process name I search for and now the code executes;我从我搜索的进程名称中删除了“.exe”,现在代码执行了; however that seems to be a step backwards, as now the taskbar button isn't even highlighted when I call ShowWindow[Async].然而,这似乎是倒退了一步,因为现在当我调用 ShowWindow[Async] 时,任务栏按钮甚至没有突出显示。 So, I now know that neither my app, nor the external app I'm invoking, can change the window state of a different instance programmatically in Win7.所以,我现在知道我的应用程序和我正在调用的外部应用程序都不能在 Win7 中以编程方式更改不同实例的窗口状态。 What's goin' on here?这是怎么回事?

Working code using FindWindow method:使用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);
}

You can use it by calling BringWindowToFront() .您可以通过调用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.我总是运行一个应用程序实例,因此如果您可以同时打开多个实例,您可能需要稍微更改逻辑。

... 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. Process.MainWindowHandle 返回应用程序创建的第一个窗口的窗口句柄,通常是该应用程序的主顶级窗口。 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.但是,就我而言,对 FindWindow() 的调用表明,我要恢复的实际窗口的句柄不是 MainWindowHandle 所指向的。 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.在这种情况下,来自 Process 的窗口句柄似乎是程序加载主窗体时显示的初始屏幕的句柄。

If I call ShowWindow on the handle that FindWindow returned, it works perfectly.如果我在 FindWindow 返回的句柄上调用 ShowWindow,它可以完美运行。

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.更不寻常的是,当窗口打开时,调用 SetForegroundWindow(),当给定进程的 MainWindowHandle(它应该是无效的,因为该窗口已关闭),工作正常。 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.总之,如果您发现自己陷入困境,请调用 FindWindow,将您的外部应用程序主窗口的已知名称传递给它,以获得您需要的句柄。

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 .我发现的最佳解决方案是使用标志SW_MINIMIZE调用ShowWindow ,然后使用SW_RESTORE :D :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来自以下评论: http : //msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx

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

This might solve your problem.这可能会解决您的问题。

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.听起来您正在尝试执行与 alt-tabbing 具有相同结果的操作,如果窗口最小化,则将窗口带回来,而如果最大化则“记住”。

NativeMethods.cs: 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, ie 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.) (一般规则:如果一个字符串值是序数,即它属于某物而不只是一个值,那么最好以编程方式获取它。更改事物时您会省去很多麻烦。在这种情况下,我假设安装位置可以转换为全局常量,并且可以通过编程方式找到 .exe 名称。)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM