Como enviar uma mensagem a partir de uma instância de um aplicativo gerenciado para outro?

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

  •  06-07-2019
  •  | 
  •  

Pergunta

Eu tenho um aplicativo WinForms, onde se já existe uma instância em execução e as tentativas do usuário para girar um outro, eu pará-lo, verificando contra um Mutex antes de chamar Application.Run (). Essa parte funciona muito bem. O que eu gostaria de fazer é passar uma mensagem a partir da nova instância do aplicativo (junto com um pedaço de dados em forma de string) para a instância existente antes de matar o novo processo.

Eu tentei chamar PostMessage, e eu receber a mensagem no aplicativo em execução, mas a corda eu passar ao longo do lparam está falhando (sim, eu verifiquei para ter certeza que estou passando um bom string para começar com). Como posso melhor fazer isso?

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

Por outro lado, na minha 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);
}
Foi útil?

Solução

Greg,

O ponteiro não gerenciado criado por StringToHGlobalAuto é válido apenas no espaço de processo que o criou. A memória que as referências não podem ser acessados ??a partir do outro processo.

Para passar dados de um aplicativo para outro, use SendMessage () com a mensagem WM_COPYDATA.

Scott

Outras dicas

Verifique se o seu código novamente. Você está usando StringToHGlobalAuto para criar a string (provavelmente acabar com Unicode). Então, você está chamando PtrToStringAnsi, que não está usando unicode.

Se você não pode obter esta solução de trabalho, existem várias opções. Você pode ler sobre eles, procurando por IPC (Interprocess Communication).

Um método que eu usei, uma vez que foi rápido e fácil, é criar um arquivo bem conhecido com o conteúdo necessário. Você bloquear o uso desse arquivo com eventos nomeados, e dizer o app "dono" de que o arquivo foi alterado, definindo um outro evento chamado. O "dono" checkes o evento, ocasionalmente, ou lançamentos de um segmento de trabalho para assistir para ele.

Mais uma vez, o IPC tem muitos sabores, se essas idéias não funcionam, continue procurando.

Aqui está uma maneira simples. Eu não executar o código, mas você começa a idéia

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

                }
            }
        }
    }

Há muito mais simples e métodos modernos para fazer a comunicação entre processos estes dias. em particular, veja WCF.

Embora eu admita que há um pouquinho de uma curva de aprendizado. Uma vez que você descobrir isso é realmente muito fácil. Você pode até mesmo fazer tudo isso por meio de programação, assim você não precisa se preocupar com quaisquer confusões de configuração.

Parece um exemplo completo está faltando. Lutei um pouco para obter um exemplo de execução de trabalho. Portanto, esta é minha implementação mínima do aplicativo de instância única, quando executado várias vezes, entrega a mensagem (argumento da linha de primeiro comando) para a primeira instância. Se alguém como eu precisa de um exemplo de trabalho completo, este é um grande começo:

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);
            }
        }
    }
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top