Quelle est la bonne façon de créer une application à instance unique?

en utilisant C# et WPF sous .NET (plutôt que Windows Forms ou console), Quelle est la bonne façon de créer une application qui ne peut être exécutée qu'en une seule instance?

je sais que cela a quelque chose à voir avec quelque chose de mythique appelé un mutex, rarement puis-je trouver quelqu'un qui se donne la peine de s'arrêter et d'expliquer ce que l'un d'eux sont.

le code doit également informer l'instance déjà en cours d'exécution que l'Utilisateur a essayé de lancer un deuxièmement, et peut-être aussi passer tous les arguments de ligne de commande le cas échéant.

562
demandé sur Peter Mortensen 2008-08-21 04:33:59

30 réponses

voici un très bon article concernant la solution Mutex. L'approche décrite par l'article est avantageuse pour deux raisons.

tout d'abord, il ne nécessite pas une dépendance sur le Microsoft.Assemblage VisualBasic. Si mon projet dépendait déjà de cette assemblée, je préconiserais probablement d'utiliser l'approche indiquée dans la réponse acceptée. Mais en l'état, Je n'utilise pas Microsoft.VisualBasic assemblée, et je préfère ne pas en ajouter un dépendance inutile à mon projet.

Deuxièmement, l'article montre comment mettre en avant l'instance existante de l'application lorsque l'utilisateur essaie de démarrer une autre instance. C'est une très belle touche que les autres solutions Mutex décrites ici ne prennent pas en compte.


mise à JOUR

en date du 8/1/2014, l'article que j'ai lié ci-dessus est toujours actif, mais le blog n'a pas été mis à jour depuis un certain temps. Cela me fait craindre qu'elle finisse par disparaître, et avec elle, la solution préconisée. Je reproduit le contenu de l'article ici pour la postérité. Les mots appartiennent uniquement au propriétaire du blog à code Sanity Free .

Aujourd'hui, je voulais reformuler un code qui interdisait mon application à partir de l'exécution de plusieurs instances de lui-même.

J'ai déjà utilisé Système.Diagnostic.Processus pour rechercher un exemple de mon myapp.exe dans la liste des processus. Bien que cela fonctionne, il apporte beaucoup de frais généraux, et je voulais quelque chose de plus propre.

sachant que je pourrais utiliser un mutex pour cela (mais ne l'ayant jamais fait avant) j'ai décidé de couper mon code et de simplifier ma vie.

dans la classe de mon application principale j'ai créé un statique nommé Mutex :

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    ...
}

ayant un mutex nommé nous permet de synchroniser la pile à travers plusieurs fils et processus qui est juste la magie que je cherche pour.

Mutex.WaitOne a une surcharge qui spécifie une durée pour nous attendre. Puisque nous ne voulons pas vraiment synchroniser notre code (plus juste vérifier si elle est actuellement en usage) nous utilisons la surcharge avec deux paramètres: Mutex.WaitOne (Timespan timeout, bool exitContext) . Attendre retourne vrai si il est capable d'entrer, et false si elle ne l'était pas. Dans ce cas, nous ne voulons pas attendre du tout; si notre mutex est utilisé, sauter, et passer à autre chose, donc nous passons dans le temps.0 (attendez 0 millisecondes), et de définir le contextexit à true afin que nous puissions sortir de la contexte de synchronisation avant d'essayer d'acquérir un verrou sur elle. Utiliser ceci, nous emballons notre demande.Exécutez le code à l'intérieur de quelque chose comme ça.:

static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            MessageBox.Show("only one instance at a time");
        }
    }
}

donc, si notre application est en cours d'exécution, WaitOne retournera false, et nous obtiendrons un boîte de message.

au lieu de montrer une boîte de message, j'ai choisi D'utiliser un peu de Win32 pour avertissez mon instance de course que quelqu'un a oublié que c'était déjà exécution (en se plaçant au sommet de toutes les autres fenêtres). De j'ai utilisé PostMessage pour diffuser un message personnalisé à chacun fenêtre (le message personnalisé a été enregistré avec RegisterWindowMessage par mon application en cours d'exécution, ce qui signifie que seule mon application sait il est) puis ma deuxième instance sort. L'exécution de l'instance de l'application recevrait cette notification et la traiterait. Pour ce faire, j'ai l'emportent sur les WndProc dans mon formulaire principal et écouté pour ma personnalisé notification. Quand j'ai reçu cette notification que j'ai défini la forme du La propriété la plus élevée à vrai pour le faire monter au sommet.

voici ce que j'ai fini par dire:

  • programme.cs
static class Program
{
    static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
    [STAThread]
    static void Main() {
        if(mutex.WaitOne(TimeSpan.Zero, true)) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
            mutex.ReleaseMutex();
        } else {
            // send our Win32 message to make the currently running instance
            // jump on top of all the other windows
            NativeMethods.PostMessage(
                (IntPtr)NativeMethods.HWND_BROADCAST,
                NativeMethods.WM_SHOWME,
                IntPtr.Zero,
                IntPtr.Zero);
        }
    }
}
  • NativeMethods.cs
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}
  • Form1.cs (face avant partielle)
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }
    protected override void WndProc(ref Message m)
    {
        if(m.Msg == NativeMethods.WM_SHOWME) {
            ShowMe();
        }
        base.WndProc(ref m);
    }
    private void ShowMe()
    {
        if(WindowState == FormWindowState.Minimized) {
            WindowState = FormWindowState.Normal;
        }
        // get our current "TopMost" value (ours will always be false though)
        bool top = TopMost;
        // make our form jump to the top of everything
        TopMost = true;
        // set it back to whatever it was
        TopMost = top;
    }
}
453
répondu Matt Davis 2014-08-01 17:33:53

vous pouvez utiliser la classe Mutex, mais vous découvrirez bientôt que vous aurez besoin d'implémenter le code pour passer les arguments et ainsi de suite. J'ai appris un truc en programmant dans WinForms quand j'ai lu Le Livre de Chris Sell . Cette astuce utilise une logique qui est déjà disponible pour nous dans le cadre. Je ne sais pas pour vous, mais quand j'apprends des choses que je peux réutiliser dans le cadre, c'est généralement la route que je prends au lieu de réinventer la roue. Sauf bien sûr, il ne fait pas tout ce que je veux.

quand je suis entré dans WPF, j'ai trouvé un moyen d'utiliser le même code, mais dans une application WPF. Cette solution devrait répondre à vos besoins basés sur votre question.

tout d'Abord, nous devons créer notre classe d'application. Dans cette classe, nous allons outrepasser L'événement OnStartup et créer une méthode appelée Activate, qui sera utilisée plus tard.

public class SingleInstanceApplication : System.Windows.Application
{
    protected override void OnStartup(System.Windows.StartupEventArgs e)
    {
        // Call the OnStartup event on our base class
        base.OnStartup(e);

        // Create our MainWindow and show it
        MainWindow window = new MainWindow();
        window.Show();
    }

    public void Activate()
    {
        // Reactivate the main window
        MainWindow.Activate();
    }
}

Deuxièmement, nous aurons besoin de créer un classe qui peut gérer nos instances. Avant de passer à travers cela, nous allons réellement réutiliser un code qui est dans le Microsoft.Assemblage VisualBasic. Comme J'utilise C# dans cet exemple, j'ai dû faire référence à l'Assemblée. Si vous utilisez VB.NET tu n'as rien à faire. La classe que nous allons utiliser est WindowsFormsApplicationBase et hériter de notre gestionnaire d'instance hors de lui et puis tirer parti des propriétés et des événements pour gérer l'instancing simple.

public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
    private SingleInstanceApplication _application;
    private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;

    public SingleInstanceManager()
    {
        IsSingleInstance = true;
    }

    protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
    {
        // First time _application is launched
        _commandLine = eventArgs.CommandLine;
        _application = new SingleInstanceApplication();
        _application.Run();
        return false;
    }

    protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
    {
        // Subsequent launches
        base.OnStartupNextInstance(eventArgs);
        _commandLine = eventArgs.CommandLine;
        _application.Activate();
    }
}

Fondamentalement, nous utilisons les bits VB pour détecter les instances simples et traiter en conséquence. OnStartup sera déclenché lorsque la première instance sera chargée. OnStartupNextInstance est lancé lorsque l'application est à nouveau lancée. Comme vous pouvez le voir, je peux accéder à ce qui a été passé sur la ligne de commande à travers les arguments d'événement. J'ai placé la valeur dans un champ d'instance. Vous pourriez analyser la ligne de commande ici, ou vous pouvez passer à votre demande par le constructeur et l'appel à l'Activer méthode.

troisièmement, il est temps de créer notre point D'entrée. Au lieu de newing l'application comme vous le feriez normalement, nous allons profiter de notre SingleInstanceManager.

public class EntryPoint
{
    [STAThread]
    public static void Main(string[] args)
    {
        SingleInstanceManager manager = new SingleInstanceManager();
        manager.Run(args);
    }
}

eh Bien, j'espère que vous êtes en mesure de suivre tout et être en mesure d'utiliser cette application et faire votre propre.

94
répondu Dale Ragan 2008-08-21 15:32:05

à Partir de ici .

une utilisation courante pour un Mutex Cross-process est de s'assurer que seule l'instance d'un programme peut s'exécuter à la fois. Voici comment on fait:

class OneAtATimePlease {

  // Use a name unique to the application (eg include your company URL)
  static Mutex mutex = new Mutex (false, "oreilly.com OneAtATimeDemo");

  static void Main()
  {
    // Wait 5 seconds if contended – in case another instance
    // of the program is in the process of shutting down.
    if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false))
    {
        Console.WriteLine("Another instance of the app is running. Bye!");
        return;
    }

    try
    {    
        Console.WriteLine("Running - press Enter to exit");
        Console.ReadLine();
    }
    finally
    {
        mutex.ReleaseMutex();
    }    
  }    
}

une bonne caractéristique de Mutex est que si L'application se termine sans que ReleaseMutex soit d'abord appelé, le CLR relâchera le Mutex automatiquement.

76
répondu jason saldo 2010-08-23 07:20:03

MSDN a en fait un exemple d'application pour C# et VB pour faire exactement ceci: http://msdn.microsoft.com/en-us/library/ms771662 (v = VS.90).aspx

la technique la plus courante et la plus fiable pour le développement d'instance unique la détection est D'utiliser le Microsoft. NET Cadre infrastructure à distance (Système.L'accès distant). La Microsoft .NET Le cadre (version 2.0) comprend un type, WindowsFormsApplicationBase, qui encapsule le nécessaire la fonction distante. Incorporer ce type dans une application WPF, un le type doit en dériver, et être utilisé comme un joint entre l'application méthode du point d'entrée statique, Main, et demande de la WPF type. La cale détecte quand un l'application est lancé pour la première fois, et lorsque les lancements ultérieurs sont les tentatives de contrôle du WPF Type de demande pour déterminer comment procédez aux lancements.

  • Pour C# les gens il suffit de prendre une profonde respiration et oublier tout le "je ne veux pas inclure VisualBasic DLL". En raison de ce et ce Scott Hanselman dit et le fait que cette assez grande est la solution la plus propre au problème et est conçu par des gens qui en savent beaucoup plus sur le cadre que vous.
  • du point de vue de la convivialité, le fait est que si votre Utilisateur charge une application et il est déjà ouvert et vous leur donnez un message d'erreur comme 'Another instance of the app is running. Bye' alors ils ne vont pas être un utilisateur très heureux. Vous devez simplement (dans une application GUI) passer à cette application et passer dans les arguments fournis - ou si les paramètres de ligne de commande n'ont pas de sens, alors vous devez pop up l'application qui peut avoir été minimisée.

le cadre a déjà le soutien pour cela - son juste qu'un idiot appelé la DLL Microsoft.VisualBasic et il n'a pas obtenu mettez dans Microsoft.ApplicationUtils ou quelque chose comme ça. Passez au - dessus ou ouvrez le réflecteur.

Astuce: Si vous utilisez cette approche exactement comme il est, et vous avez déjà une Application.xaml avec des ressources etc. vous aurez envie de jeter un oeil à cela aussi .

49
répondu Simon_Weaver 2017-05-23 12:18:22

ce code doit aller à la méthode principale. Regardez ici pour plus d'informations sur la méthode principale dans WPF.

[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);

private const int SW_SHOWMAXIMIZED = 3;

static void Main() 
{
    Process currentProcess = Process.GetCurrentProcess();
    var runningProcess = (from process in Process.GetProcesses()
                          where
                            process.Id != currentProcess.Id &&
                            process.ProcessName.Equals(
                              currentProcess.ProcessName,
                              StringComparison.Ordinal)
                          select process).FirstOrDefault();
    if (runningProcess != null)
    {
        ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED);
       return; 
    }
}

Méthode 2

static void Main()
{
    string procName = Process.GetCurrentProcess().ProcessName;
    // get the list of all processes by that name

    Process[] processes=Process.GetProcessesByName(procName);

    if (processes.Length > 1)
    {
        MessageBox.Show(procName + " already running");  
        return;
    } 
    else
    {
        // Application.Run(...);
    }
}

Note: les méthodes ci-dessus supposent que votre processus/demande a un nom unique. Parce qu'il utilise le nom du processus à trouver si tout processeurs existants. Donc, si votre application a une très nom commun (ex: bloc-notes), ci-dessus l'approche ne fonctionnera pas.

21
répondu CharithJ 2015-07-15 01:06:35

Eh bien, j'ai une classe jetable pour cela qui fonctionne facilement pour la plupart des cas d'utilisation:

utilisez - le comme ceci:

static void Main()
{
    using (SingleInstanceMutex sim = new SingleInstanceMutex())
    {
        if (sim.IsOtherInstanceRunning)
        {
            Application.Exit();
        }

        // Initialize program here.
    }
}

le voici:

/// <summary>
/// Represents a <see cref="SingleInstanceMutex"/> class.
/// </summary>
public partial class SingleInstanceMutex : IDisposable
{
    #region Fields

    /// <summary>
    /// Indicator whether another instance of this application is running or not.
    /// </summary>
    private bool isNoOtherInstanceRunning;

    /// <summary>
    /// The <see cref="Mutex"/> used to ask for other instances of this application.
    /// </summary>
    private Mutex singleInstanceMutex = null;

    /// <summary>
    /// An indicator whether this object is beeing actively disposed or not.
    /// </summary>
    private bool disposed;

    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class.
    /// </summary>
    public SingleInstanceMutex()
    {
        this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning);
    }

    #endregion

    #region Properties

    /// <summary>
    /// Gets an indicator whether another instance of the application is running or not.
    /// </summary>
    public bool IsOtherInstanceRunning
    {
        get
        {
            return !this.isNoOtherInstanceRunning;
        }
    }

    #endregion

    #region Methods

    /// <summary>
    /// Closes the <see cref="SingleInstanceMutex"/>.
    /// </summary>
    public void Close()
    {
        this.ThrowIfDisposed();
        this.singleInstanceMutex.Close();
    }

    public void Dispose()
    {
        this.Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            /* Release unmanaged ressources */

            if (disposing)
            {
                /* Release managed ressources */
                this.Close();
            }

            this.disposed = true;
        }
    }

    /// <summary>
    /// Throws an exception if something is tried to be done with an already disposed object.
    /// </summary>
    /// <remarks>
    /// All public methods of the class must first call this.
    /// </remarks>
    public void ThrowIfDisposed()
    {
        if (this.disposed)
        {
            throw new ObjectDisposedException(this.GetType().Name);
        }
    }

    #endregion
}
15
répondu Oliver Friedrich 2017-02-01 19:49:41

un nouveau qui utilise des trucs de Mutex et de IPC, et passe aussi tous les arguments de ligne de commande à l'instance en cours d'exécution, est WPF Single Instance Application .

12
répondu huseyint 2016-01-17 12:19:51

Le code C# .NET Seule Instance de l'Application qui est la référence pour l'marqué réponse est un excellent point de départ.

cependant, j'ai trouvé qu'il ne gère pas très bien les cas où l'instance qui existe déjà a un dialogue modal ouvert, que ce dialogue soit un dialogue géré (comme une autre forme telle qu'une boîte à propos), ou un dialogue non géré (comme le OpenFileDialog même en utilisant la classe standard .NET). Avec le code original, la forme principale est activé, mais le modal reste inactif, ce qui semble étrange, plus l'utilisateur doit cliquer dessus pour continuer à utiliser l'application.

donc, j'ai créé une classe utilitaire Monoinstance pour gérer tout cela très automatiquement pour les Winforms et les applications WPF.

Winforms :

1) modifier la classe de programme comme suit:

static class Program
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(Program).FullName);

    [STAThread]
    static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}

2) Modifier la classe de fenêtre principale comme suit:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    protected override void WndProc(ref Message m)
    {
        // if needed, the singleton will restore this window
        Program.Singleton.OnWndProc(this, m, true);

        // TODO: handle specific messages here if needed
        base.WndProc(ref m);
    }
}

WPF:

1) Modifiez la page de L'application comme ceci (et assurez-vous de définir son action de construction à la page pour être en mesure de redéfinir la méthode principale):

public partial class App : Application
{
    public static readonly SingleInstance Singleton = new SingleInstance(typeof(App).FullName);

    [STAThread]
    public static void Main(string[] args)
    {
        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        Singleton.RunFirstInstance(() =>
        {
            SingleInstanceMain(args);
        });
    }

    public static void SingleInstanceMain(string[] args)
    {
        // standard code that was in Main now goes here
        App app = new App();
        app.InitializeComponent();
        app.Run();
    }
}

2) Modifier la classe de fenêtre principale comme suit:

public partial class MainWindow : Window
{
    private HwndSource _source;

    public MainWindow()
    {
        InitializeComponent();
    }

    protected override void OnSourceInitialized(EventArgs e)
    {
        base.OnSourceInitialized(e);
        _source = (HwndSource)PresentationSource.FromVisual(this);
        _source.AddHook(HwndSourceHook);
    }

    protected virtual IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
    {
        // if needed, the singleton will restore this window
        App.Singleton.OnWndProc(hwnd, msg, wParam, lParam, true, true);

        // TODO: handle other specific message
        return IntPtr.Zero;
    }

et voici la classe d'utilité:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;

namespace SingleInstanceUtilities
{
    public sealed class SingleInstance
    {
        private const int HWND_BROADCAST = 0xFFFF;

        [DllImport("user32.dll")]
        private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

        [DllImport("user32.dll", CharSet = CharSet.Unicode)]
        private static extern int RegisterWindowMessage(string message);

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

        public SingleInstance(string uniqueName)
        {
            if (uniqueName == null)
                throw new ArgumentNullException("uniqueName");

            Mutex = new Mutex(true, uniqueName);
            Message = RegisterWindowMessage("WM_" + uniqueName);
        }

        public Mutex Mutex { get; private set; }
        public int Message { get; private set; }

        public void RunFirstInstance(Action action)
        {
            RunFirstInstance(action, IntPtr.Zero, IntPtr.Zero);
        }

        // NOTE: if this always return false, close & restart Visual Studio
        // this is probably due to the vshost.exe thing
        public void RunFirstInstance(Action action, IntPtr wParam, IntPtr lParam)
        {
            if (action == null)
                throw new ArgumentNullException("action");

            if (WaitForMutext(wParam, lParam))
            {
                try
                {
                    action();
                }
                finally
                {
                    ReleaseMutex();
                }
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            if (hwnd == IntPtr.Zero)
                return;

            FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
        }

        public void OnWndProc(IntPtr hwnd, int m, IntPtr wParam, IntPtr lParam, bool restorePlacement, bool activate)
        {
            if (m == Message)
            {
                if (restorePlacement)
                {
                    WindowPlacement placement = WindowPlacement.GetPlacement(hwnd, false);
                    if (placement.IsValid && placement.IsMinimized)
                    {
                        const int SW_SHOWNORMAL = 1;
                        placement.ShowCmd = SW_SHOWNORMAL;
                        placement.SetPlacement(hwnd);
                    }
                }

                if (activate)
                {
                    SetForegroundWindow(hwnd);
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
                }
            }
        }

#if WINFORMS // define this for Winforms apps
        public void OnWndProc(System.Windows.Forms.Form form, int m, IntPtr wParam, IntPtr lParam, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            if (m == Message)
            {
                if (activate)
                {
                    if (form.WindowState == System.Windows.Forms.FormWindowState.Minimized)
                    {
                        form.WindowState = System.Windows.Forms.FormWindowState.Normal;
                    }

                    form.Activate();
                    FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(form.Handle));
                }
            }
        }

        public void OnWndProc(System.Windows.Forms.Form form, System.Windows.Forms.Message m, bool activate)
        {
            if (form == null)
                throw new ArgumentNullException("form");

            OnWndProc(form, m.Msg, m.WParam, m.LParam, activate);
        }
#endif

        public void ReleaseMutex()
        {
            Mutex.ReleaseMutex();
        }

        public bool WaitForMutext(bool force, IntPtr wParam, IntPtr lParam)
        {
            bool b = PrivateWaitForMutext(force);
            if (!b)
            {
                PostMessage((IntPtr)HWND_BROADCAST, Message, wParam, lParam);
            }
            return b;
        }

        public bool WaitForMutext(IntPtr wParam, IntPtr lParam)
        {
            return WaitForMutext(false, wParam, lParam);
        }

        private bool PrivateWaitForMutext(bool force)
        {
            if (force)
                return true;

            try
            {
                return Mutex.WaitOne(TimeSpan.Zero, true);
            }
            catch (AbandonedMutexException)
            {
                return true;
            }
        }
    }

    // NOTE: don't add any field or public get/set property, as this must exactly map to Windows' WINDOWPLACEMENT structure
    [StructLayout(LayoutKind.Sequential)]
    public struct WindowPlacement
    {
        public int Length { get; set; }
        public int Flags { get; set; }
        public int ShowCmd { get; set; }
        public int MinPositionX { get; set; }
        public int MinPositionY { get; set; }
        public int MaxPositionX { get; set; }
        public int MaxPositionY { get; set; }
        public int NormalPositionLeft { get; set; }
        public int NormalPositionTop { get; set; }
        public int NormalPositionRight { get; set; }
        public int NormalPositionBottom { get; set; }

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool SetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);

        private const int SW_SHOWMINIMIZED = 2;

        public bool IsMinimized
        {
            get
            {
                return ShowCmd == SW_SHOWMINIMIZED;
            }
        }

        public bool IsValid
        {
            get
            {
                return Length == Marshal.SizeOf(typeof(WindowPlacement));
            }
        }

        public void SetPlacement(IntPtr windowHandle)
        {
            SetWindowPlacement(windowHandle, ref this);
        }

        public static WindowPlacement GetPlacement(IntPtr windowHandle, bool throwOnError)
        {
            WindowPlacement placement = new WindowPlacement();
            if (windowHandle == IntPtr.Zero)
                return placement;

            placement.Length = Marshal.SizeOf(typeof(WindowPlacement));
            if (!GetWindowPlacement(windowHandle, ref placement))
            {
                if (throwOnError)
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return new WindowPlacement();
            }
            return placement;
        }
    }

    public static class FormUtilities
    {
        [DllImport("user32.dll")]
        private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);

        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr SetActiveWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        private static extern bool IsWindowVisible(IntPtr hWnd);

        [DllImport("kernel32.dll")]
        public static extern int GetCurrentThreadId();

        private delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam);

        [DllImport("user32.dll")]
        private static extern bool EnumThreadWindows(int dwThreadId, EnumChildrenCallback lpEnumFunc, IntPtr lParam);

        private class ModalWindowUtil
        {
            private const int GW_OWNER = 4;
            private int _maxOwnershipLevel;
            private IntPtr _maxOwnershipHandle;

            private bool EnumChildren(IntPtr hwnd, IntPtr lParam)
            {
                int level = 1;
                if (IsWindowVisible(hwnd) && IsOwned(lParam, hwnd, ref level))
                {
                    if (level > _maxOwnershipLevel)
                    {
                        _maxOwnershipHandle = hwnd;
                        _maxOwnershipLevel = level;
                    }
                }
                return true;
            }

            private static bool IsOwned(IntPtr owner, IntPtr hwnd, ref int level)
            {
                IntPtr o = GetWindow(hwnd, GW_OWNER);
                if (o == IntPtr.Zero)
                    return false;

                if (o == owner)
                    return true;

                level++;
                return IsOwned(owner, o, ref level);
            }

            public static void ActivateWindow(IntPtr hwnd)
            {
                if (hwnd != IntPtr.Zero)
                {
                    SetActiveWindow(hwnd);
                }
            }

            public static IntPtr GetModalWindow(IntPtr owner)
            {
                ModalWindowUtil util = new ModalWindowUtil();
                EnumThreadWindows(GetCurrentThreadId(), util.EnumChildren, owner);
                return util._maxOwnershipHandle; // may be IntPtr.Zero
            }
        }

        public static void ActivateWindow(IntPtr hwnd)
        {
            ModalWindowUtil.ActivateWindow(hwnd);
        }

        public static IntPtr GetModalWindow(IntPtr owner)
        {
            return ModalWindowUtil.GetModalWindow(owner);
        }
    }
}
9
répondu Simon Mourier 2014-09-16 19:37:17

Voici un exemple qui vous permet d'avoir une seule instance d'une application. Quand de nouvelles instances se chargent, elles passent leurs arguments à l'instance principale qui est en cours d'exécution.

public partial class App : Application
{
    private static Mutex SingleMutex;
    public static uint MessageId;

    private void Application_Startup(object sender, StartupEventArgs e)
    {
        IntPtr Result;
        IntPtr SendOk;
        Win32.COPYDATASTRUCT CopyData;
        string[] Args;
        IntPtr CopyDataMem;
        bool AllowMultipleInstances = false;

        Args = Environment.GetCommandLineArgs();

        // TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID
        MessageId   = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}");
        SingleMutex = new Mutex(false, "AppName");

        if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true)))
        {
            new Main();
        }
        else if (Args.Length > 1)
        {
            foreach (Process Proc in Process.GetProcesses())
            {
                SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    2000, out Result);

                if (SendOk == IntPtr.Zero)
                    continue;
                if ((uint)Result != MessageId)
                    continue;

                CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT)));

                CopyData.dwData = IntPtr.Zero;
                CopyData.cbData = Args[1].Length*2;
                CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]);

                Marshal.StructureToPtr(CopyData, CopyDataMem, false);

                Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem,
                    Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
                    5000, out Result);

                Marshal.FreeHGlobal(CopyData.lpData);
                Marshal.FreeHGlobal(CopyDataMem);
            }

            Shutdown(0);
        }
    }
}

public partial class Main : Window
{
    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        HwndSource Source;

        Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
        Source.AddHook(new HwndSourceHook(Window_Proc));
    }

    private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
    {
        Win32.COPYDATASTRUCT CopyData;
        string Path;

        if (Msg == Win32.WM_COPYDATA)
        {
            CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT));
            Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2);

            if (WindowState == WindowState.Minimized)
            {
                // Restore window from tray
            }

            // Do whatever we want with information

            Activate();
            Focus();
        }

        if (Msg == App.MessageId)
        {
            Handled = true;
            return new IntPtr(App.MessageId);
        }

        return IntPtr.Zero;
    }
}

public class Win32
{
    public const uint WM_COPYDATA = 0x004A;

    public struct COPYDATASTRUCT
    {
        public IntPtr dwData;
        public int    cbData;
        public IntPtr lpData;
    }

    [Flags]
    public enum SendMessageTimeoutFlags : uint
    {
        SMTO_NORMAL             = 0x0000,
        SMTO_BLOCK              = 0x0001,
        SMTO_ABORTIFHUNG        = 0x0002,
        SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
    }

    [DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
    public static extern uint RegisterWindowMessage(string lpString);
    [DllImport("user32.dll")]
    public static extern IntPtr SendMessageTimeout(
        IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
        SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult);
}
9
répondu Nathan Moinvaziri 2016-01-18 23:40:16

Juste quelques pensées: Il y a des cas où exiger qu'une seule instance d'une demande ne soit pas "boiteuse" comme certains voudraient vous le faire croire. Base de données des applications, etc. sont un ordre de grandeur plus difficile si l'on permet plusieurs instances de l'application pour un utilisateur unique d'accéder à une base de données (vous savez, tout cela la mise à jour de tous les dossiers qui sont ouverts dans plusieurs instances de l'application sur la machine des utilisateurs, etc.). Tout d'abord, pour le "nom de collision chose, ne pas utiliser un nom lisible par l'homme - l'utilisation d'un GUID à la place ou, mieux encore, un GUID + le nom lisible par l'humain. Les risques de collision viennent de tomber du radar et le Mutex s'en fiche. Comme quelqu'un l'a souligné, une attaque DOS serait nul, mais si la personne malveillante s'est donné la peine d'obtenir le nom mutex et l'incorporer dans leur application, vous êtes à peu près une cible de toute façon et devra faire beaucoup plus pour vous protéger que juste jouer un nom mutex. Aussi, si l'on utilise la variante de: Nouveau Mutex (vrai, " certains GUID Plus Nom", en premier lieu, vous avez déjà votre indicateur pour savoir si le Mutex est la première instance ou non.

8
répondu Bruce 2009-12-17 21:25:51

tant de réponses à une question aussi simple. Juste pour secouer un peu les choses ici est ma solution à ce problème.

créer un Mutex peut être gênant car le JIT-er ne voit que vous l'utilisez pour une petite partie de votre code et veut le marquer comme prêt pour la collecte des ordures. Il veut à peu près out-smart vous pensez que vous n'allez pas utiliser ce Mutex pour si longtemps. En réalité, vous voulez vous accrocher à ce Mutex pour aussi longtemps que votre demande est en cours d'exécution. La meilleure façon de dire au ramasseur d'ordures de vous laisser Mutex seul est de lui dire de le garder en vie malgré les différentes générations de collecte de garage. Exemple:

var m = new Mutex(...);
...
GC.KeepAlive(m);

j'ai soulevé l'idée de cette page: http://www.ai.uga.edu/~mc/SingleInstance.html

6
répondu Peter 2010-05-05 00:54:24

il semble qu'il y ait un très bon moyen de gérer cela:

WPF Single Instance Application

cela fournit une classe que vous pouvez ajouter qui gère tous les mutex et messagerie cruff pour simplifier la votre mise en œuvre au point où il est tout simplement trivial.

6
répondu Joel Barsotti 2016-01-17 12:24:35

le code suivant est mon WCF nommé pipes solution pour enregistrer une application à instance unique. C'est bien parce qu'il soulève aussi un événement quand une autre instance tente de démarrer, et reçoit la ligne de commande de l'autre instance.

il est orienté vers WPF parce qu'il utilise la classe System.Windows.StartupEventHandler , mais cela pourrait être facilement modifié.

ce code exige une référence à PresentationFramework , et System.ServiceModel .

Utilisation:

class Program
{
    static void Main()
    {
        var applicationId = new Guid("b54f7b0d-87f9-4df9-9686-4d8fd76066dc");

        if (SingleInstanceManager.VerifySingleInstance(applicationId))
        {
            SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted;

            // Start the application
        }
    }

    static void OnOtherInstanceStarted(object sender, StartupEventArgs e)
    {
        // Do something in response to another instance starting up.
    }
}

Code Source:

/// <summary>
/// A class to use for single-instance applications.
/// </summary>
public static class SingleInstanceManager
{
  /// <summary>
  /// Raised when another instance attempts to start up.
  /// </summary>
  public static event StartupEventHandler OtherInstanceStarted;

  /// <summary>
  /// Checks to see if this instance is the first instance running on this machine.  If it is not, this method will
  /// send the main instance this instance's startup information.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if this instance is the main instance.</returns>
  public static bool VerifySingleInstace(Guid guid)
  {
    if (!AttemptPublishService(guid))
    {
      NotifyMainInstance(guid);

      return false;
    }

    return true;
  }

  /// <summary>
  /// Attempts to publish the service.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  /// <returns>True if the service was published successfully.</returns>
  private static bool AttemptPublishService(Guid guid)
  {
    try
    {
      ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance));
      NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
      serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid));
      serviceHost.Open();

      return true;
    }
    catch
    {
      return false;
    }
  }

  /// <summary>
  /// Notifies the main instance that this instance is attempting to start up.
  /// </summary>
  /// <param name="guid">The application's unique identifier.</param>
  private static void NotifyMainInstance(Guid guid)
  {
    NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
    EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid));
    using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress))
    {
      ISingleInstance singleInstance = factory.CreateChannel();
      singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs());
    }
  }

  /// <summary>
  /// Creates an address to publish/contact the service at based on a globally unique identifier.
  /// </summary>
  /// <param name="guid">The identifier for the application.</param>
  /// <returns>The address to publish/contact the service.</returns>
  private static string CreateAddress(Guid guid)
  {
    return string.Format(CultureInfo.CurrentCulture, "net.pipe://localhost/{0}", guid);
  }

  /// <summary>
  /// The interface that describes the single instance service.
  /// </summary>
  [ServiceContract]
  private interface ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    [OperationContract]
    void NotifyMainInstance(string[] args);
  }

  /// <summary>
  /// The implementation of the single instance service interface.
  /// </summary>
  private class SingleInstance : ISingleInstance
  {
    /// <summary>
    /// Notifies the main instance that another instance of the application attempted to start.
    /// </summary>
    /// <param name="args">The other instance's command-line arguments.</param>
    public void NotifyMainInstance(string[] args)
    {
      if (OtherInstanceStarted != null)
      {
        Type type = typeof(StartupEventArgs);
        ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
        StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null);
        FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic);
        Debug.Assert(argsField != null);
        argsField.SetValue(e, args);

        OtherInstanceStarted(null, e);
      }
    }
  }
}
6
répondu Dan 2016-01-17 12:33:31

vous ne devez jamais utiliser un mutex nommé pour implémenter une application à instance unique (ou du moins pas pour le code de production). Le code malveillant peut facilement DoS ( Déni de Service ) de votre cul...

5
répondu Matt Davison 2016-01-17 12:17:29

Voici ce que j'utilise. Il a combiné l'énumération de processus pour effectuer la commutation et mutex pour protéger de "cliqueurs actifs":

public partial class App
{
    [DllImport("user32")]
    private static extern int OpenIcon(IntPtr hWnd);

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

    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);
        var p = Process
           .GetProcessesByName(Process.GetCurrentProcess().ProcessName);
            foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero))
            {
                OpenIcon(t.MainWindowHandle);
                SetForegroundWindow(t.MainWindowHandle);
                Current.Shutdown();
                return;
            }

            // there is a chance the user tries to click on the icon repeatedly
            // and the process cannot be discovered yet
            bool createdNew;
            var mutex = new Mutex(true, "MyAwesomeApp", 
               out createdNew);  // must be a variable, though it is unused - 
            // we just need a bit of time until the process shows up
            if (!createdNew)
            {
                Current.Shutdown();
                return;
            }

            new Bootstrapper().Run();
        }
    }
4
répondu Sergey Aldoukhov 2010-06-22 00:04:21

j'ai trouvé la solution la plus simple, semblable à celle de Dale Ragan, mais légèrement modifiée. Il fait pratiquement tout ce dont vous avez besoin et basé sur la classe de Base Standard Microsoft WindowsFormsApplicationBase.

tout D'abord, vous créez la classe SingleInstanceController, que vous pouvez utiliser dans toutes les autres applications à instance unique, qui utilisent les formes de Windows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;


namespace SingleInstanceController_NET
{
    public class SingleInstanceController
    : WindowsFormsApplicationBase
    {
        public delegate Form CreateMainForm();
        public delegate void StartNextInstanceDelegate(Form mainWindow);
        CreateMainForm formCreation;
        StartNextInstanceDelegate onStartNextInstance;
        public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance)
        {
            // Set whether the application is single instance
            this.formCreation = formCreation;
            this.onStartNextInstance = onStartNextInstance;
            this.IsSingleInstance = true;

            this.StartupNextInstance += new StartupNextInstanceEventHandler(this_StartupNextInstance);                      
        }

        void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
        {
            if (onStartNextInstance != null)
            {
                onStartNextInstance(this.MainForm); // This code will be executed when the user tries to start the running program again,
                                                    // for example, by clicking on the exe file.
            }                                       // This code can determine how to re-activate the existing main window of the running application.
        }

        protected override void OnCreateMainForm()
        {
            // Instantiate your main application form
            this.MainForm = formCreation();
        }

        public void Run()
        {
            string[] commandLine = new string[0];
            base.Run(commandLine);
        }
    }
}

alors vous pouvez l'utiliser dans votre programme comme suit:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SingleInstanceController_NET;

namespace SingleInstance
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        static Form CreateForm()
        {
            return new Form1(); // Form1 is used for the main window.
        }

        static void OnStartNextInstance(Form mainWindow) // When the user tries to restart the application again,
                                                         // the main window is activated again.
        {
            mainWindow.WindowState = FormWindowState.Maximized;
        }
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);            
            SingleInstanceController controller = new SingleInstanceController(CreateForm, OnStartNextInstance);
            controller.Run();         
        }
    }
}

le programme et la solution SingleInstanceController_NET devraient faire référence à Microsoft.VisualBasic . Si vous voulez simplement réactiver l'application en cours d'exécution comme une fenêtre normale lorsque l'utilisateur essaie de redémarrer le programme en cours d'exécution, le deuxième paramètre du Controller SingleInstanceController peut être null. Dans l'exemple donné, la fenêtre est maximisée.

4
répondu Mikhail Semenov 2011-04-04 09:12:41

regardez le code suivant. Il s'agit d'une solution simple et efficace pour prévenir de multiples instances d'une demande de WPF.

private void Application_Startup(object sender, StartupEventArgs e)
{
    Process thisProc = Process.GetCurrentProcess();
    if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
    {
        MessageBox.Show("Application running");
        Application.Current.Shutdown();
        return;
    }

    var wLogin = new LoginWindow();

    if (wLogin.ShowDialog() == true)
    {
        var wMain = new Main();
        wMain.WindowState = WindowState.Maximized;
        wMain.Show();
    }
    else
    {
        Application.Current.Shutdown();
    }
}
4
répondu carlito 2016-01-17 12:26:10

ne Pas utiliser un Mutex bien, la réponse est simple:

System.Diagnostics;    
...
string thisprocessname = Process.GetCurrentProcess().ProcessName;

if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

mettez-le à l'intérieur du Program.Main() .

exemple :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;

namespace Sample
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            //simple add Diagnostics namespace, and these 3 lines below 
            string thisprocessname = Process.GetCurrentProcess().ProcessName;
            if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
                return;

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Sample());
        }
    }
}

vous pouvez ajouter MessageBox.Show à la if - déclaration et mettre"Application déjà en cours d'exécution".

Cela peut être utile à quelqu'un.

2
répondu newbieguy 2016-12-30 04:30:21

mise à Jour 2017-01-25. après avoir essayé peu de choses, j'ai décidé D'aller avec VisualBasic.dll il est plus facile et fonctionne mieux (au moins pour moi). J'ai laissé ma réponse précédente comme référence...

juste comme référence, c'est comme ça que j'ai fait sans passer d'arguments (que je ne trouve aucune raison de le faire... Je veux dire d'une seule application, avec des arguments qui que la transmission d'une instance à l'autre). Si l'association de fichiers est nécessaire, alors une application devrait (selon l'attente standard de l'Utilisateur) être instancié pour chaque doc. Si vous devez passer args à l'application existante, je pense que je voudrais utiliser VB dll.

ne pas passer args (simple application d'instance), je préfère ne pas enregistrer un nouveau message de fenêtre et ne pas outrepasser la boucle de message tel que défini dans la solution de Matt Davis. Bien que ce ne soit pas une grosse affaire pour ajouter une dll VisualBasic, mais je préfère ne pas ajouter une nouvelle référence juste pour faire simple application d'instance. Aussi, je préfère instanciate une nouvelle classe avec Main au lieu d'appeler Shutdown à partir de L'application.Startup override pour s'assurer de quitter le plus tôt possible.

Dans l'espoir que quelqu'un comme elle... ou inspirera un peu: -)

La classe

"Project startup" doit être définie comme "SingleInstanceApp".

public class SingleInstanceApp
{
    [STAThread]
    public static void Main(string[] args)
    {
        Mutex _mutexSingleInstance = new Mutex(true, "MonitorMeSingleInstance");

        if (_mutexSingleInstance.WaitOne(TimeSpan.Zero, true))
        {
            try
            {
                var app = new App();
                app.InitializeComponent();
                app.Run();

            }
            finally
            {
                _mutexSingleInstance.ReleaseMutex();
                _mutexSingleInstance.Close();
            }
        }
        else
        {
            MessageBox.Show("One instance is already running.");

            var processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name);
            {
                if (processes.Length > 1)
                {
                    foreach (var process in processes)
                    {
                        if (process.Id != Process.GetCurrentProcess().Id)
                        {
                            WindowHelper.SetForegroundWindow(process.MainWindowHandle);
                        }
                    }
                }
            }
        }
    }
}

WindowHelper:

using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;

namespace HQ.Util.Unmanaged
{
    public class WindowHelper
    {
        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);
2
répondu Eric Ouellet 2017-01-25 13:56:22

utiliser solution de mutex:

using System;
using System.Windows.Forms;
using System.Threading;

namespace OneAndOnlyOne
{
static class Program
{
    static String _mutexID = " // generate guid"
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);

        Boolean _isNotRunning;
        using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning))
        {
            if (_isNotRunning)
            {
                Application.Run(new Form1());
            }
            else
            {
                MessageBox.Show("An instance is already running.");
                return;
            }
        }
    }
}
}
1
répondu Cornel Marian 2013-10-19 20:23:46

Voici une solution légère que j'utilise qui permet à l'application d'apporter une fenêtre déjà existante au premier plan sans recourir à des messages windows personnalisés ou des noms de processus de recherche aveuglément.

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

static readonly string guid = "<Application Guid>";

static void Main()
{
    Mutex mutex = null;
    if (!CreateMutex(out mutex))
        return;

    // Application startup code.

    Environment.SetEnvironmentVariable(guid, null, EnvironmentVariableTarget.User);
}

static bool CreateMutex(out Mutex mutex)
{
    bool createdNew = false;
    mutex = new Mutex(false, guid, out createdNew);

    if (createdNew)
    {
        Process process = Process.GetCurrentProcess();
        string value = process.Id.ToString();

        Environment.SetEnvironmentVariable(guid, value, EnvironmentVariableTarget.User);
    }
    else
    {
        string value = Environment.GetEnvironmentVariable(guid, EnvironmentVariableTarget.User);
        Process process = null;
        int processId = -1;

        if (int.TryParse(value, out processId))
            process = Process.GetProcessById(processId);

        if (process == null || !SetForegroundWindow(process.MainWindowHandle))
            MessageBox.Show("Unable to start application. An instance of this application is already running.");
    }

    return createdNew;
}

Edit: vous pouvez également stocker et initialiser mutex et createdNew statically, mais vous aurez besoin d'éliminer/libérer explicitement le mutex une fois que vous en avez fini avec lui. Personnellement, je préfère garder le mutex local car il sera automatiquement éliminé même si L'application se ferme sans jamais atteindre la fin de Main.

1
répondu Jason Lim 2014-08-07 17:18:16

vous pouvez également utiliser le Codefluent Runtime qui est un jeu gratuit d'outils. Il fournit une classe Mono instance pour implémenter une application à instance unique.

1
répondu Antoine Diekmann 2014-09-15 08:54:54

Voici la même chose implémentée via Event.

public enum ApplicationSingleInstanceMode
{
    CurrentUserSession,
    AllSessionsOfCurrentUser,
    Pc
}

public class ApplicationSingleInstancePerUser: IDisposable
{
    private readonly EventWaitHandle _event;

    /// <summary>
    /// Shows if the current instance of ghost is the first
    /// </summary>
    public bool FirstInstance { get; private set; }

    /// <summary>
    /// Initializes 
    /// </summary>
    /// <param name="applicationName">The application name</param>
    /// <param name="mode">The single mode</param>
    public ApplicationSingleInstancePerUser(string applicationName, ApplicationSingleInstanceMode mode = ApplicationSingleInstanceMode.CurrentUserSession)
    {
        string name;
        if (mode == ApplicationSingleInstanceMode.CurrentUserSession)
            name = $"Local\{applicationName}";
        else if (mode == ApplicationSingleInstanceMode.AllSessionsOfCurrentUser)
            name = $"Global\{applicationName}{Environment.UserDomainName}";
        else
            name = $"Global\{applicationName}";

        try
        {
            bool created;
            _event = new EventWaitHandle(false, EventResetMode.ManualReset, name, out created);
            FirstInstance = created;
        }
        catch
        {
        }
    }

    public void Dispose()
    {
        _event.Dispose();
    }
}
1
répondu Siarhei Kuchuk 2016-07-11 11:56:17

je ne peux pas trouver un à court de solution ici, donc j'espère que quelqu'un sera comme ceci:

mise à jour 2018-09-20

(Btw. mettez le code dans votre programme .cs " )

    using System.Diagnostics;

    static void Main()
    {
        Process ThisProcess = Process.GetCurrentProcess();
        Process[] AllProcesses = Process.GetProcessesByName(ThisProcess.ProcessName);
        if (AllProcesses.Length > 1)
        {
            //Don't put a MessageBox in here because the user could spam this MessageBox.
            return;
        }

/ / code facultatif. Si vous ne voulez pas que quelqu'un va vous êtes ".exe" avec un nom différent:

        string exeName = AppDomain.CurrentDomain.FriendlyName;
        if (exeName != "the name of you're executable.exe") // If u try that in debug mode, don't forget that u don't use ur normal .exe. Debug uses the .vshost.exe.
        {// You can add here a MassageBox if you want. To point users that the name got changed and maybe what the name should be or something like that^^ 
            MessageBox.Show("The executable name should be \"the name of you're executable.exe\"", 
            "Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        //Following Code is default code:
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());
    }
1
répondu Deniz 2018-09-26 07:01:37

j'ai ajouté une méthode sendMessage à la classe des méthodes Nativ.

apparemment le travail de dosent de méthode de post-essai, si l'application n'est pas montré dans la barre des tâches, cependant en utilisant la méthode de sendmessage résout cela.

class NativeMethods
{
    public const int HWND_BROADCAST = 0xffff;
    public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
    [DllImport("user32")]
    public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
}
0
répondu Martin Bech 2015-01-26 08:35:05

généralement quand on exécute un .exe, chaque fois qu'il crée un processus windows séparé avec son propre espace d'adresse, des ressources et ainsi de suite. Mais nous ne voulons pas de ce critère car cela nous empêche de créer des processus unique. Les applications à instance unique peuvent être créées en utilisant le Mutex en C# qui est discuté dans cet article

en Outre, si nous voulons apporter de l'application sur le dessus, nous pouvons le faire en utilisant

 [DllImport("user32")]
 static extern IntPtr SetForegroundWindow(IntPtr hWnd);
0
répondu Vikram 2015-08-04 06:10:24

normalement, c'est le code que j'utilise pour une instance unique Windows Forms applications:

[STAThread]
public static void Main()
{
    String assemblyName = Assembly.GetExecutingAssembly().GetName().Name;

    using (Mutex mutex = new Mutex(false, assemblyName))
    {
        if (!mutex.WaitOne(0, false))
        {
            Boolean shownProcess = false;
            Process currentProcess = Process.GetCurrentProcess();

            foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
            {
                if (!process.Id.Equals(currentProcess.Id) && process.MainModule.FileName.Equals(currentProcess.MainModule.FileName) && !process.MainWindowHandle.Equals(IntPtr.Zero))
                {
                    IntPtr windowHandle = process.MainWindowHandle;

                    if (NativeMethods.IsIconic(windowHandle))
                        NativeMethods.ShowWindow(windowHandle, ShowWindowCommand.Restore);

                    NativeMethods.SetForegroundWindow(windowHandle);

                    shownProcess = true;
                }
            }

            if (!shownProcess)
                MessageBox.Show(String.Format(CultureInfo.CurrentCulture, "An instance of {0} is already running!", assemblyName), assemblyName, MessageBoxButtons.OK, MessageBoxIcon.Asterisk, MessageBoxDefaultButton.Button1, (MessageBoxOptions)0);
        }
        else
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form());
        }
    }
}

où les composants natifs sont:

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean IsIconic([In] IntPtr windowHandle);

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean SetForegroundWindow([In] IntPtr windowHandle);

[DllImport("User32.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern Boolean ShowWindow([In] IntPtr windowHandle, [In] ShowWindowCommand command);

public enum ShowWindowCommand : int
{
    Hide                   = 0x0,
    ShowNormal             = 0x1,
    ShowMinimized          = 0x2,
    ShowMaximized          = 0x3,
    ShowNormalNotActive    = 0x4,
    Minimize               = 0x6,
    ShowMinimizedNotActive = 0x7,
    ShowCurrentNotActive   = 0x8,
    Restore                = 0x9,
    ShowDefault            = 0xA,
    ForceMinimize          = 0xB
}
0
répondu Tommaso Belluzzo 2016-01-17 12:30:24

Voici une solution:

Protected Overrides Sub OnStartup(e As StartupEventArgs)
    Const appName As String = "TestApp"
    Dim createdNew As Boolean
    _mutex = New Mutex(True, appName, createdNew)
    If Not createdNew Then
        'app is already running! Exiting the application
        MessageBox.Show("Application is already running.")
        Application.Current.Shutdown()
    End If
    MyBase.OnStartup(e)
End Sub
0
répondu Code Scratcher 2016-07-05 21:14:10

c'est ainsi que j'ai fini par m'occuper de cette question. Notez que le code de débogage est toujours là pour les tests. Ce code est dans L'OnStartup de L'application.XAML.cs fichier. (WPF)

        // Process already running ? 
        if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
        {

            // Show your error message
            MessageBox.Show("xxx is already running.  \r\n\r\nIf the original process is hung up you may need to restart your computer, or kill the current xxx process using the task manager.", "xxx is already running!", MessageBoxButton.OK, MessageBoxImage.Exclamation);

            // This process 
            Process currentProcess = Process.GetCurrentProcess();

            // Get all processes running on the local computer.
            Process[] localAll = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);

            // ID of this process... 
            int temp = currentProcess.Id;
            MessageBox.Show("This Process ID:  " + temp.ToString());

            for (int i = 0; i < localAll.Length; i++)
            {
                // Find the other process 
                if (localAll[i].Id != currentProcess.Id)
                {
                    MessageBox.Show("Original Process ID (Switching to):  " + localAll[i].Id.ToString());

                    // Switch to it... 
                    SetForegroundWindow(localAll[i].MainWindowHandle);

                }
            }

            Application.Current.Shutdown();

        }

il y a peut-être des problèmes que je n'ai pas encore cernés. Si j'en croise, je mettrai à jour ma réponse.

0
répondu pStan 2016-08-09 14:35:08

Voici mes 2 cents

 static class Program
    {
        [STAThread]
        static void Main()
        {
            bool createdNew;
            using (new Mutex(true, "MyApp", out createdNew))
            {
                if (createdNew) {
                    Application.EnableVisualStyles();
                    Application.SetCompatibleTextRenderingDefault(false);
                    var mainClass = new SynGesturesLogic();
                    Application.ApplicationExit += mainClass.tray_exit;
                    Application.Run();
                }
                else
                {
                    var current = Process.GetCurrentProcess();
                    foreach (var process in Process.GetProcessesByName(current.ProcessName).Where(process => process.Id != current.Id))
                    {
                        NativeMethods.SetForegroundWindow(process.MainWindowHandle);
                        break;
                    }
                }
            }
        }
    }
0
répondu kakopappa 2016-08-26 07:11:53