c# - ventanas - xamarin forms tutorial




¿Cuál es la forma correcta de crear una aplicación WPF de instancia única? (20)

Aquí hay un ejemplo que le permite tener una sola instancia de una aplicación. Cuando se cargan nuevas instancias, pasan sus argumentos a la instancia principal que se está ejecutando.

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

Al usar C # y WPF en .NET (en lugar de Windows Forms o consola), ¿cuál es la forma correcta de crear una aplicación que solo se puede ejecutar como una sola instancia?

Sé que tiene algo que ver con una cosa mítica llamada mutex, rara vez puedo encontrar a alguien que se moleste en detenerse y explicar qué es uno de estos.

El código también debe informar a la instancia que ya se está ejecutando que el usuario intentó iniciar una segunda, y quizás también pase cualquier argumento de la línea de comandos si existiera alguno.


Aquí hay un muy buen article sobre la solución Mutex. El enfoque descrito por el artículo es ventajoso por dos razones.

Primero, no requiere una dependencia en el ensamblado Microsoft.VisualBasic. Si mi proyecto ya dependiera de esa asamblea, probablemente recomendaría el uso del enfoque que se muestra en la respuesta aceptada. Pero como es, no uso el ensamblado Microsoft.VisualBasic, y prefiero no agregar una dependencia innecesaria a mi proyecto.

En segundo lugar, el artículo muestra cómo llevar la instancia existente de la aplicación al primer plano cuando el usuario intenta iniciar otra instancia. Ese es un toque muy agradable que las otras soluciones de Mutex descritas aquí no abordan.

ACTUALIZAR

A partir del 1 de agosto de 2014, el artículo que vinculé anteriormente todavía está activo, pero el blog no se ha actualizado en mucho tiempo. Eso me preocupa de que eventualmente pueda desaparecer, y con ello, la solución recomendada. Estoy reproduciendo el contenido del artículo aquí para la posteridad. Las palabras pertenecen únicamente al propietario del blog en Sanity Free Coding .

Hoy quería refactorizar un código que prohibía que mi aplicación ejecutara múltiples instancias de sí misma.

Anteriormente había usado System.Diagnostics.Process para buscar una instancia de mi myapp.exe en la lista de procesos. Si bien esto funciona, conlleva muchos gastos generales y quería algo más limpio.

Sabiendo que podría usar un mutex para esto (pero nunca haberlo hecho antes) me propuse cortar mi código y simplificar mi vida.

En la clase de mi aplicación principal, creé una estática llamada Mutex :

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

Tener un mutex con nombre nos permite apilar la sincronización a través de múltiples procesos y subprocesos, que es la magia que estoy buscando.

Mutex.WaitOne tiene una sobrecarga que especifica una cantidad de tiempo para que esperemos. Dado que en realidad no queremos sincronizar nuestro código (más simplemente verifique si está actualmente en uso) usamos la sobrecarga con dos parámetros: Mutex.WaitOne (Timepan timeout, bool exitContext) . Espera uno devuelve verdadero si es capaz de ingresar, y falso si no lo fue. En este caso, no queremos esperar nada; Si se está utilizando nuestro mutex, omítalo y continúe, así pasamos en TimeSpan.Zero (espere 0 milisegundos), y configuramos exitContext en true para que podamos salir del contexto de sincronización antes de intentar bloquearlo. Usando esto, envolvemos nuestro código Application.Run dentro de algo como esto:

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

Por lo tanto, si nuestra aplicación se está ejecutando, WaitOne devolverá el valor falso y obtendremos un cuadro de mensaje.

En lugar de mostrar un cuadro de mensaje, opté por utilizar un poco de Win32 para notificar a mi instancia en ejecución que alguien olvidó que ya se estaba ejecutando (colocándose en la parte superior de todas las demás ventanas). Para lograr esto, utilicé PostMessage para transmitir un mensaje personalizado a cada ventana (el mensaje personalizado fue registrado con RegisterWindowMessage por mi aplicación en ejecución, lo que significa que solo mi aplicación sabe qué es) y luego mi segunda instancia se cierra. La instancia de la aplicación en ejecución recibiría esa notificación y la procesaría. Para hacer eso, WndProc en mi formulario principal y escuché mi notificación personalizada. Cuando recibí esa notificación, establezco la propiedad TopMost del formulario en true para que aparezca en la parte superior.

Esto es lo que terminé con:

  • Programa.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 (lado frontal parcial)
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;
    }
}

Bueno, tengo una clase desechable para esto que funciona fácilmente para la mayoría de los casos de uso:

Úsalo así:

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

        // Initialize program here.
    }
}

Aquí está:

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

Desde here

Un uso común para un Mutex de proceso cruzado es asegurar que solo se pueda ejecutar una instancia de un programa a la vez. Así es como se hace:

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

Una buena característica de Mutex es que si la aplicación finaliza sin que se llame primero a ReleaseMutex, el CLR lanzará el Mutex automáticamente.


Este código debe ir al método principal. Mire here para obtener más información sobre el método principal en 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étodo 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(...);
    }
}

Nota: los métodos anteriores suponen que su proceso / aplicación tiene un nombre único. Debido a que utiliza el nombre del proceso para encontrar si hay procesadores existentes. Entonces, si su aplicación tiene un nombre muy común (es decir, Bloc de notas), el enfoque anterior no funcionará.


MSDN en realidad tiene una aplicación de ejemplo para C # y VB para hacer exactamente esto: http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx

La técnica más común y confiable para desarrollar la detección de una sola instancia es usar la infraestructura remota de Microsoft .NET Framework (System.Remoting). Microsoft .NET Framework (versión 2.0) incluye un tipo, WindowsFormsApplicationBase, que encapsula la funcionalidad de comunicación remota requerida. Para incorporar este tipo en una aplicación WPF, un tipo debe derivarse de él, y debe utilizarse como un calzo entre el método de punto de entrada estático de la aplicación, Principal y el tipo de Aplicación de la aplicación WPF. El shim detecta cuándo se inicia una aplicación por primera vez, y cuando se intentan los lanzamientos posteriores, y los rendimientos controlan el tipo de aplicación WPF para determinar cómo procesar los lanzamientos.

  • Para C #, la gente simplemente respira hondo y se olvida de todo "No quiero incluir VisualBasic DLL". Por this y por lo que dice Scott Hanselman y por el hecho de que esta es prácticamente la solución más limpia al problema y está diseñada por personas que saben mucho más sobre el marco que usted.
  • Desde el punto de vista de la usabilidad, el hecho es si su usuario está cargando una aplicación y ya está abierta y le está dando un mensaje de error como 'Another instance of the app is running. Bye' 'Another instance of the app is running. Bye' entonces no van a ser un usuario muy feliz. Simplemente DEBE (en una aplicación GUI) cambiar a esa aplicación y pasar los argumentos proporcionados, o si los parámetros de la línea de comando no tienen ningún significado, debe mostrar la aplicación que puede haber sido minimizada.

El marco de trabajo ya tiene soporte para esto, es solo que un idiota llamado DLL Microsoft.VisualBasic y no se colocó en Microsoft.ApplicationUtils o algo así. Supéralo, o abre Reflector.

Sugerencia: si utiliza este enfoque exactamente como es, y ya tiene un App.xaml con recursos, etc., querrá echarle un vistazo a esto también .



Actualización 2017-01-25. Después de probar algunas cosas, decidí usar VisualBasic.dll, es más fácil y funciona mejor (al menos para mí). Dejo mi respuesta anterior solo como referencia ...

Justo como referencia, así es como lo hice sin pasar argumentos (lo cual no puedo encontrar ninguna razón para hacerlo ... me refiero a una aplicación única con argumentos que pasan de una instancia a otra). Si se requiere la asociación de archivos, entonces se debe instanciar una aplicación (según la expectativa estándar de los usuarios) para cada documento. Si tiene que pasar args a la aplicación existente, creo que usaría vb dll.

No pasar args (solo aplicación de instancia única), prefiero no registrar un nuevo mensaje de Windows y no anular el bucle de mensajes como se define en la Solución de Matt Davis. Aunque no es un gran problema agregar un VisualBasic dll, pero prefiero no agregar una nueva referencia solo para hacer una aplicación de instancia única. Además, prefiero instanciar una nueva clase con Main en lugar de llamar a Shutdown from App.Startup override para asegurar que salga lo antes posible.

Con la esperanza de que a alguien le guste ... o le inspire un poco :-)

La clase de inicio del proyecto debe establecerse como '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);

Aquí están mis 2 centavos.

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

Esta es una solución liviana que utilizo que le permite a la aplicación traer una ventana ya existente a primer plano sin tener que recurrir a mensajes personalizados de Windows o buscar nombres de procesos a ciegas.

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

Editar: También puede almacenar e inicializar la exclusión mutua y crear Nueva estaticamente, pero tendrá que disponer / liberar explícitamente la exclusión mutua una vez que haya terminado con ella. Personalmente, prefiero mantener el mutex local, ya que se eliminará automáticamente incluso si la aplicación se cierra sin llegar al final de Main.


Parece que hay una muy buena manera de manejar esto:

blogs.microsoft.co.il/blogs/arik/archive/2010/05/28/…

Esto proporciona una clase que puede agregar que administra todo el problema de exclusión y mensajería para simplificar su implementación hasta el punto en que es simplemente trivial.


Sin usar Mutex, respuesta simple:

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

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

Ponlo dentro del Program.Main().
Ejemplo :

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

Puede agregar MessageBox.Showa la ifdeclaración y poner "Aplicación que ya se está ejecutando".
Esto podría ser útil para alguien.


Así es como terminé ocupándome de este problema. Tenga en cuenta que el código de depuración todavía está allí para probar. Este código está dentro de OnStartup en el archivo App.xaml.cs. (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();

        }

Esto puede tener problemas que no he detectado todavía. Si me encuentro con alguno actualizaré mi respuesta.


El siguiente código es mi solución de tuberías con nombre WCF para registrar una aplicación de instancia única. Está bien porque también provoca un evento cuando otra instancia intenta iniciarse y recibe la línea de comandos de la otra instancia.

Está orientado hacia WPF porque usa la System.Windows.StartupEventHandlerclase, pero esto podría modificarse fácilmente.

Este código requiere una referencia a PresentationFramework, y System.ServiceModel.

Uso:

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

Código fuente:

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

Esto es lo que yo uso. Combinó la enumeración de procesos para realizar la conmutación y la exclusión mutua para salvaguardar de los "clics activos":

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

Mira el siguiente código. Es una solución excelente y simple para evitar múltiples instancias de una aplicación 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();
    }
}

Normalmente, este es el código que uso para las aplicaciones de Windows Forms de instancia única :

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

Donde los componentes nativos son:

[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
}

Nunca debe usar un mutex con nombre para implementar una aplicación de instancia única (o al menos no para el código de producción). El código malicioso puede fácilmente DoS ( denegación de servicio ) su culo ...


Solo algunos pensamientos: hay casos en los que se requiere que solo una instancia de una aplicación no sea "aburrida", como algunos le harían creer. Las aplicaciones de la base de datos, etc. son un orden de magnitud más difícil si se permite que múltiples instancias de la aplicación para que un solo usuario acceda a una base de datos (ya sabes, todo eso actualiza todos los registros que están abiertos en varias instancias de la aplicación para los usuarios). máquina, etc.). En primer lugar, por lo que se refiere a la "colisión de nombres", no use un nombre legible por humanos, use un GUID en su lugar o, mejor aún, un GUID + el nombre legible por humanos. Como alguien señaló, un ataque de DOS apestaría, pero si la persona maliciosa se tomó la molestia de obtener el nombre de exclusión mutua e incorporarlo en su aplicación,De todos modos, es prácticamente un objetivo y tendrá que hacer MUCHO más para protegerse que solo un nombre de mutex. Además, si uno usa la variante de: nuevo Mutex (verdadero, "algún GUID más Nombre", fuera de AIsFirstInstance), ya tiene su indicador de si el Mutex es la primera instancia.






mutex