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

StackOverflow https://stackoverflow.com/questions/1214638

  •  06-07-2019
  •  | 
  •  

Вопрос

У меня есть приложение WinForms, где, если уже запущен экземпляр и пользователь пытается запустить другой, я останавливаю его, проверяя на соответствие мьютексу перед вызовом 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);
}
Это было полезно?

Решение

Грег,

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

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

Скотт

Другие советы

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

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

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

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

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

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

                }
            }
        }
    }

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

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

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

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);
            }
        }
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top