Pull to refresh

Windows mobile: один экземпляр приложения

Reading time2 min
Views1.5K

Проблема


Работая над 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.


Похожей идеей обладает вариант, используя мьютексы.
Надеюсь, данное решение кому-то окажется полезным.
Tags:
Hubs:
Total votes 3: ↑2 and ↓1+1
Comments10

Articles