관리 된 앱의 한 인스턴스에서 다른 인스턴스로 메시지를 보내는 방법은 무엇입니까?

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

  •  06-07-2019
  •  | 
  •  

문제

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);
}
도움이 되었습니까?

해결책

그렉,

StringToHglobalauto가 만든 관리되지 않은 포인터는이를 생성 한 프로세스 공간에서만 유효합니다. 다른 프로세스에서는 참조 된 메모리에 액세스 할 수 없습니다.

한 앱에서 다른 앱으로 데이터를 전달하려면 wm_copydata 메시지와 함께 SendMessage ()를 사용하십시오.

스콧

다른 팁

코드를 다시 확인하십시오. StringToHglobalAuto를 사용하여 문자열을 생성합니다 (유니 코드로 끝날 수 있음). 그런 다음 유니 코드를 사용하지 않는 Ptrtostringansi를 호출합니다.

이 솔루션이 작동하지 않으면 몇 가지 옵션이 있습니다. IPC를 찾아서 그들에 대해 읽을 수 있습니다 (통신 통신).

빠르고 쉽기 때문에 내가 사용한 한 가지 방법은 필요한 콘텐츠가있는 잘 알려진 파일을 만드는 것입니다. 이름이 명명 된 이벤트와 함께 해당 파일의 사용을 잠그고 "소유자"앱에 다른 이름의 이벤트를 설정하여 파일이 변경되었다고 말합니다. "소유자"는 때때로 이벤트를 확인하거나 작업자 스레드를 시작하여이를 관찰합니다.

다시 말하지만, 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