Подтвердить что ты не робот

Восстановить свернутое окно другого приложения

Я добавляю код в приложение, которое запустит другое приложение, если оно еще не запущено, или если оно есть, принесите его на передний план. Это требует небольшого количества 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 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. Что здесь происходит?

4b9b3361

Ответ 1

... По-видимому, вы не можете доверять информации, которую предоставляет процесс.

Process.MainWindowHandle возвращает дескриптор окна первого окна, созданного приложением, которое является USUALLY главным окном верхнего уровня приложения. Однако в моем случае вызов FindWindow() показывает, что дескриптор фактического окна, который я хочу восстановить, не является тем, на что указывает MainWindowHandle. Похоже, что дескриптор окна из процесса в этом случае относится к экрану заставки, показанному как программа загружает основную форму.

Если я вызываю ShowWindow в дескрипторе, возвращаемом FindWindow, он отлично работает.

Что еще более необычно, когда при открытии окна вызов SetForegroundWindow(), когда задан процесс MainWindowHandle (который должен быть недействительным при закрытии этого окна), работает нормально. Таким образом, очевидно, что дескриптор имеет НЕПОСРЕДСТВЕННУЮ действительность, а не когда окно минимизировано.

В общем, если вы окажетесь в моем затруднительном положении, вызовите FindWindow, передав ему известное имя главного окна внешнего приложения, чтобы получить требуемый дескриптор.

Ответ 2

Рабочий код с использованием метода 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 focus to the window
    SetForegroundWindow(wdwIntPtr);
}

Вы можете использовать его, вызывая BringWindowToFront().

У меня всегда работает один экземпляр приложения, поэтому, если вы можете иметь несколько открытых экземпляров одновременно, вы можете немного изменить логику.

Ответ 3

У меня была та же проблема. Лучшее решение, которое я нашел, - вызвать ShowWindow с флагом SW_MINIMIZE, а затем с помощью SW_RESTORE.: D

Другое возможное решение:

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

Лого вызова ShowWindow (дескриптор, SW_RESTORE); после SetForegroundWindow (дескриптор);

Это может решить вашу проблему.

Ответ 5

Похоже, вы пытаетесь выполнить действие, которое приводит к тому же результату, что и alt-tabbing, который возвращает окно назад, если оно было свернуто, и "запоминает", если оно было развернуто.

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 может быть найдено программно.)