Проблема
Работая над windows mobile проектом (Visual Studio 2008, .net cf 3.5, C#) столкнулся с проблемой контроля запуска одного экземпляра приложения. К сожалению создатели .net compact framework 3.5 (и ранних версий) не включили возможность поиска процесса по его имени — метод System.Diagnostics.Process.GetProcessesByName(). Дополнительных методов, которые с помощью управляемого кода помогли бы решить проблему, обнаружено не было.
Решение
Проблему можно решить несколькими путями, используя неуправляемый код. В качестве помощников выступают глобальные объекты синхронизации процессов: мьютексы, семафоры и события. Остановимся на последних.
Для начала необходимо создать постоянный идентификатор для нашего события (Visual Studio имеет встроенный генератор GUID'ов: Tools->Create GUID):
private static readonly Guid SingleInstanceGuid = new Guid(«1DADFDD1-DDD1-4390-95B9-5852CFB39807»);
Затем воспользуемся неуправляемым кодом:
private const int ERROR_ALREADY_EXISTS = 183; //код ошибки, возвращаемый в случае уже существующего события
[DllImport(«coredll.dll», SetLastError = true)]
private static extern IntPtr CreateEvent(IntPtr lpEventAttributes, bool bManualReset, bool bInitialState, string lpName);
[DllImport(«coredll.dll», SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);
[DllImport(«coredll.dll», SetLastError = true)]
private static extern IntPtr FindWindow(string className, string wndName);
Создаём событие, получаем код ошибки, возвращаем ответ:
public static bool IsSingleInstance() {
var path = Assembly.GetExecutingAssembly().GetName().CodeBase;
handle = CreateEvent(IntPtr.Zero, false, false, SingleInstanceGuid.ToString());
var error = Marshal.GetLastWin32Error();
//Если событие уже существует, находим запущенное приложение и посылаем ему команду 0x8001, которая должна переактивировать приложение
if (error == ERROR_ALREADY_EXISTS) {
var hWnd = FindWindow("#NETCF_AGL_PARK_" + path, string.Empty);
if (hWnd != IntPtr.Zero) {
var msg = Message.Create(hWnd, 0x8001, (IntPtr)0, (IntPtr)0);
MessageWindow.SendMessage(ref msg);
}
return false;
}
return true;
}
Перед закрытием приложения закрываем событие:
public static void CloseHandle() {
CloseHandle(handle);
}
Весь этот код для удобства помещаем в класс, например SingleInstance.cs. На практике применяется следующим образом в файле program.cs:
if (!SingleInstance.IsSingleInstance()) {
return;
}
try {
Application.Run(new Form1());
} catch (Exception ex) {
} finally {
SingleInstance.CloseHandle();
}* This source code was highlighted with Source Code Highlighter.
Похожей идеей обладает вариант, используя мьютексы.
Надеюсь, данное решение кому-то окажется полезным.