简体   繁体   English

如何在WinForms应用程序的另一个流程实例中调用方法?

[英]How can I invoke a method within another process instance of my WinForms app?

I am working on an application that uses a Mutex to ensure that it is the only instance of the application running on the system. 我正在使用一个使用Mutex的应用程序来确保它是系统上运行的应用程序的唯一实例。

When another instance of the application attempts to start, I want a method to run in the original instance. 当另一个应用程序实例尝试启动时,我想要一个方法在原始实例中运行。

Can I invoke a specific method in my application from another instance of the application? 我可以从应用程序的另一个实例调用我的应用程序中的特定方法吗?

I've found some examples using RegisterWindowMessage/PostMessage Win32 APIs by sending the message to HWND_BROADCAST, but I couldn't get them to work, and I've read elsewhere that using HWND_BROADCAST can be dangerous. 我发现了一些使用RegisterWindowMessage / PostMessage Win32 API的示例,通过将消息发送到HWND_BROADCAST,但我无法让它们工作,我在其他地方读过使用HWND_BROADCAST可能很危险。

Is there a better way to do this that doesn't involve the app needing to be run in privileged mode? 有没有更好的方法来做这个不涉及需要在特权模式下运行的应用程序?

Here's a little helper I wrote. 这是我写的一个小帮手。

To use it: 要使用它:

var pipeListener = new NamedPipeListener<String>(); // instantiate an instance
pipeListener.MessageReceived += (sender, e) => MessageBox.Show(e.Message); // when a message is received, show a messagebox with the message
pipeListener.Error += (sender, e) => MessageBox.Show("Error ({0}): {1}", e.ErrorType, e.Exception.Message); // oh noes!
pipeListener.Start(); // when you're ready, start listening

From another process: 从另一个过程:

NamedPipeListener<String>.SendMessage("Howdy howdy howdy");

Note that it uses the full name of the PipeListener as the default name of the pipe. 请注意,它使用PipeListener的全名作为管道的默认名称。 If you need to get more discreet than that, use the constructor overload that takes a pipe name. 如果您需要更加谨慎,请使用带有管道名称的构造函数重载。

Here's the class: 这是班级:

using System;
using System.IO.Pipes;
using System.Runtime.Serialization.Formatters.Binary;

namespace FunWithNamedPipes
{
    /// <summary>Contains event data for <see cref="NamedPipeMessageReceiveHandler{TMessage}" /> events.</summary>
    /// <typeparam name="TMessage"></typeparam>
    public class NamedPipeListenerMessageReceivedEventArgs<TMessage> : EventArgs
    {
        /// <summary>Initializes an instance of <see cref="NamedPipeListenerMessageReceivedEventArgs{TMessage}" /> with the specified <paramref name="message" />.</summary>
        /// <param name="message">The message passed by the event.</param>
        public NamedPipeListenerMessageReceivedEventArgs(TMessage message)
        {
            this.Message = message;
        }

        /// <summary>Gets the message passed by the event.</summary>
        public TMessage Message { get; private set; }
    }

    /// <summary>Contains event data for <see cref="NamedPipeListenerErrorEventHandler" /> events.</summary>
    public class NamedPipeListenerErrorEventArgs : EventArgs
    {
        /// <summary>Initializes an instance of <see cref="NamedPipeListenerErrorEventArgs" /> with the specified <paramref name="errorType" /> and <paramref name="exception" />.</summary>
        /// <param name="errorType">A <see cref="NamedPipeListenerErrorType" /> describing the part of the listener process where the error was caught.</param>
        /// <param name="ex">The <see cref="Exception" /> that was thrown.</param>
        public NamedPipeListenerErrorEventArgs(NamedPipeListenerErrorType errorType, Exception ex)
        {
            this.ErrorType = errorType;
            this.Exception = ex;
        }

        /// <summary>Gets a <see cref="NamedPipeListenerErrorType" /> describing the part of the listener process where the error was caught.</summary>
        public NamedPipeListenerErrorType ErrorType { get; private set; }

        /// <summary>Gets the <see cref="Exception" /> that was caught.</summary>
        public Exception Exception { get; private set; }
    }

    /// <summary>Represents a method that will handle an event where a message is received via named pipes.</summary>
    /// <typeparam name="TMessage">The type of message that will be received.</typeparam>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The event data passed by the event, which includes the message that was received.</param>
    public delegate void NamedPipeMessageReceivedHandler<TMessage>(Object sender, NamedPipeListenerMessageReceivedEventArgs<TMessage> e);

    /// <summary>Represents a method that will handle an event that is fired when an exception is caught.</summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The event data passed by the event, included the error type and exception that was caught.</param>
    public delegate void NamedPipeMessageErrorHandler(Object sender, NamedPipeListenerErrorEventArgs e);

    /// <summary>Includes different types of errors that describe where in the listening process an exception was caught.</summary>
    public enum NamedPipeListenerErrorType : byte
    {
        /// <summary>Indicates that an exception was caught while calling <see cref="NamedPipeServerStream.BeginWaitForConnection" />.</summary>
        BeginWaitForConnection = 1,

        /// <summary>Indicates that an exception was caught while calling <see cref="NamedPipeServerStream.EndWaitForConnection" />.</summary>
        EndWaitForConnection = 2,

        /// <summary>Indicates that an exception was caught while deserializing a message received from the named pipe.</summary>
        DeserializeMessage = 3,

        /// <summary>Indicates that an exception was caught while closing or disposing a used named pipe.</summary>
        CloseAndDisposePipe = 4,

        /// <summary>Indicates that an exception was caught while invoking the <see cref="NamedPipeListener{TMessage}.MessageReceived"/> event.</summary>
        NotifyMessageReceived = 5
    }

    /// <summary>A helper class for sending and receiving messages using named pipes.</summary>
    /// <typeparam name="TMessage">The type of message that will be sent or received.</typeparam>
    public class NamedPipeListener<TMessage> : IDisposable
    {
        /// <summary>Occurs when a message is received.</summary>
        public event NamedPipeMessageReceivedHandler<TMessage> MessageReceived;

        /// <summary>Occurs when an exception is caught.</summary>
        public event NamedPipeMessageErrorHandler Error;

        static readonly String DEFAULT_PIPENAME = typeof(NamedPipeListener<TMessage>).FullName;
        static readonly BinaryFormatter formatter = new BinaryFormatter();

        NamedPipeServerStream pipeServer;

        /// <summary>Initializes a new instance of <see cref="NamedPipeListener{TMessage}" /> using the specified <paramref name="pipeName" />.</summary>
        /// <param name="pipeName">The name of the named pipe that will be used to listen on.</param>
        public NamedPipeListener(String pipeName)
        {
            this.PipeName = pipeName;
        }

        /// <summary>Initializes a new instance of <see cref="NamedPipeListener{TMessage}" /> using the default pipe name.</summary>
        /// <remarks>The default pipe name is the full name of the type of the instance.</remarks>
        public NamedPipeListener()
            : this(DEFAULT_PIPENAME) { }

        /// <summary>The name of the named pipe that will be used to listen on.</summary>
        public String PipeName { get; private set; }

        /// <summary>Starts listening on the named pipe specified for the instance.</summary>
        internal void Start()
        {
            if (pipeServer == null) pipeServer = new NamedPipeServerStream(DEFAULT_PIPENAME, PipeDirection.In, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous);

            try { pipeServer.BeginWaitForConnection(new AsyncCallback(PipeConnectionCallback), null); }
            catch (Exception ex) { this.OnError(NamedPipeListenerErrorType.BeginWaitForConnection, ex); }
        }

        private void PipeConnectionCallback(IAsyncResult result)
        {
            try
            {
                pipeServer.EndWaitForConnection(result);
            }
            catch (Exception ex)
            {
                this.OnError(NamedPipeListenerErrorType.EndWaitForConnection, ex);
                return;
            }

            TMessage message;
            try
            {
                message = (TMessage)formatter.Deserialize(pipeServer);
            }
            catch (Exception ex)
            {
                this.OnError(NamedPipeListenerErrorType.DeserializeMessage, ex);
                return;
            }

            try
            {
                this.OnMessageReceived(new NamedPipeListenerMessageReceivedEventArgs<TMessage>(message));
            }
            catch (Exception ex)
            {
                this.OnError(NamedPipeListenerErrorType.NotifyMessageReceived, ex);
                return;
            }

            if (this.End())
            {
                this.Start();
            }
        }

        internal Boolean End()
        {
            try
            {
                pipeServer.Close();
                pipeServer.Dispose();
                pipeServer = null;

                return true;
            }
            catch (Exception ex)
            {
                this.OnError(NamedPipeListenerErrorType.CloseAndDisposePipe, ex);
                return false;
            }
        }

        private void OnMessageReceived(TMessage message)
        {
            this.OnMessageReceived(new NamedPipeListenerMessageReceivedEventArgs<TMessage>(message));
        }

        protected virtual void OnMessageReceived(NamedPipeListenerMessageReceivedEventArgs<TMessage> e)
        {
            if (this.MessageReceived != null)
            {
                this.MessageReceived(this, e);
            }
        }

        private void OnError(NamedPipeListenerErrorType errorType, Exception ex)
        {
            this.OnError(new NamedPipeListenerErrorEventArgs(errorType, ex));
        }

        protected virtual void OnError(NamedPipeListenerErrorEventArgs e)
        {
            if (this.Error != null)
            {
                this.Error(this, e);
            }
        }

        void IDisposable.Dispose()
        {
            if(pipeServer != null)
            {
                try { pipeServer.Disconnect(); }
                catch { }

                try { pipeServer.Close(); }
                catch { }

                try { pipeServer.Dispose(); }
                catch { }
            }
        }

        /// <summary>Sends the specified <paramref name="message" /> to the default named pipe for the message.</summary>        
        /// <param name="message">The message to send.</param>
        public static void SendMessage(TMessage message)
        {
            NamedPipeListener<TMessage>.SendMessage(DEFAULT_PIPENAME, message);
        }

        /// <summary>Sends the specified <paramref name="message" /> to the specified named pipe.</summary>
        /// <param name="pipeName">The name of the named pipe the message will be sent to.</param>
        /// <param name="message">The message to send.</param>
        public static void SendMessage(String pipeName, TMessage message)
        {
            using (var pipeClient = new NamedPipeClientStream(".", DEFAULT_PIPENAME, PipeDirection.Out, PipeOptions.None))
            {
                pipeClient.Connect();

                formatter.Serialize(pipeClient, message);
                pipeClient.Flush();

                pipeClient.WaitForPipeDrain();
                pipeClient.Close();
            }
        }
    }
}

I've done research on this before - you can use a memory mapped file, demonstrated in this article http://www.codeproject.com/KB/cs/singleinstanceapplication.aspx , or you can do what I did (the easy way) and take advantage of vb.net features (specifically, one that lets you make single instance apps and calls a method in the currently running instance that passes on command line args [so you could use it to invoke the method in your application]). 我之前已经对此进行过研究 - 您可以使用内存映射文件,在本文中演示http://www.codeproject.com/KB/cs/singleinstanceapplication.aspx ,或者您可以做我做的事情(简单的方法)并利用vb.net功能(特别是,允许​​您创建单实例应用程序并在当前运行的实例中调用方法,该方法传递命令行参数[因此您可以使用它来调用应用程序中的方法]) 。 I know using VB classes in C# sounds a bit poor but it's the most abstract and easy way. 我知道在C#中使用VB类听起来有点差,但这是最抽象和简单的方法。 Link to the relevant articles - http://www.codeproject.com/KB/cs/CSSIApp.aspx , last part of http://msdn.microsoft.com/en-us/magazine/cc163741.aspx 链接到相关的文章- http://www.codeproject.com/KB/cs/CSSIApp.aspx的最后一部分http://msdn.microsoft.com/en-us/magazine/cc163741.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM