Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
894
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4350
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Как правильно создать приложение с одним экземпляром WPF?
Используя C# и WPF под .NET (а не Windows формы или консоль), как правильно создать приложение, которое можно запустить только в одном экземпляре?
Я знаю, что это как-то связано с какой-то мифической вещью, называемой a mutex, и редко могу найти кого-то, кто потрудился бы остановиться и объяснить, что это такое.
Код должен также сообщить уже запущенному экземпляру, что пользователь попытался запустить второй экземпляр, и, возможно, также передать любые аргументы командной строки, если таковые существуют.
Вот очень хорошая статья , касающаяся решения Mutex. Описанный в статье подход выгоден по двум причинам.
Во-первых, он не требует зависимости от Microsoft.VisualBasic assembly. Если бы мой проект уже имел зависимость от этого assembly, я бы, вероятно, поддержал использование подхода, показанного в другом ответе . Но как бы то ни было, я не использую Microsoft.VisualBasic assembly, и мне бы не хотелось добавлять ненужную зависимость в свой проект.
Во-вторых, в статье показано, как вывести существующий экземпляр приложения на передний план, когда пользователь пытается запустить другой экземпляр. Это очень приятный штрих, который другие решения Mutex, описанные здесь, не затрагивают.
UPDATE
По состоянию на 8/1/2014, статья, на которую я ссылался выше, все еще активна, но блог давно не обновлялся. Это заставляет меня беспокоиться, что в конечном итоге он может исчезнуть, а вместе с ним и предлагаемое решение. Я воспроизводю содержание этой статьи здесь для потомков. Слова принадлежат исключительно владельцу блога на вменяемость свободного кодирования .
Сегодня я хотел рефакторировать какой-то код, который запретил мое приложение от запуска нескольких экземпляров самого себя.
Ранее я использовал System.Diagnostics.Process для поиска экземпляр моего myapp.exe в списке процессов. Пока это работает, оно навлекает на себя много накладных расходов, а мне хотелось чего-нибудь почище.
Зная, что я мог бы использовать mutex для этого (но никогда не делал этого ранее) я решил сократить свой код и упростить свою жизнь.
В классе моего заявления, главное я создал статический имени Mutex :
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
...
}
Наличие именованного mutex позволяет нам стекировать синхронизацию по всему множество потоков и процессов это просто волшебство которое я ищу для.
Mutex.WaitOne имеет перегрузку, которая определяет количество времени для нас ждать. Поскольку мы на самом деле не хотим синхронизировать наш код (больше просто проверить, если он в настоящее время используется) мы используем перегрузку с два параметра: Mutex.WaitOne(timespan timeout, bool exitContext). Wait one возвращает true, если он может войти, и false, если он не был. В этом случае мы вообще не хотим ждать; если наш mutex будет используется, пропустить его и двигаться дальше, так что мы проходим в TimeSpan.Zero (подождите 0 миллисекунды), и установите exitContext в true, чтобы мы могли выйти из контекст синхронизации, прежде чем мы попытаемся получить блокировку на нем. С помощью это, мы обертываем наш код Application.Run внутри чего-то вроде этого:
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
static void Main() {
if(mutex.WaitOne(TimeSpan.Zero, true)) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
mutex.ReleaseMutex();
} else {
MessageBox.Show("only one instance at a time");
}
}
}
Итак, если наше приложение запущено, WaitOne вернет false, и мы получим окно сообщения.
Вместо того, чтобы показывать окно сообщения, я решил использовать немного Win32, чтобы уведомите мой запущенный экземпляр, что кто-то забыл, что это уже было бег (поднимаясь на вершину всех остальных windows). К для достижения этой цели я использовал PostMessage для трансляции пользовательского сообщения каждому окно (пользовательское сообщение было зарегистрировано с RegisterWindowMessage моим запущенным приложением, что означает, что только мое приложение знает, что именно это), то мой второй экземпляр выходит. Запущенный экземпляр приложения получил бы это уведомление и обработал его. Для того, чтобы сделать это, я переехал WndProc в моей основной форме и прислушался к моему обычаю уведомление. Когда я получил это уведомление, я установил форму TopMost свойство к истине, чтобы поднять его на вершину.
Вот что у меня получилось:
- Program.cs
static class Program
{
static Mutex mutex = new Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");
[STAThread]
static void Main() {
if(mutex.WaitOne(TimeSpan.Zero, true)) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
mutex.ReleaseMutex();
} else {
// send our Win32 message to make the currently running instance
// jump on top of all the other windows
NativeMethods.PostMessage(
(IntPtr)NativeMethods.HWND_BROADCAST,
NativeMethods.WM_SHOWME,
IntPtr.Zero,
IntPtr.Zero);
}
}
}
- NativeMethods.cs
// this class just wraps some Win32 stuff that we're going to use
internal class NativeMethods
{
public const int HWND_BROADCAST = 0xffff;
public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");
[DllImport("user32")]
public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32")]
public static extern int RegisterWindowMessage(string message);
}
- Form1.cs (передняя сторона частично)
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
if(m.Msg == NativeMethods.WM_SHOWME) {
ShowMe();
}
base.WndProc(ref m);
}
private void ShowMe()
{
if(WindowState == FormWindowState.Minimized) {
WindowState = FormWindowState.Normal;
}
// get our current "TopMost" value (ours will always be false though)
bool top = TopMost;
// make our form jump to the top of everything
TopMost = true;
// set it back to whatever it was
TopMost = top;
}
}
Вы можете использовать класс Mutex, но вскоре обнаружите, что вам нужно будет реализовать код для передачи аргументов и так далее. Ну, я научился одному трюку при программировании в WinForms году, когда читал книгу Криса Селла . Этот трюк использует логику, которая уже доступна нам в рамках фреймворка. Я не знаю, как вы, но когда я узнаю о том, что я могу повторно использовать в рамках, это обычно тот путь, который я выбираю вместо того, чтобы изобретать колесо. Если, конечно, он не делает все, что я хочу.
Когда я попал в WPF, я придумал способ использовать тот же самый код, но в приложении WPF. Это решение должно отвечать вашим потребностям, основанным на вашем вопросе.
Во-первых, нам нужно создать наш класс приложений. В этом классе мы собираемся переопределить событие OnStartup и создать метод с именем Activate, который будет использоваться позже.
public class SingleInstanceApplication : System.Windows.Application
{
protected override void OnStartup(System.Windows.StartupEventArgs e)
{
// Call the OnStartup event on our base class
base.OnStartup(e);
// Create our MainWindow and show it
MainWindow window = new MainWindow();
window.Show();
}
public void Activate()
{
// Reactivate the main window
MainWindow.Activate();
}
}
Во-вторых, нам нужно будет создать класс, который сможет управлять нашими экземплярами. Прежде чем мы пройдем через это, мы на самом деле собираемся повторно использовать некоторый код, который находится в Microsoft.VisualBasic assembly. Поскольку в этом примере я использую C#, мне пришлось сделать ссылку на assembly. Если вы используете VB.NET, вам ничего не нужно делать. Класс, который мы собираемся использовать, является WindowsFormsApplicationBase и наследует наш менеджер экземпляров от него, а затем использует свойства и события для обработки одного экземпляра.
public class SingleInstanceManager : Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
{
private SingleInstanceApplication _application;
private System.Collections.ObjectModel.ReadOnlyCollection<string> _commandLine;
public SingleInstanceManager()
{
IsSingleInstance = true;
}
protected override bool OnStartup(Microsoft.VisualBasic.ApplicationServices.StartupEventArgs eventArgs)
{
// First time _application is launched
_commandLine = eventArgs.CommandLine;
_application = new SingleInstanceApplication();
_application.Run();
return false;
}
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs eventArgs)
{
// Subsequent launches
base.OnStartupNextInstance(eventArgs);
_commandLine = eventArgs.CommandLine;
_application.Activate();
}
}
В основном, мы используем биты VB для обнаружения одного экземпляра и обработки соответственно. OnStartup будет запущен при загрузке первого экземпляра. OnStartupNextInstance срабатывает при повторном запуске приложения. Как вы можете видеть, я могу добраться до того, что было передано в командной строке через аргументы события. Я установил значение в поле экземпляра. Здесь можно выполнить синтаксический анализ командной строки или передать ее приложению через конструктор и вызов метода Activate.
В-третьих, пришло время создать наш EntryPoint. Вместо того, чтобы создавать новое приложение, как вы обычно делаете, мы собираемся воспользоваться нашим SingleInstanceManager.
public class EntryPoint
{
[STAThread]
public static void Main(string[] args)
{
SingleInstanceManager manager = new SingleInstanceManager();
manager.Run(args);
}
}
Ну, я надеюсь, что вы сможете следить за всем и сможете использовать эту реализацию и сделать ее своей собственной.
Отсюда .
Обычно кросс-процесс Mutex используется для обеспечения возможности одновременного запуска только одного экземпляра программы. Вот как это делается:
class OneAtATimePlease {
// Use a name unique to the application (eg include your company URL)
static Mutex mutex = new Mutex (false, "oreilly.com OneAtATimeDemo");
static void Main()
{
// Wait 5 seconds if contended – in case another instance
// of the program is in the process of shutting down.
if (!mutex.WaitOne(TimeSpan.FromSeconds (5), false))
{
Console.WriteLine("Another instance of the app is running. Bye!");
return;
}
try
{
Console.WriteLine("Running - press Enter to exit");
Console.ReadLine();
}
finally
{
mutex.ReleaseMutex();
}
}
}
Хорошая особенность Mutex заключается в том, что если приложение завершает работу без первого вызова ReleaseMutex, то CLR автоматически освободит Mutex.
MSDN на самом деле имеет пример приложения для C# и VB, чтобы сделать именно это: http://msdn.microsoft.com/en-us/library/ms771662 (v=VS.90).aspx
Самая распространенная и надежная методика для разработки одного экземпляра обнаружение заключается в использовании Microsoft .NET Основу инфраструктуры удаленного (System.Remoting). Microsoft .NET Фреймворк (версия 2.0) включает в себя тип, WindowsFormsApplicationBase, который инкапсулирует необходимые функциональность удаленного доступа. Включать этот тип в приложение WPF, а тип должен выводиться из него и быть используется в качестве прокладки между приложениями статический метод точки входа, основной и приложение WPF приложения тип. Прокладка обнаруживает когда приложение сначала запускается, и когда последующие запуски будут попытка, и выходы контролируют WPF Тип приложения, чтобы определить, как процесс запусков.
- Для C# людей просто сделайте глубокий вдох и забудьте о целом "я не хочу включать VisualBasic DLL". Из-за этого и того, что говорит Скотт Ханселман , и того факта, что это в значительной степени является самым чистым решением проблемы и разработано людьми, которые знают намного больше о фреймворке, чем вы.
- С точки зрения удобства использования дело в том, что если ваш пользователь загружает приложение, и оно уже открыто, и вы даете ему сообщение об ошибке, например
'Another instance of the app is running. Bye', то он не будет очень счастливым пользователем. Вы просто MUST (в приложении GUI) переключаетесь на это приложение и передаете предоставленные аргументы - или если параметры командной строки не имеют значения, то вы должны открыть приложение, которое, возможно, было свернуто.
Фреймворк уже имеет поддержку для этого - просто какой-то идиот назвал DLL Microsoft.VisualBasic , и он не был помещен в Microsoft.ApplicationUtils или что-то в этом роде. Смирись с этим - или открывай рефлектор.
Совет: Если вы используете этот подход именно так, как есть, и у вас уже есть App.xaml с ресурсами и т. д. Вы тоже захотите взглянуть на это .
Этот код должен перейти к основному методу. Смотрите здесь для получения дополнительной информации о главном методе в WPF.
[DllImport("user32.dll")]
private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);
private const int SW_SHOWMAXIMIZED = 3;
static void Main()
{
Process currentProcess = Process.GetCurrentProcess();
var runningProcess = (from process in Process.GetProcesses()
where
process.Id != currentProcess.Id &&
process.ProcessName.Equals(
currentProcess.ProcessName,
StringComparison.Ordinal)
select process).FirstOrDefault();
if (runningProcess != null)
{
ShowWindow(runningProcess.MainWindowHandle, SW_SHOWMAXIMIZED);
return;
}
}
Способ 2
static void Main()
{
string procName = Process.GetCurrentProcess().ProcessName;
// get the list of all processes by that name
Process[] processes=Process.GetProcessesByName(procName);
if (processes.Length > 1)
{
MessageBox.Show(procName + " already running");
return;
}
else
{
// Application.Run(...);
}
}
Примечание: вышеуказанные методы предполагают, что ваш процесс/приложение имеет уникальное имя. Потому что он использует имя процесса, чтобы найти, если какие-либо существующие процессоры. Таким образом, если ваше приложение имеет очень распространенное имя (т. е. Блокнот), вышеописанный подход не будет работать.
Ну, у меня есть одноразовый класс для этого, который легко работает для большинства случаев использования:
Используйте его вот так:
static void Main()
{
using (SingleInstanceMutex sim = new SingleInstanceMutex())
{
if (sim.IsOtherInstanceRunning)
{
Application.Exit();
}
// Initialize program here.
}
}
Вот оно:
/// <summary>
/// Represents a <see cref="SingleInstanceMutex"/> class.
/// </summary>
public partial class SingleInstanceMutex : IDisposable
{
#region Fields
/// <summary>
/// Indicator whether another instance of this application is running or not.
/// </summary>
private bool isNoOtherInstanceRunning;
/// <summary>
/// The <see cref="Mutex"/> used to ask for other instances of this application.
/// </summary>
private Mutex singleInstanceMutex = null;
/// <summary>
/// An indicator whether this object is beeing actively disposed or not.
/// </summary>
private bool disposed;
#endregion
#region Constructor
/// <summary>
/// Initializes a new instance of the <see cref="SingleInstanceMutex"/> class.
/// </summary>
public SingleInstanceMutex()
{
this.singleInstanceMutex = new Mutex(true, Assembly.GetCallingAssembly().FullName, out this.isNoOtherInstanceRunning);
}
#endregion
#region Properties
/// <summary>
/// Gets an indicator whether another instance of the application is running or not.
/// </summary>
public bool IsOtherInstanceRunning
{
get
{
return !this.isNoOtherInstanceRunning;
}
}
#endregion
#region Methods
/// <summary>
/// Closes the <see cref="SingleInstanceMutex"/>.
/// </summary>
public void Close()
{
this.ThrowIfDisposed();
this.singleInstanceMutex.Close();
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
/* Release unmanaged ressources */
if (disposing)
{
/* Release managed ressources */
this.Close();
}
this.disposed = true;
}
}
/// <summary>
/// Throws an exception if something is tried to be done with an already disposed object.
/// </summary>
/// <remarks>
/// All public methods of the class must first call this.
/// </remarks>
public void ThrowIfDisposed()
{
if (this.disposed)
{
throw new ObjectDisposedException(this.GetType().Name);
}
}
#endregion
}
Новое приложение, использующее функции Mutex и IPC, а также передающее любые аргументы командной строки запущенному экземпляру, - это приложение с одним экземпляром WPF .
Код C# .NET одно Экземплярное приложение , которое является ссылкой для отмеченного ответа, является отличным началом.
Однако я обнаружил, что он не очень хорошо обрабатывает случаи, когда экземпляр, который уже существует, имеет открытый модальный диалог, будь то управляемый (Например, другая форма, такая как окно about) или неуправляемый (например, OpenFileDialog даже при использовании стандартного класса .NET). С оригинальным кодом основная форма активируется, но модальная остается неактивна, что выглядит странно, плюс пользователь должен нажать на нее, чтобы продолжать использовать приложение.
Итак, я создал SingleInstance utility class, чтобы обрабатывать все это совершенно автоматически для Winforms и WPF приложений.
Winforms :
1) измените класс программы следующим образом:
static class Program
{
public static readonly SingleInstance Singleton = new SingleInstance(typeof(Program).FullName);
[STAThread]
static void Main(string[] args)
{
// NOTE: if this always return false, close & restart Visual Studio
// this is probably due to the vshost.exe thing
Singleton.RunFirstInstance(() =>
{
SingleInstanceMain(args);
});
}
public static void SingleInstanceMain(string[] args)
{
// standard code that was in Main now goes here
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
2) Измените класс главного окна следующим образом:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void WndProc(ref Message m)
{
// if needed, the singleton will restore this window
Program.Singleton.OnWndProc(this, m, true);
// TODO: handle specific messages here if needed
base.WndProc(ref m);
}
}
WPF:
1) измените страницу приложения следующим образом (и убедитесь, что вы установили его действие сборки на страницу, чтобы иметь возможность переопределить основной метод):
public partial class App : Application
{
public static readonly SingleInstance Singleton = new SingleInstance(typeof(App).FullName);
[STAThread]
public static void Main(string[] args)
{
// NOTE: if this always return false, close & restart Visual Studio
// this is probably due to the vshost.exe thing
Singleton.RunFirstInstance(() =>
{
SingleInstanceMain(args);
});
}
public static void SingleInstanceMain(string[] args)
{
// standard code that was in Main now goes here
App app = new App();
app.InitializeComponent();
app.Run();
}
}
2) Измените класс главного окна следующим образом:
public partial class MainWindow : Window
{
private HwndSource _source;
public MainWindow()
{
InitializeComponent();
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
_source = (HwndSource)PresentationSource.FromVisual(this);
_source.AddHook(HwndSourceHook);
}
protected virtual IntPtr HwndSourceHook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
// if needed, the singleton will restore this window
App.Singleton.OnWndProc(hwnd, msg, wParam, lParam, true, true);
// TODO: handle other specific message
return IntPtr.Zero;
}
А вот и служебный класс:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Threading;
namespace SingleInstanceUtilities
{
public sealed class SingleInstance
{
private const int HWND_BROADCAST = 0xFFFF;
[DllImport("user32.dll")]
private static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern int RegisterWindowMessage(string message);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
public SingleInstance(string uniqueName)
{
if (uniqueName == null)
throw new ArgumentNullException("uniqueName");
Mutex = new Mutex(true, uniqueName);
Message = RegisterWindowMessage("WM_" + uniqueName);
}
public Mutex Mutex { get; private set; }
public int Message { get; private set; }
public void RunFirstInstance(Action action)
{
RunFirstInstance(action, IntPtr.Zero, IntPtr.Zero);
}
// NOTE: if this always return false, close & restart Visual Studio
// this is probably due to the vshost.exe thing
public void RunFirstInstance(Action action, IntPtr wParam, IntPtr lParam)
{
if (action == null)
throw new ArgumentNullException("action");
if (WaitForMutext(wParam, lParam))
{
try
{
action();
}
finally
{
ReleaseMutex();
}
}
}
public static void ActivateWindow(IntPtr hwnd)
{
if (hwnd == IntPtr.Zero)
return;
FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
}
public void OnWndProc(IntPtr hwnd, int m, IntPtr wParam, IntPtr lParam, bool restorePlacement, bool activate)
{
if (m == Message)
{
if (restorePlacement)
{
WindowPlacement placement = WindowPlacement.GetPlacement(hwnd, false);
if (placement.IsValid && placement.IsMinimized)
{
const int SW_SHOWNORMAL = 1;
placement.ShowCmd = SW_SHOWNORMAL;
placement.SetPlacement(hwnd);
}
}
if (activate)
{
SetForegroundWindow(hwnd);
FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(hwnd));
}
}
}
#if WINFORMS // define this for Winforms apps
public void OnWndProc(System.Windows.Forms.Form form, int m, IntPtr wParam, IntPtr lParam, bool activate)
{
if (form == null)
throw new ArgumentNullException("form");
if (m == Message)
{
if (activate)
{
if (form.WindowState == System.Windows.Forms.FormWindowState.Minimized)
{
form.WindowState = System.Windows.Forms.FormWindowState.Normal;
}
form.Activate();
FormUtilities.ActivateWindow(FormUtilities.GetModalWindow(form.Handle));
}
}
}
public void OnWndProc(System.Windows.Forms.Form form, System.Windows.Forms.Message m, bool activate)
{
if (form == null)
throw new ArgumentNullException("form");
OnWndProc(form, m.Msg, m.WParam, m.LParam, activate);
}
#endif
public void ReleaseMutex()
{
Mutex.ReleaseMutex();
}
public bool WaitForMutext(bool force, IntPtr wParam, IntPtr lParam)
{
bool b = PrivateWaitForMutext(force);
if (!b)
{
PostMessage((IntPtr)HWND_BROADCAST, Message, wParam, lParam);
}
return b;
}
public bool WaitForMutext(IntPtr wParam, IntPtr lParam)
{
return WaitForMutext(false, wParam, lParam);
}
private bool PrivateWaitForMutext(bool force)
{
if (force)
return true;
try
{
return Mutex.WaitOne(TimeSpan.Zero, true);
}
catch (AbandonedMutexException)
{
return true;
}
}
}
// NOTE: don't add any field or public get/set property, as this must exactly map to Windows' WINDOWPLACEMENT structure
[StructLayout(LayoutKind.Sequential)]
public struct WindowPlacement
{
public int Length { get; set; }
public int Flags { get; set; }
public int ShowCmd { get; set; }
public int MinPositionX { get; set; }
public int MinPositionY { get; set; }
public int MaxPositionX { get; set; }
public int MaxPositionY { get; set; }
public int NormalPositionLeft { get; set; }
public int NormalPositionTop { get; set; }
public int NormalPositionRight { get; set; }
public int NormalPositionBottom { get; set; }
[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);
[DllImport("user32.dll", SetLastError = true)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WindowPlacement lpwndpl);
private const int SW_SHOWMINIMIZED = 2;
public bool IsMinimized
{
get
{
return ShowCmd == SW_SHOWMINIMIZED;
}
}
public bool IsValid
{
get
{
return Length == Marshal.SizeOf(typeof(WindowPlacement));
}
}
public void SetPlacement(IntPtr windowHandle)
{
SetWindowPlacement(windowHandle, ref this);
}
public static WindowPlacement GetPlacement(IntPtr windowHandle, bool throwOnError)
{
WindowPlacement placement = new WindowPlacement();
if (windowHandle == IntPtr.Zero)
return placement;
placement.Length = Marshal.SizeOf(typeof(WindowPlacement));
if (!GetWindowPlacement(windowHandle, ref placement))
{
if (throwOnError)
throw new Win32Exception(Marshal.GetLastWin32Error());
return new WindowPlacement();
}
return placement;
}
}
public static class FormUtilities
{
[DllImport("user32.dll")]
private static extern IntPtr GetWindow(IntPtr hWnd, int uCmd);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool IsWindowVisible(IntPtr hWnd);
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();
private delegate bool EnumChildrenCallback(IntPtr hwnd, IntPtr lParam);
[DllImport("user32.dll")]
private static extern bool EnumThreadWindows(int dwThreadId, EnumChildrenCallback lpEnumFunc, IntPtr lParam);
private class ModalWindowUtil
{
private const int GW_OWNER = 4;
private int _maxOwnershipLevel;
private IntPtr _maxOwnershipHandle;
private bool EnumChildren(IntPtr hwnd, IntPtr lParam)
{
int level = 1;
if (IsWindowVisible(hwnd) && IsOwned(lParam, hwnd, ref level))
{
if (level > _maxOwnershipLevel)
{
_maxOwnershipHandle = hwnd;
_maxOwnershipLevel = level;
}
}
return true;
}
private static bool IsOwned(IntPtr owner, IntPtr hwnd, ref int level)
{
IntPtr o = GetWindow(hwnd, GW_OWNER);
if (o == IntPtr.Zero)
return false;
if (o == owner)
return true;
level++;
return IsOwned(owner, o, ref level);
}
public static void ActivateWindow(IntPtr hwnd)
{
if (hwnd != IntPtr.Zero)
{
SetActiveWindow(hwnd);
}
}
public static IntPtr GetModalWindow(IntPtr owner)
{
ModalWindowUtil util = new ModalWindowUtil();
EnumThreadWindows(GetCurrentThreadId(), util.EnumChildren, owner);
return util._maxOwnershipHandle; // may be IntPtr.Zero
}
}
public static void ActivateWindow(IntPtr hwnd)
{
ModalWindowUtil.ActivateWindow(hwnd);
}
public static IntPtr GetModalWindow(IntPtr owner)
{
return ModalWindowUtil.GetModalWindow(owner);
}
}
}
Вот пример, который позволяет вам иметь один экземпляр приложения. Когда загружаются новые экземпляры, они передают свои аргументы в запущенный основной экземпляр.
public partial class App : Application
{
private static Mutex SingleMutex;
public static uint MessageId;
private void Application_Startup(object sender, StartupEventArgs e)
{
IntPtr Result;
IntPtr SendOk;
Win32.COPYDATASTRUCT CopyData;
string[] Args;
IntPtr CopyDataMem;
bool AllowMultipleInstances = false;
Args = Environment.GetCommandLineArgs();
// TODO: Replace {00000000-0000-0000-0000-000000000000} with your application's GUID
MessageId = Win32.RegisterWindowMessage("{00000000-0000-0000-0000-000000000000}");
SingleMutex = new Mutex(false, "AppName");
if ((AllowMultipleInstances) || (!AllowMultipleInstances && SingleMutex.WaitOne(1, true)))
{
new Main();
}
else if (Args.Length > 1)
{
foreach (Process Proc in Process.GetProcesses())
{
SendOk = Win32.SendMessageTimeout(Proc.MainWindowHandle, MessageId, IntPtr.Zero, IntPtr.Zero,
Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
2000, out Result);
if (SendOk == IntPtr.Zero)
continue;
if ((uint)Result != MessageId)
continue;
CopyDataMem = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Win32.COPYDATASTRUCT)));
CopyData.dwData = IntPtr.Zero;
CopyData.cbData = Args[1].Length*2;
CopyData.lpData = Marshal.StringToHGlobalUni(Args[1]);
Marshal.StructureToPtr(CopyData, CopyDataMem, false);
Win32.SendMessageTimeout(Proc.MainWindowHandle, Win32.WM_COPYDATA, IntPtr.Zero, CopyDataMem,
Win32.SendMessageTimeoutFlags.SMTO_BLOCK | Win32.SendMessageTimeoutFlags.SMTO_ABORTIFHUNG,
5000, out Result);
Marshal.FreeHGlobal(CopyData.lpData);
Marshal.FreeHGlobal(CopyDataMem);
}
Shutdown(0);
}
}
}
public partial class Main : Window
{
private void Window_Loaded(object sender, RoutedEventArgs e)
{
HwndSource Source;
Source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
Source.AddHook(new HwndSourceHook(Window_Proc));
}
private IntPtr Window_Proc(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, ref bool Handled)
{
Win32.COPYDATASTRUCT CopyData;
string Path;
if (Msg == Win32.WM_COPYDATA)
{
CopyData = (Win32.COPYDATASTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32.COPYDATASTRUCT));
Path = Marshal.PtrToStringUni(CopyData.lpData, CopyData.cbData / 2);
if (WindowState == WindowState.Minimized)
{
// Restore window from tray
}
// Do whatever we want with information
Activate();
Focus();
}
if (Msg == App.MessageId)
{
Handled = true;
return new IntPtr(App.MessageId);
}
return IntPtr.Zero;
}
}
public class Win32
{
public const uint WM_COPYDATA = 0x004A;
public struct COPYDATASTRUCT
{
public IntPtr dwData;
public int cbData;
public IntPtr lpData;
}
[Flags]
public enum SendMessageTimeoutFlags : uint
{
SMTO_NORMAL = 0x0000,
SMTO_BLOCK = 0x0001,
SMTO_ABORTIFHUNG = 0x0002,
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
public static extern uint RegisterWindowMessage(string lpString);
[DllImport("user32.dll")]
public static extern IntPtr SendMessageTimeout(
IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam,
SendMessageTimeoutFlags fuFlags, uint uTimeout, out IntPtr lpdwResult);
}
Просто некоторые мысли: Есть случаи, когда требуется, чтобы только один экземпляр приложения не был "lame", как некоторые хотели бы вам верить. Приложения для баз данных и т.д. на порядок сложнее, если разрешить нескольким экземплярам приложения для одного пользователя доступ к базе данных (вы знаете, все это обновление всех записей, которые открыты в нескольких экземплярах приложения на компьютере пользователя и т. д.). Во - первых, для "столкновения имен" не используйте читаемое человеком имя-используйте вместо него GUID или, еще лучше, GUID + читаемое человеком имя. Шансы на столкновение имен просто исчезли с радаров, и Mutex это не волнует. Как кто-то заметил, атака DOS будет отстойной, но если злоумышленник взял на себя труд получить имя mutex и включить его в свое приложение, вы в любом случае в значительной степени являетесь мишенью и должны будете сделать MUCH больше, чтобы защитить себя, чем просто имя fiddle a mutex. Кроме того, если вы используете вариант: new Mutex (правда, "some GUID plus Name", out AIsFirstInstance), у вас уже есть свой индикатор того, является ли Mutex первым экземпляром.
Так много ответов на такой, казалось бы, простой вопрос. Просто чтобы немного встряхнуть ситуацию, вот мое решение этой проблемы.
Создание Mutex может быть проблематичным, поскольку JIT-er видит, что вы используете его только для небольшой части кода, и хочет отметить его как готовый к сборке мусора. Он в значительной степени хочет перещеголять вас, думая, что вы не собираетесь использовать этот Mutex так долго. На самом деле вы хотите висеть на этом Mutex до тех пор, пока ваше приложение работает. Лучший способ сказать сборщику мусора, чтобы он оставил вас Mutex в покое, - это сказать ему, чтобы он продолжал жить, несмотря на разные поколения гаражной коллекции. Пример:
var m = new Mutex(...);
...
GC.KeepAlive(m);
Я поднял эту идею с этой страницы: http://www.ai.uga.edu/~mc/SingleInstance.html
Похоже, что есть действительно хороший способ справиться с этим:
WPF Приложение С Одним Экземпляром
Это обеспечивает класс, который вы можете добавить, который управляет всеми mutex и сообщениями cruff, чтобы упростить вашу реализацию до точки, где это просто тривиально.
Следующий код-это мое решение WCF named pipes для регистрации приложения с одним экземпляром. Это хорошо, потому что он также вызывает событие, когда другой экземпляр пытается запустить и получает командную строку другого экземпляра.
Он ориентирован на WPF, потому что использует класс System.Windows.StartupEventHandler , но его можно легко изменить.
Этот код требует Ссылки на PresentationFramework и System.ServiceModel .
Использование:
class Program
{
static void Main()
{
var applicationId = new Guid("b54f7b0d-87f9-4df9-9686-4d8fd76066dc");
if (SingleInstanceManager.VerifySingleInstance(applicationId))
{
SingleInstanceManager.OtherInstanceStarted += OnOtherInstanceStarted;
// Start the application
}
}
static void OnOtherInstanceStarted(object sender, StartupEventArgs e)
{
// Do something in response to another instance starting up.
}
}
исходный код:
/// <summary>
/// A class to use for single-instance applications.
/// </summary>
public static class SingleInstanceManager
{
/// <summary>
/// Raised when another instance attempts to start up.
/// </summary>
public static event StartupEventHandler OtherInstanceStarted;
/// <summary>
/// Checks to see if this instance is the first instance running on this machine. If it is not, this method will
/// send the main instance this instance's startup information.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
/// <returns>True if this instance is the main instance.</returns>
public static bool VerifySingleInstace(Guid guid)
{
if (!AttemptPublishService(guid))
{
NotifyMainInstance(guid);
return false;
}
return true;
}
/// <summary>
/// Attempts to publish the service.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
/// <returns>True if the service was published successfully.</returns>
private static bool AttemptPublishService(Guid guid)
{
try
{
ServiceHost serviceHost = new ServiceHost(typeof(SingleInstance));
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
serviceHost.AddServiceEndpoint(typeof(ISingleInstance), binding, CreateAddress(guid));
serviceHost.Open();
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Notifies the main instance that this instance is attempting to start up.
/// </summary>
/// <param name="guid">The application's unique identifier.</param>
private static void NotifyMainInstance(Guid guid)
{
NetNamedPipeBinding binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
EndpointAddress remoteAddress = new EndpointAddress(CreateAddress(guid));
using (ChannelFactory<ISingleInstance> factory = new ChannelFactory<ISingleInstance>(binding, remoteAddress))
{
ISingleInstance singleInstance = factory.CreateChannel();
singleInstance.NotifyMainInstance(Environment.GetCommandLineArgs());
}
}
/// <summary>
/// Creates an address to publish/contact the service at based on a globally unique identifier.
/// </summary>
/// <param name="guid">The identifier for the application.</param>
/// <returns>The address to publish/contact the service.</returns>
private static string CreateAddress(Guid guid)
{
return string.Format(CultureInfo.CurrentCulture, "net.pipe://localhost/{0}", guid);
}
/// <summary>
/// The interface that describes the single instance service.
/// </summary>
[ServiceContract]
private interface ISingleInstance
{
/// <summary>
/// Notifies the main instance that another instance of the application attempted to start.
/// </summary>
/// <param name="args">The other instance's command-line arguments.</param>
[OperationContract]
void NotifyMainInstance(string[] args);
}
/// <summary>
/// The implementation of the single instance service interface.
/// </summary>
private class SingleInstance : ISingleInstance
{
/// <summary>
/// Notifies the main instance that another instance of the application attempted to start.
/// </summary>
/// <param name="args">The other instance's command-line arguments.</param>
public void NotifyMainInstance(string[] args)
{
if (OtherInstanceStarted != null)
{
Type type = typeof(StartupEventArgs);
ConstructorInfo constructor = type.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
StartupEventArgs e = (StartupEventArgs)constructor.Invoke(null);
FieldInfo argsField = type.GetField("_args", BindingFlags.Instance | BindingFlags.NonPublic);
Debug.Assert(argsField != null);
argsField.SetValue(e, args);
OtherInstanceStarted(null, e);
}
}
}
}
Вы никогда не должны использовать имя mutex для реализации приложения с одним экземпляром (или, по крайней мере, не для производственного кода). Вредоносный код может легко DoS (отказ в обслуживании) вашу задницу...
Вот что я использую. Он объединил перечисление процессов для выполнения переключения и mutex для защиты от " активных кликеров":
public partial class App
{
[DllImport("user32")]
private static extern int OpenIcon(IntPtr hWnd);
[DllImport("user32.dll")]
private static extern bool SetForegroundWindow(IntPtr hWnd);
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var p = Process
.GetProcessesByName(Process.GetCurrentProcess().ProcessName);
foreach (var t in p.Where(t => t.MainWindowHandle != IntPtr.Zero))
{
OpenIcon(t.MainWindowHandle);
SetForegroundWindow(t.MainWindowHandle);
Current.Shutdown();
return;
}
// there is a chance the user tries to click on the icon repeatedly
// and the process cannot be discovered yet
bool createdNew;
var mutex = new Mutex(true, "MyAwesomeApp",
out createdNew); // must be a variable, though it is unused -
// we just need a bit of time until the process shows up
if (!createdNew)
{
Current.Shutdown();
return;
}
new Bootstrapper().Run();
}
}
Я нашел более простое решение, похожее на решение Дейла Рагана, но слегка измененное. Он делает практически все, что вам нужно, и основан на стандартном классе Microsoft WindowsFormsApplicationBase.
Во-первых, вы создаете класс SingleInstanceController, который можно использовать во всех других одно экземплярных приложениях, использующих формы Windows:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;
namespace SingleInstanceController_NET
{
public class SingleInstanceController
: WindowsFormsApplicationBase
{
public delegate Form CreateMainForm();
public delegate void StartNextInstanceDelegate(Form mainWindow);
CreateMainForm formCreation;
StartNextInstanceDelegate onStartNextInstance;
public SingleInstanceController(CreateMainForm formCreation, StartNextInstanceDelegate onStartNextInstance)
{
// Set whether the application is single instance
this.formCreation = formCreation;
this.onStartNextInstance = onStartNextInstance;
this.IsSingleInstance = true;
this.StartupNextInstance += new StartupNextInstanceEventHandler(this_StartupNextInstance);
}
void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
{
if (onStartNextInstance != null)
{
onStartNextInstance(this.MainForm); // This code will be executed when the user tries to start the running program again,
// for example, by clicking on the exe file.
} // This code can determine how to re-activate the existing main window of the running application.
}
protected override void OnCreateMainForm()
{
// Instantiate your main application form
this.MainForm = formCreation();
}
public void Run()
{
string[] commandLine = new string[0];
base.Run(commandLine);
}
}
}
Затем вы можете использовать его в своей программе следующим образом:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using SingleInstanceController_NET;
namespace SingleInstance
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static Form CreateForm()
{
return new Form1(); // Form1 is used for the main window.
}
static void OnStartNextInstance(Form mainWindow) // When the user tries to restart the application again,
// the main window is activated again.
{
mainWindow.WindowState = FormWindowState.Maximized;
}
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
SingleInstanceController controller = new SingleInstanceController(CreateForm, OnStartNextInstance);
controller.Run();
}
}
}
И программа, и решение SingleInstanceController_NET должны ссылаться на Microsoft.VisualBasic . Если вы просто хотите повторно активировать запущенное приложение как обычное окно, когда пользователь пытается перезапустить запущенную программу, второй параметр в SingleInstanceController может быть null. В данном примере окно развернуто.
Посмотрите на следующий код. Это отличное и простое решение для предотвращения нескольких экземпляров приложения WPF.
private void Application_Startup(object sender, StartupEventArgs e)
{
Process thisProc = Process.GetCurrentProcess();
if (Process.GetProcessesByName(thisProc.ProcessName).Length > 1)
{
MessageBox.Show("Application running");
Application.Current.Shutdown();
return;
}
var wLogin = new LoginWindow();
if (wLogin.ShowDialog() == true)
{
var wMain = new Main();
wMain.WindowState = WindowState.Maximized;
wMain.Show();
}
else
{
Application.Current.Shutdown();
}
}
Обновление 2017-01-25. Попробовав несколько вещей, я решил пойти с VisualBasic.dll это проще и работает лучше (по крайней мере, для меня). Я позволяю своему предыдущему ответу быть просто ссылкой...
Так же, как и ссылка, это то, как я сделал без передачи аргументов (что я не могу найти никаких причин для этого... Я имею в виду одно приложение с аргументами, которые должны передаваться из одного экземпляра в другой). Если требуется сопоставление файлов, то для каждого документа должно быть создано приложение (в соответствии со стандартными ожиданиями пользователей). Если вам нужно передать args в существующее приложение, я думаю, что я бы использовал vb dll.
Не передавая args (только одно приложение экземпляра), я предпочитаю не регистрировать новое сообщение окна и не переопределять цикл сообщений, как определено в решении Мэтта Дэвиса. Хотя это не так уж и важно, чтобы добавить VisualBasic dll, но я предпочитаю не добавлять новую ссылку только для создания приложения с одним экземпляром. Кроме того, я предпочитаю инстанцировать новый класс с Main вместо вызова Shutdown from App.Startup override, чтобы обеспечить выход как можно скорее.
В надежде, что кому-нибудь это понравится... или будет немного вдохновлять :-)
Класс запуска проекта должен быть установлен как 'SingleInstanceApp'.
public class SingleInstanceApp
{
[STAThread]
public static void Main(string[] args)
{
Mutex _mutexSingleInstance = new Mutex(true, "MonitorMeSingleInstance");
if (_mutexSingleInstance.WaitOne(TimeSpan.Zero, true))
{
try
{
var app = new App();
app.InitializeComponent();
app.Run();
}
finally
{
_mutexSingleInstance.ReleaseMutex();
_mutexSingleInstance.Close();
}
}
else
{
MessageBox.Show("One instance is already running.");
var processes = Process.GetProcessesByName(Assembly.GetEntryAssembly().GetName().Name);
{
if (processes.Length > 1)
{
foreach (var process in processes)
{
if (process.Id != Process.GetCurrentProcess().Id)
{
WindowHelper.SetForegroundWindow(process.MainWindowHandle);
}
}
}
}
}
}
}
WindowHelper:
using System;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Threading;
namespace HQ.Util.Unmanaged
{
public class WindowHelper
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
Не используя Mutex, хотя, простой ответ:
System.Diagnostics;
...
string thisprocessname = Process.GetCurrentProcess().ProcessName;
if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
return;
Положите его внутрь Program.Main() .
Пример :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
namespace Sample
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
//simple add Diagnostics namespace, and these 3 lines below
string thisprocessname = Process.GetCurrentProcess().ProcessName;
if (Process.GetProcesses().Count(p => p.ProcessName == thisprocessname) > 1)
return;
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Sample());
}
}
}
Вы можете добавить MessageBox.Show в if-оператор и поставить "Application already running".
Это может быть кому-то полезно.
Использовать раствор mutex :
using System;
using System.Windows.Forms;
using System.Threading;
namespace OneAndOnlyOne
{
static class Program
{
static String _mutexID = " // generate guid"
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Boolean _isNotRunning;
using (Mutex _mutex = new Mutex(true, _mutexID, out _isNotRunning))
{
if (_isNotRunning)
{
Application.Run(new Form1());
}
else
{
MessageBox.Show("An instance is already running.");
return;
}
}
}
}
}
Вот легкое решение, которое я использую, которое позволяет приложению вывести уже существующее окно на передний план, не прибегая к пользовательским сообщениям windows или слепому поиску имен процессов.
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
static readonly string guid = "<Application Guid>";
static void Main()
{
Mutex mutex = null;
if (!CreateMutex(out mutex))
return;
// Application startup code.
Environment.SetEnvironmentVariable(guid, null, EnvironmentVariableTarget.User);
}
static bool CreateMutex(out Mutex mutex)
{
bool createdNew = false;
mutex = new Mutex(false, guid, out createdNew);
if (createdNew)
{
Process process = Process.GetCurrentProcess();
string value = process.Id.ToString();
Environment.SetEnvironmentVariable(guid, value, EnvironmentVariableTarget.User);
}
else
{
string value = Environment.GetEnvironmentVariable(guid, EnvironmentVariableTarget.User);
Process process = null;
int processId = -1;
if (int.TryParse(value, out processId))
process = Process.GetProcessById(processId);
if (process == null || !SetForegroundWindow(process.MainWindowHandle))
MessageBox.Show("Unable to start application. An instance of this application is already running.");
}
return createdNew;
}
Правка: вы также можете хранить и инициализировать mutex и createdNew статически, но вам нужно будет явно удалить/освободить mutex, как только вы закончите с ним. Лично я предпочитаю сохранить mutex local, поскольку он будет автоматически удален, даже если приложение закроется, не достигнув конца Main.
Вы также можете использовать среду выполнения CodeFluent , которая является бесплатным набором инструментов. Он предоставляет класс SingleInstance для реализации приложения с одним экземпляром.
Вот то же самое реализовано через Event.
public enum ApplicationSingleInstanceMode
{
CurrentUserSession,
AllSessionsOfCurrentUser,
Pc
}
public class ApplicationSingleInstancePerUser: IDisposable
{
private readonly EventWaitHandle _event;
/// <summary>
/// Shows if the current instance of ghost is the first
/// </summary>
public bool FirstInstance { get; private set; }
/// <summary>
/// Initializes
/// </summary>
/// <param name="applicationName">The application name</param>
/// <param name="mode">The single mode</param>
public ApplicationSingleInstancePerUser(string applicationName, ApplicationSingleInstanceMode mode = ApplicationSingleInstanceMode.CurrentUserSession)
{
string name;
if (mode == ApplicationSingleInstanceMode.CurrentUserSession)
name = $"Local\\{applicationName}";
else if (mode == ApplicationSingleInstanceMode.AllSessionsOfCurrentUser)
name = $"Global\\{applicationName}{Environment.UserDomainName}";
else
name = $"Global\\{applicationName}";
try
{
bool created;
_event = new EventWaitHandle(false, EventResetMode.ManualReset, name, out created);
FirstInstance = created;
}
catch
{
}
}
public void Dispose()
{
_event.Dispose();
}
}
Named-mutex-based подходы не являются кроссплатформенными, поскольку именованные мьютексы не являются глобальными в Mono. Process-enumeration-based подходы не имеют никакой синхронизации и могут привести к неправильному поведению (например, несколько процессов, запущенных одновременно, могут все саморазрываться в зависимости от времени). Windowing-system-based подходы нежелательны в консольном приложении. Это решение, построенное на основе ответа Дивина, решает все эти вопросы:
using System;
using System.IO;
namespace TestCs
{
public class Program
{
// The app id must be unique. Generate a new guid for your application.
public static string AppId = "01234567-89ab-cdef-0123-456789abcdef";
// The stream is stored globally to ensure that it won't be disposed before the application terminates.
public static FileStream UniqueInstanceStream;
public static int Main(string[] args)
{
EnsureUniqueInstance();
// Your code here.
return 0;
}
private static void EnsureUniqueInstance()
{
// Note: If you want the check to be per-user, use Environment.SpecialFolder.ApplicationData instead.
string lockDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
"UniqueInstanceApps");
string lockPath = Path.Combine(lockDir, $"{AppId}.unique");
Directory.CreateDirectory(lockDir);
try
{
// Create the file with exclusive write access. If this fails, then another process is executing.
UniqueInstanceStream = File.Open(lockPath, FileMode.Create, FileAccess.Write, FileShare.None);
// Although only the line above should be sufficient, when debugging with a vshost on Visual Studio
// (that acts as a proxy), the IO exception isn't passed to the application before a Write is executed.
UniqueInstanceStream.Write(new byte[] { 0 }, 0, 1);
UniqueInstanceStream.Flush();
}
catch
{
throw new Exception("Another instance of the application is already running.");
}
}
}
}
Я не могу найти здесь короткое решение , поэтому я надеюсь, что кому-то это понравится:
UPDATED 2018-09-20
(Кстати. вставь код в свой "Program.cs" )
using System.Diagnostics;
static void Main()
{
Process ThisProcess = Process.GetCurrentProcess();
Process[] AllProcesses = Process.GetProcessesByName(ThisProcess.ProcessName);
if (AllProcesses.Length > 1)
{
//Don't put a MessageBox in here because the user could spam this MessageBox.
return;
}
// Необязательный код. Если вы не хотите, чтобы кто-то бежал, вы ".exe" с другим именем:
string exeName = AppDomain.CurrentDomain.FriendlyName;
if (exeName != "the name of you're executable.exe") // If you try that in debug mode, don't forget that u don't use ur normal .exe. Debug uses the .vshost.exe.
{// You can add here a MessageBox if you want. To point users that the name got changed and maybe what the name should be or something like that^^
MessageBox.Show("The executable name should be \"the name of you're executable.exe\"",
"Wrong executable name", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
//Following Code is default code:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}