代码之家  ›  专栏  ›  技术社区  ›  Greg Hurlman

如何从托管应用程序的一个实例向另一个实例发送消息?

  •  1
  • Greg Hurlman  · 技术社区  · 15 年前

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

    我试过调用postmessage,我确实在running应用程序上收到了消息,但是我在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 回复  |  直到 7 年前
        1
  •  3
  •   ScottTx    15 年前

    格雷戈

    由StringTohGlobalAuto创建的非托管指针仅在创建它的进程空间中有效。它引用的内存无法从其他进程访问。

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

    斯科特

        2
  •  0
  •   John Fisher    15 年前

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

    如果您不能使这个解决方案工作,有几个选项。您可以通过查找IPC(进程间通信)来阅读它们。

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

    同样,IPC有很多风格,如果这些想法不起作用,继续寻找。

        3
  •  0
  •   csaam    15 年前

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

    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
  •  0
  •   Joel Martinez    15 年前

    现在有很多简单而现代的跨过程通信方法。尤其是查看wcf。

    尽管我承认有一个很小的学习曲线。一旦你弄明白了,这真的很容易。您甚至可以通过编程来完成所有这些工作,这样就不必担心任何配置混乱。

        5
  •  0
  •   nivs1978    7 年前

    似乎缺少一个完整的例子。我费了点劲才找到一个有效的例子。所以这是我对单实例应用程序的最小实现,当运行多次时,将消息(第一个命令行参数)传递给第一个实例。如果像我这样的人需要一个完整的工作示例,这是一个很好的开始:

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