20

我有一个应用程序,它一次只能打开一个自身实例。为了强制执行此操作,我使用以下代码:

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

它工作正常。我还希望它能够专注于已经运行的副本的形式。也就是说,在返回之前,我想将此应用程序的另一个实例带到前台。

我怎么做?

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

7 回答 7

10

我遇到了同样的问题,SwitchToThisWindow()对我来说效果最好。唯一的限制是您必须安装 XP sp1。我玩过 SetForegroundWindow、ShowWindow,它们都在将窗口拉入视野时遇到了问题。

于 2009-01-14T20:39:45.667 回答
7

C# 相当于 Tom Juergens 的答案。对我来说就像一个魅力。

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);
    }
}
于 2015-02-19T11:13:51.960 回答
5

和 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
于 2011-01-25T10:27:46.200 回答
2

我相信你会想要使用SetForegroundWindow

MSDN 示例

于 2009-01-14T20:03:07.550 回答
2

完成旁注...

您可以使用

Process.GetProcessesByName(me.ProcessName) 

而不是循环遍历系统上运行的所有进程......

更新

此类事情的PInvoke规则...

于 2009-01-14T20:30:24.703 回答
0

您能否获取 Process 对象的 MainWindowHandle 属性并向其发送 WM_USER 消息,您可以将其解释为“其他实例想要将我带到前面”。

于 2009-01-14T20:05:44.957 回答
0

这是桌面应用程序中非常常见的行为,当我创建一个新的 WPF 应用程序时,我经常必须这样做。所以我创建了一个继承自 Application 的 SingletonApp 类:

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

要使用它,您只需在 App.xaml.cs 中从 SingletonApp 而不是 Application 继承:

public partial class App : SingletonApp

不要忘记更新 App.xaml :

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

有了这个,在每个新的桌面客户端中实现这个行为变得非常容易。

于 2020-09-04T10:09:14.770 回答