Как я могу вызвать метод в другом экземпляре процесса моего приложения WinForms?

Я работаю над приложением, которое использует Mutex, чтобы гарантировать, что это единственный экземпляр приложения, работающий в системе.

Когда другой экземпляр приложения пытается запуститься, я хочу, чтобы метод запускался в исходном экземпляре.

Могу ли я вызвать определенный метод в моем приложении из другого экземпляра приложения?

Я нашел несколько примеров использования Win32 API-интерфейсов RegisterWindowMessage / PostMessage, отправив сообщение на HWND_BROADCAST, но мне не удалось заставить их работать, и я читал в другом месте, что использование HWND_BROADCAST может быть опасным.

Есть ли лучший способ сделать это, чтобы приложение не запускалось в привилегированном режиме?


person Daniel Schaffer    schedule 26.10.2011    source источник
comment
Если вам нужен хакерский подход (и я имею в виду хакерский), когда запускается второй экземпляр приложения, создайте фиктивный файл в каком-то временном месте и попросите приложение использовать FileSystemWatcher для отслеживания этого временного местоположения для нового созданный файл. Когда он обнаруживает, что был создан новый файл, сделайте то, что вам нужно сделать :)   -  person BFree    schedule 26.10.2011


Ответы (2)


Я уже исследовал это раньше - вы можете использовать файл с отображением памяти, продемонстрированный в этой статье http://www.codeproject.com/KB/cs/singleinstanceapplication.aspx, или вы можете сделать то, что сделал я (простой способ), и воспользоваться преимуществами функций vb.net (в частности, той, которая позволяет вам создавать приложения с одним экземпляром и вызывает метод в текущем запущенном экземпляре, который передает аргументы командной строки [чтобы вы могли использовать его для вызова метода в вашем приложении]). Я знаю, что использование классов VB в C # звучит плохо, но это наиболее абстрактный и простой способ. Ссылка на соответствующие статьи - http://www.codeproject.com/KB/cs/CSSIApp.aspx, последняя часть http://msdn.microsoft.com/en-us/magazine/cc163741.aspx

person Zhanger    schedule 26.10.2011
comment
Эта техника работает ... и работает хорошо. Я использовал его для вызова работающего экземпляра из меню «Недавние» моего приложения, когда он закреплен на панели приложения Win7. - person AlfredBr; 26.10.2011
comment
да, это довольно простой, но эффективный способ, но использование классов vb = / для некоторых людей (включая меня) - person Zhanger; 26.10.2011
comment
Я бы использовал NamedPipes, так как они не требуют PInvoking и запускаются на C # без дополнительных ссылок. Принцип такой же, как и в первой статье кодового проекта, упомянутой выше, за исключением того, что вы действительно можете оптимизировать код с помощью NamedPipes. Этой статье 6 лет ... - person BenSwayne; 26.10.2011

Вот маленький помощник, который я написал.

Чтобы использовать это:

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

Из другого процесса:

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

Обратите внимание, что он использует полное имя PipeListener как имя канала по умолчанию. Если вам нужно быть более осторожным, используйте перегрузку конструктора, которая принимает имя канала.

Вот класс:

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();
            }
        }
    }
}
person Daniel Schaffer    schedule 26.10.2011