Domanda

Ho un'app WinForms, dove se esiste già un'istanza con & amp; l'utente prova a crearne un altro, lo interrompo controllando un Mutex prima di chiamare Application.Run (). Quella parte funziona bene. Quello che vorrei fare è passare un messaggio dalla nuova istanza dell'app (insieme a un pezzo di dati in forma di stringa) all'istanza esistente prima di terminare il nuovo processo.

Ho provato a chiamare PostMessage e ricevo il messaggio sull'app in esecuzione, ma la stringa che passo con lparam non riesce (sì, ho verificato per essere sicuro di passare una buona stringa a iniziare con). Come posso farlo al meglio?

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

Altrove, nella mia forma ...

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);
}
È stato utile?

Soluzione

Greg,

Il puntatore non gestito creato da StringToHGlobalAuto è valido solo nello spazio del processo che lo ha creato. La memoria a cui fa riferimento non è accessibile dall'altro processo.

Per passare i dati da un'app a un'altra, utilizzare SendMessage () con il messaggio WM_COPYDATA.

Scott

Altri suggerimenti

Controlla di nuovo il tuo codice. Stai usando StringToHGlobalAuto per creare la stringa (probabilmente finendo con Unicode). Quindi, stai chiamando PtrToStringAnsi, che non utilizza unicode.

Se non riesci a far funzionare questa soluzione, ci sono diverse opzioni. Puoi leggerli cercando IPC (InterProcess Communication.)

Un metodo che ho usato, dato che è stato semplice e veloce, è quello di creare un file ben noto con il contenuto necessario. Blocchi l'uso di quel file con eventi denominati e dici al "proprietario" app che il file è stato modificato impostando un altro evento denominato. Il "proprietario" controlla l'evento occasionalmente o avvia un thread di lavoro per controllarlo.

Ancora una volta, IPC ha molti gusti, se queste idee non funzionano, continua a cercare.

Ecco un modo semplice. Non ho eseguito il codice ma hai capito

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

                }
            }
        }
    }

Al giorno d'oggi esistono metodi molto più semplici e moderni per effettuare comunicazioni tra processi. in particolare, controlla WCF.

Anche se ammetterò che c'è una piccola curva di apprendimento. Una volta capito, è davvero molto facile. Puoi persino fare tutto a livello di programmazione in modo da non doverti preoccupare di eventuali confusioni di configurazione.

Sembra che manchi un esempio completo. Ho faticato un po 'per ottenere un esempio funzionante. Quindi questa è la mia implementazione minima dell'applicazione a istanza singola quando eseguita più volte, recapita il messaggio (primo argomento della riga di comando) alla prima istanza. Se qualcuno come me ha bisogno di un esempio di lavoro completo, questo è un ottimo inizio:

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);
            }
        }
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top