Question

J'ai une application WinForms où, s'il y a déjà une instance en cours d'exécution, & amp; l'utilisateur essaie d'en créer un autre, je l'arrête en vérifiant par un mutex avant d'appeler Application.Run (). Cette partie fonctionne très bien. Ce que je voudrais faire est de transmettre un message de la nouvelle instance de l'application (avec un élément de données sous forme de chaîne) à l'instance existante avant de tuer le nouveau processus.

J'ai essayé d'appeler PostMessage et je reçois le message sur l'application en cours d'exécution, mais la chaîne que je transmets dans le lparam échoue (oui, j'ai vérifié pour passer une bonne chaîne à commencer avec). Comment puis-je le faire au mieux?

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

Ailleurs, sous ma forme ...

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);
}
Était-ce utile?

La solution

Greg,

Le pointeur non géré créé par StringToHGlobalAuto est uniquement valide dans l'espace de processus qui l'a créé. La mémoire référencée n’est pas accessible depuis l’autre processus.

Pour transmettre des données d'une application à une autre, utilisez SendMessage () avec le message WM_COPYDATA.

Scott

Autres conseils

Vérifiez à nouveau votre code. Vous utilisez StringToHGlobalAuto pour créer la chaîne (aboutissant probablement à Unicode). Ensuite, vous appelez PtrToStringAnsi, qui n’utilise pas unicode.

Si cette solution ne fonctionne pas, vous avez plusieurs options. Vous pouvez en savoir plus en recherchant IPC (InterProcess Communication.)

Depuis que c'était rapide et facile, j'ai utilisé une méthode pour créer un fichier bien connu avec le contenu nécessaire. Vous verrouillez l'utilisation de ce fichier avec des événements nommés et vous en informez le propriétaire. app que le fichier a changé en définissant un autre événement nommé. Le " propriétaire " vérifie occasionnellement l'événement ou lance un thread de travail pour le surveiller.

Encore une fois, IPC a de nombreuses variantes, si ces idées ne fonctionnent pas, continuez à chercher.

Voici un moyen simple. Je n'ai pas exécuté le code mais vous avez une idée

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

                }
            }
        }
    }

De nos jours, il existe des méthodes beaucoup plus simples et modernes de communication entre processus. en particulier, consultez WCF.

Bien que je reconnaisse qu’il existe une petite courbe d’apprentissage. Une fois que vous avez compris, c'est vraiment très facile. Vous pouvez même tout faire par programme afin de ne pas avoir à vous soucier des confusions de configuration.

Il semble qu'il manque un exemple complet. J'ai un peu lutté pour obtenir un exemple de travail en cours d'exécution. Il s’agit donc de mon implémentation minimale de l’application à instance unique lorsqu’il est exécuté plusieurs fois et transmet le message (premier argument de ligne de commande) à la première instance. Si quelqu'un comme moi a besoin d'un exemple complet, c'est un bon début:

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);
            }
        }
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top