Как отправить сообщение из одного экземпляра управляемого приложения в другой? - PullRequest
1 голос
/ 01 августа 2009

У меня есть приложение WinForms, где, если экземпляр уже запущен, а пользователь пытается запустить другой, я останавливаю его, проверяя Mutex перед вызовом Application.Run (). Эта часть работает просто отлично. Я хотел бы передать сообщение от нового экземпляра приложения (вместе с фрагментом данных в строковой форме) существующему экземпляру перед тем, как завершить новый процесс.

Я пытался вызвать PostMessage, и я получаю сообщение о работающем приложении, но строка, которую я передаю в lparam, не работает (да, я проверил, чтобы убедиться, что я передаю хорошую строку для начинается с). Как мне лучше всего это сделать?

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

В другом месте, в моей форме ...

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

Ответы [ 5 ]

3 голосов
/ 01 августа 2009

Грег

Неуправляемый указатель, созданный StringToHGlobalAuto, действителен только в том пространстве процесса, в котором он был создан. Память, на которую он ссылается, недоступна из другого процесса.

Чтобы передать данные из одного приложения в другое, используйте SendMessage () с сообщением WM_COPYDATA.

Scott

0 голосов
/ 15 марта 2018

Кажется, полный пример отсутствует. Я изо всех сил пытался заставить работать рабочий пример. Так что это моя минимальная реализация приложения с одним экземпляром, когда запускается несколько раз, доставляет сообщение (первый аргумент командной строки) в первый экземпляр. Если кому-то, как я, нужен полный рабочий пример, это отличное начало:

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);
            }
        }
    }
}
0 голосов
/ 01 августа 2009

В наши дни существуют намного более простые и современные методы межпроцессного взаимодействия. в частности, проверьте WCF.

Хотя я признаю, что есть небольшая кривая обучения. Как только вы поймете это, это действительно очень легко. Вы даже можете делать все это программно, поэтому вам не придется беспокоиться о путанице в конфигурации.

0 голосов
/ 01 августа 2009

Вот простой способ. Я не запускал код, но вы поняли

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

                }
            }
        }
    }
0 голосов
/ 01 августа 2009

Проверьте ваш код еще раз. Вы используете StringToHGlobalAuto для создания строки (вероятно, заканчивающейся Unicode). Затем вы вызываете PtrToStringAnsi, который не использует Unicode.

Если вы не можете заставить это решение работать, есть несколько вариантов. Вы можете прочитать о них, ища IPC (InterProcess Communication.)

Один из методов, который я использовал, поскольку он был быстрым и простым, - это создание хорошо известного файла с необходимым содержимым. Вы блокируете использование этого файла с именованными событиями и сообщаете приложению «владельца», что файл изменился, установив другое именованное событие. «Владелец» периодически проверяет событие или запускает рабочий поток для его отслеживания.

Опять же, у IPC есть много вариантов, если эти идеи не сработают, продолжайте искать.

...