Pregunta

Tengo una aplicación WinForms, donde si ya hay una instancia en ejecución & amp; el usuario intenta activar otro, lo detengo comprobando un Mutex antes de llamar a Application.Run (). Esa parte funciona bien. Lo que me gustaría hacer es pasar un mensaje de la nueva instancia de la aplicación (junto con un dato en forma de cadena) a la instancia existente antes de finalizar el nuevo proceso.

He intentado llamar a PostMessage, y recibo el mensaje en la aplicación en ejecución, pero la cadena que paso en el lparam falla (sí, lo he verificado para asegurarme de que estoy pasando una buena cadena a empezar con). ¿Cómo puedo hacer esto mejor?

static class Program
{
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    public static extern bool PostMessage(int hhwnd, uint msg, IntPtr wparam, IntPtr lParam);

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern uint RegisterWindowMessage(string lpString);

    private const int HWND_BROADCAST = 0xffff;
    static uint _wmJLPC = unchecked((uint)-1);

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        _wmJLPC = RegisterWindowMessage("JumpListProjectClicked");
        if (_wmJLPC == 0)
        {
            throw new Exception(string.Format("Error registering window message: \"{0}\"", Marshal.GetLastWin32Error().ToString()));
        }

        bool onlyInstance = false;
        Mutex mutex = new Mutex(true, "b73fd756-ac15-49c4-8a9a-45e1c2488599_ProjectTracker", out onlyInstance);

        if (!onlyInstance) {
            ProcessArguments();
            return;
        }

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new MainForm());

        GC.KeepAlive(mutex);
    }

    internal static void ProcessArguments()
    {
        if (Environment.GetCommandLineArgs().Length > 1)
        {
            IntPtr param = Marshal.StringToHGlobalAuto(Environment.GetCommandLineArgs()[1]);
            PostMessage(HWND_BROADCAST, _wmJLPC, IntPtr.Zero, param);
        }
    }
}

En otra parte, en mi formulario ...

protected override void WndProc(ref Message m)
{
    try
    {
        if (m.Msg == _wmJLPC)
        {
             // always returns an empty string
             string param = Marshal.PtrToStringAnsi(m.LParam);

             // UI code omitted
        }
    }
    catch (Exception ex)
    {
        HandleException(ex);
    }

    base.WndProc(ref m);
}
¿Fue útil?

Solución

Greg,

El puntero no administrado creado por StringToHGlobalAuto solo es válido en el espacio de proceso que lo creó. No se puede acceder a la memoria a la que hace referencia desde el otro proceso.

Para pasar datos de una aplicación a otra, use SendMessage () con el mensaje WM_COPYDATA.

Scott

Otros consejos

Verifique su código nuevamente. Estás utilizando StringToHGlobalAuto para crear la cadena (probablemente termina con Unicode). Luego, está llamando a PtrToStringAnsi, que no está utilizando Unicode.

Si no puede hacer que esta solución funcione, hay varias opciones. Puede leer sobre ellos buscando IPC (comunicación entre procesos).

Un método que he usado, ya que fue rápido y fácil, es crear un archivo conocido con el contenido necesario. Bloqueas el uso de ese archivo con eventos con nombre y le dices al '' propietario '' aplicación que el archivo ha cambiado al configurar otro evento con nombre. El "propietario" comprueba el evento ocasionalmente o inicia un subproceso de trabajo para observarlo.

Nuevamente, IPC tiene muchos sabores, si estas ideas no funcionan, sigue buscando.

Aquí hay una manera simple. No he ejecutado el código pero tienes la idea

class Program
{
    static Thread listenThread;
    static void Main(string[] args)
    {
        try
        {
            using (Mutex mutex = new Mutex(true, "my mutex"))
            {
                listenThread = new Thread(Listen);
                listenThread.IsBackground = true;
                listenThread.Start();
            }
        }
        catch (ApplicationException)
        {
            using (Mutex mutex = Mutex.OpenExisting("my mutex"))
            {
                mutex.WaitOne();
                try
                {
                    using (NamedPipeClientStream client = new NamedPipeClientStream("some pipe"))
                    {
                        using (StreamWriter writer = new StreamWriter(client))
                        {
                            writer.WriteLine("SomeMessage");
                        }
                    }
                }
                finally
                {
                    mutex.ReleaseMutex();
                }
            }
        }
    }
    static void Listen()
    {
        using (NamedPipeServerStream server = new NamedPipeServerStream("some pipe"))
        {
            using (StreamReader reader = new StreamReader(server))
            {
                for (; ; )
                {
                    server.WaitForConnection();
                    string message = reader.ReadLine();
                    //Dispatch the message, probably onto the thread your form 
                    //  was contructed on with Form.BeginInvoke

                }
            }
        }
    }

En la actualidad, existen métodos mucho más simples y modernos para la comunicación entre procesos. en particular, echa un vistazo a WCF.

Aunque admitiré que hay una pequeña curva de aprendizaje. Una vez que lo descubres, es realmente muy fácil. Incluso puede hacerlo todo mediante programación para que no tenga que preocuparse por las confusiones de configuración.

Parece que falta un ejemplo completo. Luché un poco para que se ejecutara un ejemplo de trabajo. Así que esta es mi implementación mínima de la aplicación de instancia única cuando se ejecuta varias veces, entrega el mensaje (primer argumento de línea de comando) a la primera instancia. Si alguien como yo necesita un ejemplo de trabajo completo, este es un gran comienzo:

using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;

namespace MutexApp
{
    class Program
    {
        private const string PIPE_NAME = "MY_PIPE"; // Name of pipe
        private const string MUTEX_NAME = "MY_MUTEX"; // Mutex name

        static void Main(string[] args)
        {
            string message = "NO MESSAGE";
            if (args.Length > 0) // If we have a parameter to the .exe get it
                message = args[0];
            bool firstinstance = false;
            Mutex mutex = new Mutex(true, MUTEX_NAME, out firstinstance);
            if (firstinstance) // We are the first instance of this process
            {
                Console.WriteLine("First instance started");
                Console.WriteLine("Message: " + message);
                Console.WriteLine("Waiting for messages (ctrl+c to break)...");

                while (true) { ProcessNextClient(); } // Unfinite loop that listens for messages by new clients
            }
            else // This process is already running, parse message to the running instance and exit
            {
                {
                    try
                    {
                        using (NamedPipeClientStream client = new NamedPipeClientStream(PIPE_NAME)) // Create connection to pipe
                        {
                            client.Connect(5000); // Maximum wait 5 seconds
                            using (StreamWriter writer = new StreamWriter(client))
                            {
                                writer.WriteLine(message); // Write command line parameter to the first instance
                            }
                        }
                    } catch (Exception ex)
                    {
                        Console.WriteLine("Error: "+ex.Message);
                    }
                }
            }
            mutex.Dispose();
        }

        private static void ProcessNextClient()
        {
            try
            {
                NamedPipeServerStream pipeStream = new NamedPipeServerStream(PIPE_NAME, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances); // Create Server pipe and listen for clients
                pipeStream.WaitForConnection(); // Wait for client connection

                using (StreamReader reader = new StreamReader(pipeStream)) // Read message from pipe stream
                {
                    string message = reader.ReadLine();
                    Console.WriteLine("At " + DateTime.Now.ToLongTimeString()+": " + message); // Print message on screen
                }
            }
            catch (Exception e)
            {
                Console.WriteLine("Error: " + ex.Message);
            }
        }
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top