【问题标题】:How to send a message from one instance of a managed app to another?如何将消息从托管应用程序的一个实例发送到另一个实例?
【发布时间】:2009-07-31 20:14:46
【问题描述】:

我有一个 WinForms 应用程序,如果已经有一个实例正在运行并且用户试图启动另一个实例,我会在调用 Application.Run() 之前通过检查 Mutex 来停止它。那部分工作得很好。我想做的是在终止新进程之前将一条消息从应用程序的新实例(以及一条字符串形式的数据)传递到现有实例。

我尝试调用 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);
}

【问题讨论】:

    标签: c# interop ipc postmessage


    【解决方案1】:

    格雷格,

    StringToHGlobalAuto 创建的非托管指针只在创建它的进程空间中有效。它引用的内存不能被其他进程访问。

    要将数据从一个应用程序传递到另一个应用程序,请将 SendMessage() 与 WM_COPYDATA 消息一起使用。

    斯科特

    【讨论】:

      【解决方案2】:

      再次检查您的代码。您正在使用 StringToHGlobalAuto 创建字符串(可能以 Unicode 结尾)。然后,您调用的是不使用 unicode 的 PtrToStringAnsi。

      如果您无法使此解决方案发挥作用,有多种选择。您可以通过查找 IPC(进程间通信)来了解它们。

      我使用的一种方法是创建一个包含所需内容的知名文件,因为它既快速又简单。您使用命名事件锁定该文件的使用,并通过设置另一个命名事件告诉“所有者”应用该文件已更改。 “所有者”偶尔会检查事件,或者启动工作线程来监视它。

      再次重申,IPC 有多种口味,如果这些想法不起作用,请继续寻找。

      【讨论】:

        【解决方案3】:

        这是一个简单的方法。我没有运行代码,但你明白了

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

        【讨论】:

          【解决方案4】:

          现在有很多更简单和现代的方法来进行跨进程通信。特别是,请查看 WCF。

          虽然我承认有一点点学习曲线。一旦你弄清楚它真的很容易。您甚至可以通过编程方式完成所有操作,因此您不必担心任何配置混乱。

          【讨论】:

            【解决方案5】:

            似乎缺少一个完整的示例。我努力让一个工作示例运行起来。所以这是我多次运行时单实例应用程序的最小实现,将消息(第一个命令行参数)传递给第一个实例。如果像我这样的人需要一个完整的工作示例,这是一个很好的开始:

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

            【讨论】:

              猜你喜欢
              • 2010-10-05
              • 2013-06-02
              • 2017-08-07
              • 2023-01-17
              • 2013-02-02
              • 2013-04-02
              • 1970-01-01
              • 2020-10-16
              • 1970-01-01
              相关资源
              最近更新 更多