كيفية إرسال رسالة من مثيل لتطبيق مُدار إلى مثيل آخر؟

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

  •  06-07-2019
  •  | 
  •  

سؤال

لدي تطبيق 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);
}
هل كانت مفيدة؟

المحلول

وجريج،

ومؤشر غير المدارة التي أنشأتها StringToHGlobalAuto صالحا فقط في مساحة العملية التي أنشأتها. لا يمكن الوصول إلى المراجع أن الذاكرة من عملية أخرى.

لتمرير البيانات من تطبيق واحد إلى آخر، استخدم SendMessage () مع رسالة WM_COPYDATA.

وسكوت

نصائح أخرى

تحقق من الرمز الخاص بك مرة أخرى.أنت تستخدم StringToHGlobalAuto لإنشاء السلسلة (من المحتمل أن ينتهي الأمر بـ Unicode).بعد ذلك، تقوم بالاتصال بـ PtrToStringAnsi، الذي لا يستخدم Unicode.

إذا لم تتمكن من تشغيل هذا الحل، فهناك العديد من الخيارات.يمكنك القراءة عنها من خلال البحث عن IPC (InterProcess Communication.)

إحدى الطرق التي استخدمتها، نظرًا لأنها كانت سريعة وسهلة، هي إنشاء ملف معروف بالمحتوى المطلوب.يمكنك قفل استخدام هذا الملف مع الأحداث المسماة، وإخبار تطبيق "المالك" بأن الملف قد تغير عن طريق تعيين حدث مسمى آخر.يتحقق "المالك" من الحدث من حين لآخر، أو يقوم بتشغيل سلسلة عمليات لمراقبته.

مرة أخرى، 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.

وعلى الرغم من أنني سوف نعترف بأن هناك قليلا teensy من منحنى التعلم. بمجرد أنها من أصل الرقم هو حقا من السهل جدا. يمكنك حتى أن تفعل كل شيء برمجيا لذلك لم يكن لديك ما يدعو للقلق أي التباسات التكوين.

ويبدو مثالا كاملا مفقود. أنا ناضلت قليلا للحصول على مثال عمل قيد التشغيل. لذلك هذا هو بلدي تنفيذ الحد الأدنى من تطبيق مثيل واحد عند تشغيل عدة مرات، يسلم الرسالة (الأولى سيطة سطر الأوامر) إلى الدرجة الأولى. إذا كان أي شخص مثلي يحتاج إلى مثال كامل، وهذا هو بداية رائعة:

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