Как сфокусировать чужое окно?

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

        System.Diagnostics.Process[] myProcesses = System.Diagnostics.Process.GetProcesses();
        System.Diagnostics.Process me = System.Diagnostics.Process.GetCurrentProcess();
        foreach (System.Diagnostics.Process p in myProcesses)
        {
            if (p.ProcessName == me.ProcessName)
                if (p.Id != me.Id)
                {
                    //if already running, abort this copy.
                    return;
                }
        }
        //launch the application.
        //...

Работает нормально. Еще хотелось бы, чтобы можно было сфокусировать форму уже работающей копии. То есть, прежде чем вернуться, я хочу вывести на передний план другой экземпляр этого приложения.

Как мне это сделать?

Re: SetForeGroundWindow:

SetForeGroundWindow работает до определенной степени:

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern bool SetForegroundWindow(IntPtr hWnd); 

    //...
                if (p.Id != me.Id)
                {
                    //if already running, focus it, and then abort this copy.
                    SetForegroundWindow(p.MainWindowHandle);
                    return;
                }
    //...

Это выводит окно на передний план, если оно не свернуто. Потрясающий. Однако, если окно свернуто, оно остается свернутым.

Это нужно отменить сворачивание.

Решение через SwitchToThisWindow (работает!):

    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);

    [STAThread]
    static void Main()
    {
        System.Diagnostics.Process me = System.Diagnostics.Process.GetCurrentProcess();
        System.Diagnostics.Process[] myProcesses = System.Diagnostics.Process.GetProcessesByName(me.ProcessName);
        foreach (System.Diagnostics.Process p in myProcesses)
        {
            if (p.Id != me.Id)
            {
                SwitchToThisWindow(p.MainWindowHandle, true);
                return;
            }
        }
        //now go ahead and start our application ;-)

person Jude Allred    schedule 14.01.2009    source источник
comment
Проверьте, является ли окно IsIconic, если да, вызовите ShowWindow msdn .microsoft.com / en-us / library / ms633527 (VS.85) .aspx msdn.microsoft.com/en-us/library/ms633548 (VS.85) .aspx   -  person cmsjr    schedule 14.01.2009


Ответы (7)


У меня была та же проблема, и SwitchToThisWindow () работал лучше всего для меня. Единственное ограничение - у вас должна быть установлена ​​XP sp1. Я играл с SetForegroundWindow, ShowWindow, и у них обоих были проблемы с отображением окна.

person scottm    schedule 14.01.2009
comment
Однако обратите внимание, что на связанной странице MSDN указано: [Эта функция не предназначена для общего использования. Он может быть изменен или недоступен в последующих версиях Windows.] - person Tom Juergens; 25.01.2011

С # эквивалент ответа Тома Юргенса. Для меня работает как оберег.

    private const  int SW_SHOWNORMAL = 1;

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern bool ShowWindow(IntPtr hwnd, int nCmdShow);


    [DllImport("user32.dll", SetLastError = true)]
    private static extern bool SetForegroundWindow(IntPtr hwnd);

    public void SetForeground()
    {
        Process[] processes = Process.GetProcessesByName("process name");

        foreach (Process p in processes) {
            ShowWindow(p.MainWindowHandle, SW_SHOWNORMAL);
            SetForegroundWindow(p.MainWindowHandle);
        }
    }
person Nigrimmist    schedule 19.02.2015
comment
как мы можем упростить приведенное выше, чтобы просто быстро запустить приложение, которое уже запущено? - person DeerSpotter; 05.02.2016

Как и OP, я обнаружил, что одного SetForegroundWindow недостаточно, когда окно свернуто. Поскольку я не хотел использовать SwitchToThisWindow, я выбрал ShowWindow, а затем SetForegroundWindow.

У меня хорошо работает!

private const SW_SHOWNORMAL = 1

<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
Private Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As integer) As Boolean
End Function

<DllImport("user32.dll", SetLastError:=True)> _
Private Function SetForegroundWindow(ByVal hwnd As IntPtr) As Boolean
End Function

Sub SetForeground()
    Dim processes As Process() = Process.GetProcessesByName("myprocess")

    For Each p as Process in processes
        ShowWindow(p.MainWindowHandle, SW_SHOWNORMAL)
        SetForegroundWindow(p.MainWindowHandle)
    Next
End Sub
person Tom Juergens    schedule 25.01.2011

Я считаю, что вы захотите использовать SetForegroundWindow

Пример MSDN

person cmsjr    schedule 14.01.2009

Полное примечание ...

Ты можешь использовать

Process.GetProcessesByName(me.ProcessName) 

вместо того, чтобы перебирать все процессы, запущенные в системе ...

ОБНОВЛЕНИЕ

PInvoke Правила для такого рода вещей ...

person Jason Punyon    schedule 14.01.2009

Можете ли вы взять свойство MainWindowHandle объекта Process и отправить ему сообщение WM_USER, которое вы можете интерпретировать как «какой-то другой экземпляр хочет вывести меня на передний план».

person plinth    schedule 14.01.2009
comment
Мог бы сделать это, хотя кажется уродливым. Это может быть хорошей альтернативой, если SetForegroundWindow не работает. - person Jude Allred; 14.01.2009

Это очень частое поведение в настольных приложениях, мне регулярно приходится это делать, когда я создаю новое приложение WPF. Итак, я создал класс SingletonApp, который наследуется от Application:

public class SingletonApp : Application
{
    private static readonly System.Threading.Mutex mutex;
    private static readonly string processName;

    [DllImport("user32.dll")]
    private static extern bool ShowWindow(IntPtr hWnd, int flags);

    [DllImport("user32.dll")]
    private static extern bool SetForegroundWindow(IntPtr hwnd);

    static SingletonApp()
    {
        processName = Process.GetCurrentProcess().ProcessName;
        mutex = new System.Threading.Mutex(false, $"Local\\{processName}");
    }

    /// <summary>
    /// A base class for application needing to prevent multiple instances
    /// </summary>
    public SingletonApp()
    {
        if (!mutex.WaitOne(0, false))
        {
            // Give focus to existing instance before shutdown
            BringToFront(processName);

            Current.Shutdown();
        }
    }

    public void BringToFront(string processName)
    {
        Process process = Process.GetProcessesByName(processName).FirstOrDefault();

        if (process != null)
        {
            // In case of window is minimized
            ShowWindow(process.MainWindowHandle, 1);     // 1 = Normal

            SetForegroundWindow(process.MainWindowHandle);
        }
    }
}

Чтобы использовать его, вам просто нужно унаследовать от SingletonApp вместо Application в вашем App.xaml.cs:

public partial class App : SingletonApp

Не забудьте также обновить App.xaml:

<utils:SingletonApp x:Class="MyApp.App"
             [...]
             xmlns:utils="clr-namespace:MyApp.Utils"
             Startup="App_OnStartup">

Благодаря этому становится очень легко реализовать такое поведение в каждом новом настольном клиенте.

person bN_    schedule 04.09.2020