Frage

ich eine WinForms-Anwendung haben, wo, wenn es bereits eine Instanz ausgeführt und der Benutzer versucht, einen anderen zu drehen, ich aufhören es durch Überprüfung gegen einen Mutex vor Application.Run () aufrufen. Das Teil funktioniert gut. Was ich möchte, ist zu tun, um eine Nachricht von der neuen Instanz der App übergeben (zusammen mit einem Stück von Daten in String-Form) an die vorhandenen Instanz vor den neuen Prozess zu töten.

Ich habe versucht Aufruf Postmessage, und ich die Nachricht auf dem laufende App erhalten, aber die Saite, die ich zusammen in der lparam passieren versagt (ja, ich überprüft haben, um sicherzustellen, dass ich in einem guten String bin vorbei an anfangen mit). Wie kann ich das am besten tun?

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

An anderer Stelle in meinem Formular ...

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);
}
War es hilfreich?

Lösung

Greg,

Der nicht verwaltete Zeiger von StringToHGlobalAuto erstellt ist in dem Prozessraum nur gültig, die es erstellt. Die Speicher es Referenzen können nicht von dem anderen Prozess zugegriffen werden.

Um Daten von einer Anwendung zu einer anderen übergeben, verwenden Sendmessage () mit der WM_COPYDATA Nachricht.

Scott

Andere Tipps

Überprüfen Sie den Code erneut. Sie verwenden StringToHGlobalAuto die Zeichenfolge (wahrscheinlich mit Unicode enden) zu erstellen. Dann Sie anrufen PtrToStringAnsi, die nicht Unicode verwendet wird.

Wenn Sie diese Lösung nicht zum Laufen bringen können, gibt es mehrere Möglichkeiten. Sie können über sie von der Suche nach IPC lesen (Interprocess Communication).

Eine Methode, die ich verwendet habe, da es schnell und einfach war, ist eine bekannte Datei mit dem benötigten Inhalt zu erstellen. Sie sperren Verwendung dieser Datei mit dem Namen Ereignisse, und sagen Sie der „Besitzer“ app, dass die Datei, indem Sie einen anderen Namen Ereignis geändert hat. Der „Eigentümer“ checkes das Ereignis gelegentlich oder startet einen Worker-Thread für ihn zu beobachten.

Auch hier IPC viele Aromen hat, wenn diese Ideen nicht funktionieren, suchen Sie weiter.

Hier ist eine einfache Art und Weise. Ich habe nicht den Code ausführen, aber Sie bekommen die Idee

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

                }
            }
        }
    }

Es gibt viel einfachere und moderne Methoden zu tun in diesen Tagen prozessübergreifende Kommunikation. insbesondere Besuche WCF.

Obwohl ich zugeben, dass es ein klitzekleines bisschen eine Lernkurve. Sobald Sie es herausfinden, es ist wirklich sehr einfach. Sie können es auch tun alle programmatisch so dass Sie nicht über alle Konfigurations Verwirrungen sorgen.

Es scheint ein vollständiges Beispiel fehlt. Ich kämpfte ein wenig läuft ein funktionierendes Beispiel zu erhalten. Das ist also meine minimale Umsetzung der einzelnen Instanz Anwendung bei der Ausführung mehrfach, liefert die Nachricht (erstes Kommandozeilenargument) an der ersten Instanz. Wenn jemand wie ich ein volles Arbeitsbeispiel braucht, ist dies ein guter Start:

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);
            }
        }
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top