У .NET, який найкращий спосіб запобігти запуску кількох примірників програми одночасно? І якщо немає «найкращої» методики, які застереження слід враховувати при кожному вирішенні?
У .NET, який найкращий спосіб запобігти запуску кількох примірників програми одночасно? І якщо немає «найкращої» методики, які застереження слід враховувати при кожному вирішенні?
Відповіді:
Використовуйте Mutex. Один із наведених вище прикладів використання GetProcessByName має багато застережень. Ось хороша стаття на цю тему:
http://odetocode.com/Blogs/scott/archive/2004/08/20/401.aspx
[STAThread]
static void Main()
{
using(Mutex mutex = new Mutex(false, "Global\\" + appGuid))
{
if(!mutex.WaitOne(0, false))
{
MessageBox.Show("Instance already running");
return;
}
Application.Run(new Form1());
}
}
private static string appGuid = "c0a76b5a-12ab-45c5-b9d9-d693faa6e7b9";
string appGuid = ((GuidAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(GuidAttribute), true)[0]).Value;що отримає інструкцію виконавчої збірки
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length > 1)
{
AppLog.Write("Application XXXX already running. Only one instance of this application is allowed", AppLog.LogMessageType.Warn);
return;
}
Ось код, який потрібно переконатися, що працює лише один екземпляр. Це метод використання названого мютексу.
public class Program
{
static System.Threading.Mutex singleton = new Mutex(true, "My App Name");
static void Main(string[] args)
{
if (!singleton.WaitOne(TimeSpan.Zero, true))
{
//there is already another instance running!
Application.Exit();
}
}
}
Hanselman має пост про використання класу WinFormsApplicationBase з складання Microsoft.VisualBasic для цього.
Здається, що до цих пір були запропоновані 3 основні методи.
Будь-які застереження я пропустив?
1 - Створіть посилання в program.cs ->
using System.Diagnostics;
2 - Введіть void Main()як перший рядок коду ->
if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Length >1)
return;
Це воно.
Mutex? Чи є улов?
Використовуючи Visual Studio 2005 або 2008 під час створення проекту для виконуваного файлу, на вікнах властивостей на панелі "Програма" є прапорець під назвою "Зробити єдиний екземпляр програми", який можна активувати для перетворення програми в програму для одного примірника .
Ось захоплення вікна, про яке я говорю:
Це проект додатків Visual Studio 2008.
Я спробував усі тут рішення, і нічого не працювало в моєму проекті C # .net 4.0. Сподіваючись допомогти комусь тут рішення, яке працювало для мене:
Як основні змінні класу:
private static string appGuid = "WRITE AN UNIQUE GUID HERE";
private static Mutex mutex;
Коли вам потрібно перевірити, чи програма вже запущена:
bool mutexCreated;
mutex = new Mutex(true, "Global\\" + appGuid, out mutexCreated);
if (mutexCreated)
mutex.ReleaseMutex();
if (!mutexCreated)
{
//App is already running, close this!
Environment.Exit(0); //i used this because its a console app
}
Мені потрібно було закрити інші речі лише за певних умов, це добре спрацювало з моєю метою
http://en.csharp-online.net/Application_Architecture_in_Windows_Forms_2.0 —Single-Instance_Detection_and_Management
Після спробу декількох рішень я питання. Тут я застосував приклад для WPF : http://www.c-sharpcorner.com/UploadFile/f9f215/how-to-restrict-the-application-to-just-one-instance/
public partial class App : Application
{
private static Mutex _mutex = null;
protected override void OnStartup(StartupEventArgs e)
{
const string appName = "MyAppName";
bool createdNew;
_mutex = new Mutex(true, appName, out createdNew);
if (!createdNew)
{
//app is already running! Exiting the application
Application.Current.Shutdown();
}
}
}
У App.xaml:
x:Class="*YourNameSpace*.App"
StartupUri="MainWindow.xaml"
Startup="App_Startup"
У цій статті просто пояснено, як можна створити програму Windows з керуванням кількістю її примірників або запустити лише один екземпляр. Це дуже типова потреба у бізнес-додатку. Вже існує маса інших можливих рішень для управління цим.
Це код для VB.Net
Private Shared Sub Main()
Using mutex As New Mutex(False, appGuid)
If Not mutex.WaitOne(0, False) Then
MessageBox.Show("Instance already running", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End If
Application.Run(New Form1())
End Using
End Sub
Це код для C #
private static void Main()
{
using (Mutex mutex = new Mutex(false, appGuid)) {
if (!mutex.WaitOne(0, false)) {
MessageBox.Show("Instance already running", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
Application.Run(new Form1());
}
}
Ви повинні використовувати System.Diagnostics.Process.
Перевірте: http://www.devx.com/tips/Tip/20044
(Примітка. Це цікаве рішення! Це працює, але для досягнення цього використовується поганий дизайн GDI +.)
Поставте зображення у свою програму та завантажте її під час запуску. Тримайте його до тих пір, поки додаток не вийде. Користувач не зможе запустити другу інстанцію. (Звичайно, розчин мютексу набагато чистіший)
private static Bitmap randomName = new Bitmap("my_image.jpg");
Main()методу, який суперечить тому, як має працювати WPF.
[STAThread]
static void Main() // args are OK here, of course
{
bool ok;
m = new System.Threading.Mutex(true, "YourNameHere", out ok);
if (! ok)
{
MessageBox.Show("Another instance is already running.");
return;
}
Application.Run(new Form1()); // or whatever was there
GC.KeepAlive(m); // important!
}
Від: Забезпечення одного примірника програми .NET
та: Одномоментний додаток Mutex
Відповідь така ж, як @Smink та @Imjustpondering з поворотом:
Поширені питання Джон Скіта на C #, щоб дізнатися, чому GC.KeepAlive має значення
Просте використання a StreamWriter, як щодо цього?
System.IO.File.StreamWriter OpenFlag = null; //globally
і
try
{
OpenFlag = new StreamWriter(Path.GetTempPath() + "OpenedIfRunning");
}
catch (System.IO.IOException) //file in use
{
Environment.Exit(0);
}
Зазвичай це робиться з ім'ям Mutex (використовуйте новий Mutex ("ваше ім'я програми", справжнє) і перевірте значення повернення), але в Microsoft.VisualBasic.dll також є кілька класів підтримки, які можуть зробити це за вас .
Це працювало для мене в чистому C #. try / catch - це, можливо, процес у списку закінчується під час циклу.
using System.Diagnostics;
....
[STAThread]
static void Main()
{
...
int procCount = 0;
foreach (Process pp in Process.GetProcesses())
{
try
{
if (String.Compare(pp.MainModule.FileName, Application.ExecutablePath, true) == 0)
{
procCount++;
if(procCount > 1) {
Application.Exit();
return;
}
}
}
catch { }
}
Application.Run(new Form1());
}
Не забудьте врахувати безпеку, обмеживши програму одним екземпляром:
Повна стаття: https://blogs.msdn.microsoft.com/oldnewthing/20060620-13/?p=30813
Ми використовуємо іменований mutex з фіксованим іменем, щоб виявити, чи працює інша копія програми. Але це також означає, що зловмисник може спершу створити мютекс, тим самим не даючи нашій програмі працювати взагалі! Як я можу запобігти атаці відмови у наданні послуги?
...
Якщо зловмисник працює в тому ж контексті безпеки, що і ваша програма (або була б) в ній, ви нічого не можете зробити. Яке б "секретне рукостискання" ви не визначили, чи працює інша копія вашої програми, зловмисник може це імітувати. Оскільки вона працює у правильному контексті безпеки, вона може робити все, що може зробити "реальна" програма.
...
Очевидно, що ви не можете захистити себе від зловмисника, який працює з тією ж пільгою безпеки, але ви все одно можете захистити себе від непривілейованих зловмисників, які працюють на інших привілеях безпеки.
Спробуйте встановити DACL на своєму mutex, ось .NET спосіб: https://msdn.microsoft.com/en-us/library/system.security.accesscontrol.mutexsecurity(v=vs.110).aspx
Жоден з цих відповідей не працював для мене, тому що мені це було потрібно для роботи під Linux за допомогою monodevelop. Це для мене чудово працює:
Назвіть цей метод, передаючи йому унікальний ідентифікатор
public static void PreventMultipleInstance(string applicationId)
{
// Under Windows this is:
// C:\Users\SomeUser\AppData\Local\Temp\
// Linux this is:
// /tmp/
var temporaryDirectory = Path.GetTempPath();
// Application ID (Make sure this guid is different accross your different applications!
var applicationGuid = applicationId + ".process-lock";
// file that will serve as our lock
var fileFulePath = Path.Combine(temporaryDirectory, applicationGuid);
try
{
// Prevents other processes from reading from or writing to this file
var _InstanceLock = new FileStream(fileFulePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
_InstanceLock.Lock(0, 0);
MonoApp.Logger.LogToDisk(LogType.Notification, "04ZH-EQP0", "Aquired Lock", fileFulePath);
// todo investigate why we need a reference to file stream. Without this GC releases the lock!
System.Timers.Timer t = new System.Timers.Timer()
{
Interval = 500000,
Enabled = true,
};
t.Elapsed += (a, b) =>
{
try
{
_InstanceLock.Lock(0, 0);
}
catch
{
MonoApp.Logger.Log(LogType.Error, "AOI7-QMCT", "Unable to lock file");
}
};
t.Start();
}
catch
{
// Terminate application because another instance with this ID is running
Environment.Exit(102534);
}
}