在。net下使用c#和WPF(而不是Windows窗体或控制台),创建一个只能作为单个实例运行的应用程序的正确方法是什么?
我知道它与某种叫做互斥的神秘事物有关,我很少能找到有人费心停下来解释其中一个是什么。
代码还需要通知已经运行的实例,用户试图启动第二个实例,如果存在命令行参数,还可能传递任何命令行参数。
在。net下使用c#和WPF(而不是Windows窗体或控制台),创建一个只能作为单个实例运行的应用程序的正确方法是什么?
我知道它与某种叫做互斥的神秘事物有关,我很少能找到有人费心停下来解释其中一个是什么。
代码还需要通知已经运行的实例,用户试图启动第二个实例,如果存在命令行参数,还可能传递任何命令行参数。
当前回答
简单地使用一个StreamWriter,怎么样?
System.IO.File.StreamWriter OpenFlag = null; //globally
and
try
{
OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
Environment.Exit(0);
}
其他回答
简单地使用一个StreamWriter,怎么样?
System.IO.File.StreamWriter OpenFlag = null; //globally
and
try
{
OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
Environment.Exit(0);
}
MSDN实际上有一个c#和VB的示例应用程序可以做到这一点:http://msdn.microsoft.com/en-us/library/ms771662(v=VS.90).aspx
The most common and reliable technique for developing single-instance detection is to use the Microsoft .NET Framework remoting infrastructure (System.Remoting). The Microsoft .NET Framework (version 2.0) includes a type, WindowsFormsApplicationBase, which encapsulates the required remoting functionality. To incorporate this type into a WPF application, a type needs to derive from it, and be used as a shim between the application static entry point method, Main, and the WPF application's Application type. The shim detects when an application is first launched, and when subsequent launches are attempted, and yields control the WPF Application type to determine how to process the launches.
For C# people just take a deep breath and forget about the whole 'I don't wanna include VisualBasic DLL'. Because of this and what Scott Hanselman says and the fact that this pretty much is the cleanest solution to the problem and is designed by people who know a lot more about the framework than you do. From a usability standpoint the fact is if your user is loading an application and it is already open and you're giving them an error message like 'Another instance of the app is running. Bye' then they're not gonna be a very happy user. You simply MUST (in a GUI application) switch to that application and pass in the arguments provided - or if command line parameters have no meaning then you must pop up the application which may have been minimized.
这个框架已经支持这个功能了——只是有些白痴把DLL命名为Microsoft罢了。VisualBasic,它没有被放到微软。ApplicationUtils之类的。克服它——或者打开Reflector。
提示:如果你完全按原样使用这种方法,并且你已经有了一个带有资源等的App.xaml,你也会想要看看这个。
这是我的两分钱
static class Program
{
[STAThread]
static void Main()
{
bool createdNew;
using (new Mutex(true, "MyApp", out createdNew))
{
if (createdNew) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainClass = new SynGesturesLogic();
Application.ApplicationExit += mainClass.tray_exit;
Application.Run();
}
else
{
var current = Process.GetCurrentProcess();
foreach (var process in Process.GetProcessesByName(current.ProcessName).Where(process => process.Id != current.Id))
{
NativeMethods.SetForegroundWindow(process.MainWindowHandle);
break;
}
}
}
}
}
[我在下面提供了控制台和wpf应用程序的示例代码。]
在创建命名的Mutex实例后,只需检查createdNew变量的值(示例如下!)。
布尔值createdNew将返回false:
如果命名为“YourApplicationNameHere”的互斥锁实例已经存在 在系统某处创建
布尔值createdNew将返回true:
如果这是第一个名为“YourApplicationNameHere”的互斥锁 系统。
控制台应用程序-示例:
static Mutex m = null;
static void Main(string[] args)
{
const string mutexName = "YourApplicationNameHere";
bool createdNew = false;
try
{
// Initializes a new instance of the Mutex class with a Boolean value that indicates
// whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex,
// and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.
using (m = new Mutex(true, mutexName, out createdNew))
{
if (!createdNew)
{
Console.WriteLine("instance is alreday running... shutting down !!!");
Console.Read();
return; // Exit the application
}
// Run your windows forms app here
Console.WriteLine("Single instance app is running!");
Console.ReadLine();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.ReadLine();
}
}
WPF-Example:
public partial class App : Application
{
static Mutex m = null;
protected override void OnStartup(StartupEventArgs e)
{
const string mutexName = "YourApplicationNameHere";
bool createdNew = false;
try
{
// Initializes a new instance of the Mutex class with a Boolean value that indicates
// whether the calling thread should have initial ownership of the mutex, a string that is the name of the mutex,
// and a Boolean value that, when the method returns, indicates whether the calling thread was granted initial ownership of the mutex.
m = new Mutex(true, mutexName, out createdNew);
if (!createdNew)
{
Current.Shutdown(); // Exit the application
}
}
catch (Exception)
{
throw;
}
base.OnStartup(e);
}
protected override void OnExit(ExitEventArgs e)
{
if (m != null)
{
m.Dispose();
}
base.OnExit(e);
}
}
这就是我最终处理这个问题的方式。注意,调试代码仍然在那里进行测试。这段代码在App.xaml.cs文件的OnStartup中。(WPF)
// Process already running ?
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
{
// Show your error message
MessageBox.Show("xxx is already running. \r\n\r\nIf the original process is hung up you may need to restart your computer, or kill the current xxx process using the task manager.", "xxx is already running!", MessageBoxButton.OK, MessageBoxImage.Exclamation);
// This process
Process currentProcess = Process.GetCurrentProcess();
// Get all processes running on the local computer.
Process[] localAll = Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName);
// ID of this process...
int temp = currentProcess.Id;
MessageBox.Show("This Process ID: " + temp.ToString());
for (int i = 0; i < localAll.Length; i++)
{
// Find the other process
if (localAll[i].Id != currentProcess.Id)
{
MessageBox.Show("Original Process ID (Switching to): " + localAll[i].Id.ToString());
// Switch to it...
SetForegroundWindow(localAll[i].MainWindowHandle);
}
}
Application.Current.Shutdown();
}
这可能有我还没有发现的问题。如果我遇到了,我会更新我的答案。