我正在开发一个使用Mutex的应用程序,以确保它是系统上运行的应用程序的唯一实例。
当另一个应用程序实例尝试启动时,我想要一个方法在原始实例中运行。
我可以从应用程序的另一个实例调用我的应用程序中的特定方法吗?
我发现了一些使用RegisterWindowMessage / PostMessage Win32 API的示例,它们将消息发送到HWND_BROADCAST,但我无法让它们工作,我在其他地方读过使用HWND_BROADCAST可能很危险。
有没有更好的方法来执行此操作,而不涉及需要在特权模式下运行的应用程序?
答案 0 :(得分:6)
这是我写的小帮手。
使用它:
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();
}
}
}
}
答案 1 :(得分:3)
我之前已经对此进行了研究 - 您可以使用内存映射文件,在本文http://www.codeproject.com/KB/cs/singleinstanceapplication.aspx中进行了演示,或者您可以执行我所做的(简单方法)并利用vb.net功能(具体来说,允许您创建单实例应用程序并调用当前正在运行的实例中传递命令行参数的方法[因此您可以使用它来调用应用程序中的方法])。我知道在C#中使用VB类听起来有点差,但这是最抽象和简单的方法。链接到相关文章 - http://www.codeproject.com/KB/cs/CSSIApp.aspx,http://msdn.microsoft.com/en-us/magazine/cc163741.aspx的最后一部分