Try to build PDB files even for releases.
[nssm.git] / gui.cpp
diff --git a/gui.cpp b/gui.cpp
index d9b7011..5aaa932 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -1,6 +1,9 @@
 #include "nssm.h"\r
 \r
-static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS };\r
+extern const TCHAR *hook_event_strings[];\r
+extern const TCHAR *hook_action_strings[];\r
+\r
+static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_DEPENDENCIES, NSSM_TAB_PROCESS, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_TAB_HOOKS, NSSM_NUM_TABS } nssm_tabs;\r
 static HWND tablist[NSSM_NUM_TABS];\r
 static int selected_tab;\r
 \r
@@ -23,14 +26,27 @@ static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function) {
   return dialog(templ, parent, function, 0);\r
 }\r
 \r
+static inline void set_logon_enabled(unsigned char interact_enabled, unsigned char credentials_enabled) {\r
+  EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), interact_enabled);\r
+  EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), credentials_enabled);\r
+  EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), credentials_enabled);\r
+  EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), credentials_enabled);\r
+}\r
+\r
 int nssm_gui(int resource, nssm_service_t *service) {\r
   /* Create window */\r
   HWND dlg = dialog(MAKEINTRESOURCE(resource), 0, nssm_dlg, (LPARAM) service);\r
   if (! dlg) {\r
-    popup_message(MB_OK, NSSM_GUI_CREATEDIALOG_FAILED, error_string(GetLastError()));\r
+    popup_message(0, MB_OK, NSSM_GUI_CREATEDIALOG_FAILED, error_string(GetLastError()));\r
     return 1;\r
   }\r
 \r
+  /* Load the icon. */\r
+  HANDLE icon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_NSSM), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);\r
+  if (icon) SendMessage(dlg, WM_SETICON, ICON_SMALL, (LPARAM) icon);\r
+  icon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_NSSM), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0);\r
+  if (icon) SendMessage(dlg, WM_SETICON, ICON_BIG, (LPARAM) icon);\r
+\r
   /* Remember what the window is for. */\r
   SetWindowLongPtr(dlg, GWLP_USERDATA, (LONG_PTR) resource);\r
 \r
@@ -61,6 +77,7 @@ int nssm_gui(int resource, nssm_service_t *service) {
 \r
     /* Set existing details. */\r
     HWND combo;\r
+    HWND list;\r
 \r
     /* Application tab. */\r
     if (service->native) SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->image);\r
@@ -76,18 +93,60 @@ int nssm_gui(int resource, nssm_service_t *service) {
 \r
     /* Log on tab. */\r
     if (service->username) {\r
-      CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_ACCOUNT);\r
-      SetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username);\r
-      EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), 0);\r
-      EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), 1);\r
-      EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), 1);\r
-      EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), 1);\r
+      if (is_virtual_account(service->name, service->username)) {\r
+        CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_VIRTUAL_SERVICE);\r
+        set_logon_enabled(0, 0);\r
+      }\r
+      else {\r
+        CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_ACCOUNT);\r
+        SetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username);\r
+        set_logon_enabled(0, 1);\r
+      }\r
     }\r
     else {\r
-      CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
+      CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_LOCALSYSTEM);\r
       if (service->type & SERVICE_INTERACTIVE_PROCESS) SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_SETCHECK, BST_CHECKED, 0);\r
     }\r
 \r
+    /* Dependencies tab. */\r
+    if (service->dependencieslen) {\r
+      TCHAR *formatted;\r
+      unsigned long newlen;\r
+      if (format_double_null(service->dependencies, service->dependencieslen, &formatted, &newlen)) {\r
+        popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("nssm_dlg()"));\r
+      }\r
+      else {\r
+        SetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, formatted);\r
+        HeapFree(GetProcessHeap(), 0, formatted);\r
+      }\r
+    }\r
+\r
+    /* Process tab. */\r
+    if (service->priority) {\r
+      int priority = priority_constant_to_index(service->priority);\r
+      combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
+      SendMessage(combo, CB_SETCURSEL, priority, 0);\r
+    }\r
+\r
+    if (service->affinity) {\r
+      list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
+      SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_UNCHECKED, 0);\r
+      EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), 1);\r
+\r
+      DWORD_PTR affinity, system_affinity;\r
+      if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {\r
+        if ((service->affinity & (__int64) system_affinity) != service->affinity) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_AFFINITY);\r
+      }\r
+\r
+      for (int i = 0; i < num_cpus(); i++) {\r
+        if (! (service->affinity & (1LL << (__int64) i))) SendMessage(list, LB_SETSEL, 0, i);\r
+      }\r
+    }\r
+\r
+    if (service->no_console) {\r
+      SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0);\r
+    }\r
+\r
     /* Shutdown tab. */\r
     if (! (service->stop_method & NSSM_STOP_METHOD_CONSOLE)) {\r
       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0);\r
@@ -107,68 +166,66 @@ int nssm_gui(int resource, nssm_service_t *service) {
     if (! (service->stop_method & NSSM_STOP_METHOD_TERMINATE)) {\r
       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_UNCHECKED, 0);\r
     }\r
+    if (! service->kill_process_tree) {\r
+      SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_SETCHECK, BST_UNCHECKED, 0);\r
+    }\r
 \r
     /* Restart tab. */\r
     SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, service->throttle_delay, 0);\r
     combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
     SendMessage(combo, CB_SETCURSEL, service->default_exit_action, 0);\r
+    SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, service->restart_delay, 0);\r
 \r
     /* I/O tab. */\r
     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDIN, service->stdin_path);\r
     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDOUT, service->stdout_path);\r
     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDERR, service->stderr_path);\r
+    if (service->timestamp_log) SendDlgItemMessage(tablist[NSSM_TAB_IO], IDC_TIMESTAMP, BM_SETCHECK, BST_CHECKED, 0);\r
 \r
     /* Rotation tab. */\r
     if (service->stdout_disposition == CREATE_ALWAYS) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_SETCHECK, BST_CHECKED, 0);\r
     if (service->rotate_files) {\r
       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_SETCHECK, BST_CHECKED, 0);\r
+      EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), 1);\r
       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), 1);\r
       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), 1);\r
     }\r
+    if (service->rotate_stdout_online || service->rotate_stderr_online) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_CHECKED, 0);\r
     SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, service->rotate_seconds, 0);\r
     if (! service->rotate_bytes_high) SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, service->rotate_bytes_low, 0);\r
 \r
+    /* Hooks tab. */\r
+    if (service->hook_share_output_handles) SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_SETCHECK, BST_CHECKED, 0);\r
+\r
     /* Check if advanced settings are in use. */\r
-    if (service->stdout_disposition ^ service->stderr_disposition || service->stdout_disposition & ~CREATE_ALWAYS || service->stderr_disposition & ~CREATE_ALWAYS) popup_message(MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_STDIO);\r
-    if (service->rotate_bytes_high) popup_message(MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ROTATE_BYTES);\r
+    if (service->stdout_disposition != service->stderr_disposition || (service->stdout_disposition && service->stdout_disposition != NSSM_STDOUT_DISPOSITION && service->stdout_disposition != CREATE_ALWAYS) || (service->stderr_disposition && service->stderr_disposition != NSSM_STDERR_DISPOSITION && service->stderr_disposition != CREATE_ALWAYS)) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_STDIO);\r
+    if (service->rotate_bytes_high) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ROTATE_BYTES);\r
 \r
     /* Environment tab. */\r
     TCHAR *env;\r
     unsigned long envlen;\r
     if (service->env_extralen) {\r
-      SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_SETCHECK, BST_CHECKED, 0);\r
       env = service->env_extra;\r
       envlen = service->env_extralen;\r
     }\r
     else {\r
       env = service->env;\r
       envlen = service->envlen;\r
+      if (envlen) SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_SETCHECK, BST_CHECKED, 0);\r
     }\r
 \r
     if (envlen) {\r
-      /* Replace NULL with CRLF. Leave NULL NULL as the end marker. */\r
-      unsigned long i, j;\r
-      unsigned long newlen = envlen;\r
-      for (i = 0; i < envlen; i++) if (! env[i] && env[i + 1]) newlen++;\r
-\r
-      TCHAR *formatted = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newlen * sizeof(TCHAR));\r
-      if (formatted) {\r
-        for (i = 0, j = 0; i < envlen; i++) {\r
-          formatted[j] = env[i];\r
-          if (! env[i]) {\r
-            if (env[i + 1]) {\r
-              formatted[j] = _T('\r');\r
-              formatted[++j] = _T('\n');\r
-            }\r
-          }\r
-          j++;\r
-        }\r
+      TCHAR *formatted;\r
+      unsigned long newlen;\r
+      if (format_double_null(env, envlen, &formatted, &newlen)) {\r
+        popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("nssm_dlg()"));\r
+      }\r
+      else {\r
         SetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, formatted);\r
         HeapFree(GetProcessHeap(), 0, formatted);\r
       }\r
-      else popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("nssm_dlg()"));\r
     }\r
-    if (service->envlen && service->env_extralen) popup_message(MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ENVIRONMENT);\r
+    if (service->envlen && service->env_extralen) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ENVIRONMENT);\r
   }\r
 \r
   /* Go! */\r
@@ -222,22 +279,114 @@ static inline void set_timeout_enabled(unsigned long control, unsigned long depe
   EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], dependent), enabled);\r
 }\r
 \r
-static inline void set_logon_enabled(unsigned char enabled) {\r
-  EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), ! enabled);\r
-  EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), enabled);\r
-  EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), enabled);\r
-  EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), enabled);\r
+static inline void set_affinity_enabled(unsigned char enabled) {\r
+  EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), enabled);\r
 }\r
 \r
 static inline void set_rotation_enabled(unsigned char enabled) {\r
+  EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), enabled);\r
   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), enabled);\r
   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), enabled);\r
 }\r
 \r
-static inline void check_io(TCHAR *name, TCHAR *buffer, unsigned long len, unsigned long control) {\r
+static inline int hook_env(const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {\r
+  return _sntprintf_s(buffer, buflen, _TRUNCATE, _T("NSSM_HOOK_%s_%s"), hook_event, hook_action);\r
+}\r
+\r
+static inline void set_hook_tab(int event_index, int action_index, bool changed) {\r
+  int first_event = NSSM_GUI_HOOK_EVENT_START;\r
+  HWND combo;\r
+  combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_EVENT);\r
+  SendMessage(combo, CB_SETCURSEL, event_index, 0);\r
+  combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_ACTION);\r
+  SendMessage(combo, CB_RESETCONTENT, 0, 0);\r
+\r
+  const TCHAR *hook_event = hook_event_strings[event_index];\r
+  TCHAR *hook_action;\r
+  int i;\r
+  switch (event_index + first_event) {\r
+    case NSSM_GUI_HOOK_EVENT_ROTATE:\r
+      i = 0;\r
+      SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_ROTATE_PRE));\r
+      if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
+      SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_ROTATE_POST));\r
+      if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
+      break;\r
+\r
+    case NSSM_GUI_HOOK_EVENT_START:\r
+      i = 0;\r
+      SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_START_PRE));\r
+      if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
+      SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_START_POST));\r
+      if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
+      break;\r
+\r
+    case NSSM_GUI_HOOK_EVENT_STOP:\r
+      i = 0;\r
+      SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_STOP_PRE));\r
+      if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
+      break;\r
+\r
+    case NSSM_GUI_HOOK_EVENT_EXIT:\r
+      i = 0;\r
+      SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_EXIT_POST));\r
+      if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
+      break;\r
+\r
+    case NSSM_GUI_HOOK_EVENT_POWER:\r
+      i = 0;\r
+      SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_POWER_CHANGE));\r
+      if (action_index == i++) hook_action = NSSM_HOOK_ACTION_CHANGE;\r
+      SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_POWER_RESUME));\r
+      if (action_index == i++) hook_action = NSSM_HOOK_ACTION_RESUME;\r
+      break;\r
+  }\r
+\r
+  SendMessage(combo, CB_SETCURSEL, action_index, 0);\r
+\r
+  TCHAR hook_name[HOOK_NAME_LENGTH];\r
+  hook_env(hook_event, hook_action, hook_name, _countof(hook_name));\r
+\r
+  if (! *hook_name) return;\r
+\r
+  TCHAR cmd[CMD_LENGTH];\r
+  if (changed) {\r
+    GetDlgItemText(tablist[NSSM_TAB_HOOKS], IDC_HOOK, cmd, _countof(cmd));\r
+    SetEnvironmentVariable(hook_name, cmd);\r
+  }\r
+  else {\r
+    if (! GetEnvironmentVariable(hook_name, cmd, _countof(cmd))) cmd[0] = _T('\0');\r
+    SetDlgItemText(tablist[NSSM_TAB_HOOKS], IDC_HOOK, cmd);\r
+  }\r
+}\r
+\r
+static inline int update_hook(TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action) {\r
+  TCHAR hook_name[HOOK_NAME_LENGTH];\r
+  if (hook_env(hook_event, hook_action, hook_name, _countof(hook_name)) < 0) return 1;\r
+  TCHAR cmd[CMD_LENGTH];\r
+  ZeroMemory(cmd, sizeof(cmd));\r
+  GetEnvironmentVariable(hook_name, cmd, _countof(cmd));\r
+  if (set_hook(service_name, hook_event, hook_action, cmd)) return 2;\r
+  return 0;\r
+}\r
+\r
+static inline int update_hooks(TCHAR *service_name) {\r
+  int ret = 0;\r
+  ret += update_hook(service_name, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE);\r
+  ret += update_hook(service_name, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST);\r
+  ret += update_hook(service_name, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE);\r
+  ret += update_hook(service_name, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST);\r
+  ret += update_hook(service_name, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE);\r
+  ret += update_hook(service_name, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME);\r
+  ret += update_hook(service_name, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE);\r
+  ret += update_hook(service_name, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST);\r
+  return ret;\r
+}\r
+\r
+static inline void check_io(HWND owner, TCHAR *name, TCHAR *buffer, unsigned long len, unsigned long control) {\r
   if (! SendMessage(GetDlgItem(tablist[NSSM_TAB_IO], control), WM_GETTEXTLENGTH, 0, 0)) return;\r
   if (GetDlgItemText(tablist[NSSM_TAB_IO], control, buffer, (int) len)) return;\r
-  popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name);\r
+  popup_message(owner, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name);\r
   ZeroMemory(buffer, len * sizeof(TCHAR));\r
 }\r
 \r
@@ -254,7 +403,7 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
 \r
   /* Get service name. */\r
   if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
-    popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
+    popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
     cleanup_nssm_service(service);\r
     return 2;\r
   }\r
@@ -262,7 +411,7 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
   /* Get executable name */\r
   if (! service->native) {\r
     if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe, _countof(service->exe))) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
       return 3;\r
     }\r
 \r
@@ -275,7 +424,7 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
     /* Get flags. */\r
     if (SendMessage(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) {\r
       if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags, _countof(service->flags))) {\r
-        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
+        popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
         return 4;\r
       }\r
     }\r
@@ -284,14 +433,14 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
   /* Get details. */\r
   if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME), WM_GETTEXTLENGTH, 0, 0)) {\r
     if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname, _countof(service->displayname))) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME);\r
       return 5;\r
     }\r
   }\r
 \r
   if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION), WM_GETTEXTLENGTH, 0, 0)) {\r
     if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description, _countof(service->description))) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION);\r
       return 5;\r
     }\r
   }\r
@@ -309,42 +458,65 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
     service->username = 0;\r
     service->usernamelen = 0;\r
     if (service->password) {\r
-      SecureZeroMemory(service->password, service->passwordlen);\r
+      SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
       HeapFree(GetProcessHeap(), 0, service->password);\r
     }\r
     service->password = 0;\r
     service->passwordlen = 0;\r
   }\r
+  else if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_VIRTUAL_SERVICE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
+    if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
+    service->username = virtual_account(service->name);\r
+    if (! service->username) {\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
+      return 6;\r
+    }\r
+    service->usernamelen = _tcslen(service->username) + 1;\r
+    service->password = 0;\r
+    service->passwordlen = 0;\r
+  }\r
   else {\r
     /* Username. */\r
     service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0);\r
     if (! service->usernamelen) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_USERNAME);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_USERNAME);\r
       return 6;\r
     }\r
     service->usernamelen++;\r
 \r
     service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));\r
     if (! service->username) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
       return 6;\r
     }\r
     if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username, (int) service->usernamelen)) {\r
       HeapFree(GetProcessHeap(), 0, service->username);\r
       service->username = 0;\r
       service->usernamelen = 0;\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME);\r
       return 6;\r
     }\r
 \r
     /*\r
-      Special case LOCALSYSTEM.\r
+      Special case for well-known accounts.\r
       Ignore the password if we're editing and the username hasn't changed.\r
     */\r
-    if (str_equiv(service->username, NSSM_LOCALSYSTEM_ACCOUNT)) {\r
-      HeapFree(GetProcessHeap(), 0, service->username);\r
-      service->username = 0;\r
-      service->usernamelen = 0;\r
+    const TCHAR *well_known = well_known_username(service->username);\r
+    if (well_known) {\r
+      if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) {\r
+        HeapFree(GetProcessHeap(), 0, service->username);\r
+        service->username = 0;\r
+        service->usernamelen = 0;\r
+      }\r
+      else {\r
+        service->usernamelen = _tcslen(well_known) + 1;\r
+        service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));\r
+        if (! service->username) {\r
+          print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("install()"));\r
+          return 6;\r
+        }\r
+        memmove(service->username, well_known, service->usernamelen * sizeof(TCHAR));\r
+      }\r
     }\r
     else {\r
       /* Password. */\r
@@ -353,11 +525,11 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
 \r
       if (! orig_service || ! orig_service->username || ! str_equiv(service->username, orig_service->username) || service->passwordlen || passwordlen) {\r
         if (! service->passwordlen) {\r
-          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
           return 6;\r
         }\r
         if (passwordlen != service->passwordlen) {\r
-          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
           return 6;\r
         }\r
         service->passwordlen++;\r
@@ -368,7 +540,7 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
           HeapFree(GetProcessHeap(), 0, service->username);\r
           service->username = 0;\r
           service->usernamelen = 0;\r
-          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password confirmation"), _T("install()"));\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password confirmation"), _T("install()"));\r
           return 6;\r
         }\r
 \r
@@ -379,45 +551,45 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
           HeapFree(GetProcessHeap(), 0, service->username);\r
           service->username = 0;\r
           service->usernamelen = 0;\r
-          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password"), _T("install()"));\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password"), _T("install()"));\r
           return 6;\r
         }\r
 \r
         /* Get first password. */\r
         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1, service->password, (int) service->passwordlen)) {\r
           HeapFree(GetProcessHeap(), 0, password);\r
-          SecureZeroMemory(service->password, service->passwordlen);\r
+          SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
           HeapFree(GetProcessHeap(), 0, service->password);\r
           service->password = 0;\r
           service->passwordlen = 0;\r
           HeapFree(GetProcessHeap(), 0, service->username);\r
           service->username = 0;\r
           service->usernamelen = 0;\r
-          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
           return 6;\r
         }\r
 \r
         /* Get confirmation. */\r
         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2, password, (int) service->passwordlen)) {\r
-          SecureZeroMemory(password, service->passwordlen);\r
+          SecureZeroMemory(password, service->passwordlen * sizeof(TCHAR));\r
           HeapFree(GetProcessHeap(), 0, password);\r
-          SecureZeroMemory(service->password, service->passwordlen);\r
+          SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
           HeapFree(GetProcessHeap(), 0, service->password);\r
           service->password = 0;\r
           service->passwordlen = 0;\r
           HeapFree(GetProcessHeap(), 0, service->username);\r
           service->username = 0;\r
           service->usernamelen = 0;\r
-          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
           return 6;\r
         }\r
 \r
         /* Compare. */\r
         if (_tcsncmp(password, service->password, service->passwordlen)) {\r
-          popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
-          SecureZeroMemory(password, service->passwordlen);\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
+          SecureZeroMemory(password, service->passwordlen * sizeof(TCHAR));\r
           HeapFree(GetProcessHeap(), 0, password);\r
-          SecureZeroMemory(service->password, service->passwordlen);\r
+          SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
           HeapFree(GetProcessHeap(), 0, service->password);\r
           service->password = 0;\r
           service->passwordlen = 0;\r
@@ -430,9 +602,59 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
     }\r
   }\r
 \r
+  /* Get dependencies. */\r
+  unsigned long dependencieslen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES), WM_GETTEXTLENGTH, 0, 0);\r
+  if (dependencieslen) {\r
+    TCHAR *dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (dependencieslen + 2) * sizeof(TCHAR));\r
+    if (! dependencies) {\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()"));\r
+      cleanup_nssm_service(service);\r
+      return 6;\r
+    }\r
+\r
+    if (! GetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, dependencies, dependencieslen + 1)) {\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DEPENDENCIES);\r
+      HeapFree(GetProcessHeap(), 0, dependencies);\r
+      cleanup_nssm_service(service);\r
+      return 6;\r
+    }\r
+\r
+    if (unformat_double_null(dependencies, dependencieslen, &service->dependencies, &service->dependencieslen)) {\r
+      HeapFree(GetProcessHeap(), 0, dependencies);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()"));\r
+      cleanup_nssm_service(service);\r
+      return 6;\r
+    }\r
+\r
+    HeapFree(GetProcessHeap(), 0, dependencies);\r
+  }\r
+\r
   /* Remaining tabs are only for services we manage. */\r
   if (service->native) return 0;\r
 \r
+  /* Get process stuff. */\r
+  combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
+  service->priority = priority_index_to_constant((unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0));\r
+\r
+  service->affinity = 0LL;\r
+  if (! (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_GETCHECK, 0, 0) & BST_CHECKED)) {\r
+    HWND list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
+    int selected = (int) SendMessage(list, LB_GETSELCOUNT, 0, 0);\r
+    int count = (int) SendMessage(list, LB_GETCOUNT, 0, 0);\r
+    if (! selected) {\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_WARN_AFFINITY_NONE);\r
+      return 5;\r
+    }\r
+    else if (selected < count) {\r
+      for (int i = 0; i < count; i++) {\r
+        if (SendMessage(list, LB_GETSEL, i, 0)) service->affinity |= (1LL << (__int64) i);\r
+      }\r
+    }\r
+  }\r
+\r
+  if (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->no_console = 0;\r
+  else service->no_console = 1;\r
+\r
   /* Get stop method stuff. */\r
   check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE);\r
   check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW);\r
@@ -441,17 +663,22 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay);\r
   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay);\r
   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay);\r
+  if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->kill_process_tree = 1;\r
+  else service->kill_process_tree = 0;\r
 \r
   /* Get exit action stuff. */\r
   check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay);\r
   combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
   service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
   if (service->default_exit_action == CB_ERR) service->default_exit_action = 0;\r
+  check_number(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, &service->restart_delay);\r
 \r
   /* Get I/O stuff. */\r
-  check_io(_T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN);\r
-  check_io(_T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT);\r
-  check_io(_T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR);\r
+  check_io(window, _T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN);\r
+  check_io(window, _T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT);\r
+  check_io(window, _T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR);\r
+  if (SendDlgItemMessage(tablist[NSSM_TAB_IO], IDC_TIMESTAMP, BM_GETCHECK, 0, 0) & BST_CHECKED) service->timestamp_log = true;\r
+  else service->timestamp_log = false;\r
 \r
   /* Override stdout and/or stderr. */\r
   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
@@ -462,56 +689,47 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
   /* Get rotation stuff. */\r
   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
     service->rotate_files = true;\r
+    if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_ONLINE;\r
     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds);\r
     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low);\r
   }\r
 \r
+  /* Get hook stuff. */\r
+  if (SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_GETCHECK, 0, 0) & BST_CHECKED) service->hook_share_output_handles = true;\r
+\r
   /* Get environment. */\r
   unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);\r
   if (envlen) {\r
     TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR));\r
     if (! env) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
       cleanup_nssm_service(service);\r
       return 5;\r
     }\r
 \r
     if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
       HeapFree(GetProcessHeap(), 0, env);\r
       cleanup_nssm_service(service);\r
       return 5;\r
     }\r
 \r
-    /* Strip CR and replace LF with NULL. */\r
-    unsigned long newlen = 0;\r
-    unsigned long i, j;\r
-    for (i = 0; i < envlen; i++) if (env[i] != _T('\r')) newlen++;\r
-    /* Must end with two NULLs. */\r
-    newlen += 2;\r
-\r
-    TCHAR *newenv = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, newlen * sizeof(TCHAR));\r
-    if (! newenv) {\r
+    TCHAR *newenv;\r
+    unsigned long newlen;\r
+    if (unformat_double_null(env, envlen, &newenv, &newlen)) {\r
       HeapFree(GetProcessHeap(), 0, env);\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
       cleanup_nssm_service(service);\r
       return 5;\r
     }\r
 \r
-    for (i = 0, j = 0; i < envlen; i++) {\r
-      if (env[i] == _T('\r')) continue;\r
-      if (env[i] == _T('\n')) newenv[j] = _T('\0');\r
-      else newenv[j] = env[i];\r
-      j++;\r
-    }\r
-\r
     HeapFree(GetProcessHeap(), 0, env);\r
     env = newenv;\r
     envlen = newlen;\r
 \r
     /* Test the environment is valid. */\r
     if (test_environment(env)) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
       HeapFree(GetProcessHeap(), 0, env);\r
       cleanup_nssm_service(service);\r
       return 5;\r
@@ -543,37 +761,39 @@ int install(HWND window) {
   /* See if it works. */\r
   switch (install_service(service)) {\r
     case 1:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("install()"));\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("install()"));\r
       cleanup_nssm_service(service);\r
       return 1;\r
 \r
     case 2:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
       cleanup_nssm_service(service);\r
       return 2;\r
 \r
     case 3:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
       cleanup_nssm_service(service);\r
       return 3;\r
 \r
     case 4:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
       cleanup_nssm_service(service);\r
       return 4;\r
 \r
     case 5:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);\r
       cleanup_nssm_service(service);\r
       return 5;\r
 \r
     case 6:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);\r
       cleanup_nssm_service(service);\r
       return 6;\r
   }\r
 \r
-  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
+  update_hooks(service->name);\r
+\r
+  popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
   cleanup_nssm_service(service);\r
   return 0;\r
 }\r
@@ -587,13 +807,13 @@ int remove(HWND window) {
   if (service) {\r
     /* Get service name */\r
     if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
       cleanup_nssm_service(service);\r
       return 2;\r
     }\r
 \r
     /* Confirm */\r
-    if (popup_message(MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {\r
+    if (popup_message(window, MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {\r
       cleanup_nssm_service(service);\r
       return 0;\r
     }\r
@@ -601,27 +821,27 @@ int remove(HWND window) {
 \r
   switch (remove_service(service)) {\r
     case 1:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("remove()"));\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("remove()"));\r
       cleanup_nssm_service(service);\r
       return 1;\r
 \r
     case 2:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
       cleanup_nssm_service(service);\r
       return 2;\r
 \r
     case 3:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
-      return 3;\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
       cleanup_nssm_service(service);\r
+      return 3;\r
 \r
     case 4:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
       cleanup_nssm_service(service);\r
       return 4;\r
   }\r
 \r
-  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
+  popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
   cleanup_nssm_service(service);\r
   return 0;\r
 }\r
@@ -637,28 +857,30 @@ int edit(HWND window, nssm_service_t *orig_service) {
 \r
   switch (edit_service(service, true)) {\r
     case 1:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()"));\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()"));\r
       cleanup_nssm_service(service);\r
       return 1;\r
 \r
     case 3:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
       cleanup_nssm_service(service);\r
       return 3;\r
 \r
     case 4:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
       cleanup_nssm_service(service);\r
       return 4;\r
 \r
     case 5:\r
     case 6:\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED);\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED);\r
       cleanup_nssm_service(service);\r
       return 6;\r
   }\r
 \r
-  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name);\r
+  update_hooks(service->name);\r
+\r
+  popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name);\r
   cleanup_nssm_service(service);\r
   return 0;\r
 }\r
@@ -702,7 +924,7 @@ void browse(HWND window, TCHAR *current, unsigned long flags, ...) {
     va_start(arg, flags);\r
     while (i = va_arg(arg, int)) {\r
       TCHAR *localised = message_string(i);\r
-      _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize, _TRUNCATE, localised);\r
+      _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, localised);\r
       len += _tcslen(localised) + 1;\r
       LocalFree(localised);\r
       TCHAR *filter = browse_filter(i);\r
@@ -712,14 +934,19 @@ void browse(HWND window, TCHAR *current, unsigned long flags, ...) {
     va_end(arg);\r
     /* Remainder of the buffer is already zeroed */\r
   }\r
-  ofn.lpstrFile = new TCHAR[MAX_PATH];\r
-  if (flags & OFN_NOVALIDATE) {\r
-    /* Directory hack. */\r
-    _sntprintf_s(ofn.lpstrFile, MAX_PATH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
+  ofn.lpstrFile = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH * sizeof(TCHAR));\r
+  if (ofn.lpstrFile) {\r
+    if (flags & OFN_NOVALIDATE) {\r
+      /* Directory hack. */\r
+      _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
+      ofn.nMaxFile = DIR_LENGTH;\r
+    }\r
+    else {\r
+      _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T("%s"), current);\r
+      ofn.nMaxFile = PATH_LENGTH;\r
+    }\r
   }\r
-  else _sntprintf_s(ofn.lpstrFile, MAX_PATH, _TRUNCATE, _T("%s"), current);\r
   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
-  ofn.nMaxFile = MAX_PATH;\r
   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
 \r
   if (GetOpenFileName(&ofn)) {\r
@@ -728,8 +955,7 @@ void browse(HWND window, TCHAR *current, unsigned long flags, ...) {
     SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile);\r
   }\r
   if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter);\r
-\r
-  delete[] ofn.lpstrFile;\r
+  if (ofn.lpstrFile) HeapFree(GetProcessHeap(), 0, ofn.lpstrFile);\r
 }\r
 \r
 INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {\r
@@ -740,7 +966,7 @@ INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {
     /* Button was pressed or control was controlled. */\r
     case WM_COMMAND:\r
       HWND dlg;\r
-      TCHAR buffer[MAX_PATH];\r
+      TCHAR buffer[PATH_LENGTH];\r
       unsigned char enabled;\r
 \r
       switch (LOWORD(w)) {\r
@@ -767,11 +993,22 @@ INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {
 \r
         /* Log on. */\r
         case IDC_LOCALSYSTEM:\r
-          set_logon_enabled(0);\r
+          set_logon_enabled(1, 0);\r
+          break;\r
+\r
+        case IDC_VIRTUAL_SERVICE:\r
+          set_logon_enabled(0, 0);\r
           break;\r
 \r
         case IDC_ACCOUNT:\r
-          set_logon_enabled(1);\r
+          set_logon_enabled(0, 1);\r
+          break;\r
+\r
+        /* Affinity. */\r
+        case IDC_AFFINITY_ALL:\r
+          if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 0;\r
+          else enabled = 1;\r
+          set_affinity_enabled(enabled);\r
           break;\r
 \r
         /* Shutdown methods. */\r
@@ -820,6 +1057,28 @@ INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {
           else enabled = 0;\r
           set_rotation_enabled(enabled);\r
           break;\r
+\r
+        /* Hook event. */\r
+        case IDC_HOOK_EVENT:\r
+          if (HIWORD(w) == CBN_SELCHANGE) set_hook_tab((int) SendMessage(GetDlgItem(tab, IDC_HOOK_EVENT), CB_GETCURSEL, 0, 0), 0, false);\r
+          break;\r
+\r
+        /* Hook action. */\r
+        case IDC_HOOK_ACTION:\r
+          if (HIWORD(w) == CBN_SELCHANGE) set_hook_tab((int) SendMessage(GetDlgItem(tab, IDC_HOOK_EVENT), CB_GETCURSEL, 0, 0), (int) SendMessage(GetDlgItem(tab, IDC_HOOK_ACTION), CB_GETCURSEL, 0, 0), false);\r
+          break;\r
+\r
+        /* Browse for hook. */\r
+        case IDC_BROWSE_HOOK:\r
+          dlg = GetDlgItem(tab, IDC_HOOK);\r
+          GetDlgItemText(tab, IDC_HOOK, buffer, _countof(buffer));\r
+          browse(dlg, _T(""), OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
+          break;\r
+\r
+        /* Hook. */\r
+        case IDC_HOOK:\r
+          set_hook_tab((int) SendMessage(GetDlgItem(tab, IDC_HOOK_EVENT), CB_GETCURSEL, 0, 0), (int) SendMessage(GetDlgItem(tab, IDC_HOOK_ACTION), CB_GETCURSEL, 0, 0), true);\r
+          break;\r
       }\r
       return 1;\r
   }\r
@@ -840,6 +1099,8 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
 \r
       HWND tabs;\r
       HWND combo;\r
+      HWND list;\r
+      int i, n;\r
       tabs = GetDlgItem(window, IDC_TAB1);\r
       if (! tabs) return 0;\r
 \r
@@ -887,11 +1148,68 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
 \r
       /* Set defaults. */\r
       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
-      set_logon_enabled(0);\r
+      set_logon_enabled(1, 0);\r
+\r
+      /* Dependencies tab. */\r
+      tab.pszText = message_string(NSSM_GUI_TAB_DEPENDENCIES);\r
+      tab.cchTextMax = (int) _tcslen(tab.pszText);\r
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DEPENDENCIES, (LPARAM) &tab);\r
+      tablist[NSSM_TAB_DEPENDENCIES] = dialog(MAKEINTRESOURCE(IDD_DEPENDENCIES), window, tab_dlg);\r
+      ShowWindow(tablist[NSSM_TAB_DEPENDENCIES], SW_HIDE);\r
 \r
       /* Remaining tabs are only for services we manage. */\r
       if (service->native) return 1;\r
 \r
+      /* Process tab. */\r
+      tab.pszText = message_string(NSSM_GUI_TAB_PROCESS);\r
+      tab.cchTextMax = (int) _tcslen(tab.pszText);\r
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab);\r
+      tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg);\r
+      ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE);\r
+\r
+      /* Set defaults. */\r
+      combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS));\r
+      SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0);\r
+\r
+      SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
+\r
+      list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
+      n = num_cpus();\r
+      SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0);\r
+      for (i = 0; i < n; i++) {\r
+        TCHAR buffer[3];\r
+        _sntprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("%d"), i);\r
+        SendMessage(list, LB_ADDSTRING, 0, (LPARAM) buffer);\r
+      }\r
+\r
+      /*\r
+        Size to fit.\r
+        The box is high enough for four rows.  It is wide enough for eight\r
+        columns without scrolling.  With scrollbars it shrinks to two rows.\r
+        Note that the above only holds if we set the column width BEFORE\r
+        adding the strings.\r
+      */\r
+      if (n < 32) {\r
+        int columns = (n - 1) / 4;\r
+        RECT rect;\r
+        GetWindowRect(list, &rect);\r
+        int width = rect.right - rect.left;\r
+        width -= (7 - columns) * 16;\r
+        int height = rect.bottom - rect.top;\r
+        if (n < 4) height -= (int) SendMessage(list, LB_GETITEMHEIGHT, 0, 0) * (4 - n);\r
+        SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\r
+      }\r
+      SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n));\r
+\r
+      SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_CHECKED, 0);\r
+      set_affinity_enabled(0);\r
+\r
       /* Shutdown tab. */\r
       tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN);\r
       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
@@ -907,6 +1225,7 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0);\r
       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0);\r
       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0);\r
+      SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_SETCHECK, BST_CHECKED, 1);\r
 \r
       /* Restart tab. */\r
       tab.pszText = message_string(NSSM_GUI_TAB_EXIT);\r
@@ -923,6 +1242,7 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY));\r
       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN));\r
       SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0);\r
+      SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0);\r
 \r
       /* I/O tab. */\r
       tab.pszText = message_string(NSSM_GUI_TAB_IO);\r
@@ -931,6 +1251,9 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       tablist[NSSM_TAB_IO] = dialog(MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);\r
 \r
+      /* Set defaults. */\r
+      SendDlgItemMessage(tablist[NSSM_TAB_IO], IDC_TIMESTAMP, BM_SETCHECK, BST_UNCHECKED, 0);\r
+\r
       /* Rotation tab. */\r
       tab.pszText = message_string(NSSM_GUI_TAB_ROTATION);\r
       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
@@ -939,6 +1262,7 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       ShowWindow(tablist[NSSM_TAB_ROTATION], SW_HIDE);\r
 \r
       /* Set defaults. */\r
+      SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_UNCHECKED, 0);\r
       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, 0, 0);\r
       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, 0, 0);\r
       set_rotation_enabled(0);\r
@@ -950,6 +1274,38 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
       ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
 \r
+      /* Hooks tab. */\r
+      tab.pszText = message_string(NSSM_GUI_TAB_HOOKS);\r
+      tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_HOOKS, (LPARAM) &tab);\r
+      tablist[NSSM_TAB_HOOKS] = dialog(MAKEINTRESOURCE(IDD_HOOKS), window, tab_dlg);\r
+      ShowWindow(tablist[NSSM_TAB_HOOKS], SW_HIDE);\r
+\r
+      /* Set defaults. */\r
+      combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_EVENT);\r
+      SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_START));\r
+      SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_STOP));\r
+      SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_EXIT));\r
+      SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_POWER));\r
+      SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_ROTATE));\r
+      SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_SETCHECK, BST_UNCHECKED, 0);\r
+      if (_tcslen(service->name)) {\r
+        TCHAR hook_name[HOOK_NAME_LENGTH];\r
+        TCHAR cmd[CMD_LENGTH];\r
+        for (i = 0; hook_event_strings[i]; i++) {\r
+          const TCHAR *hook_event = hook_event_strings[i];\r
+          int j;\r
+          for (j = 0; hook_action_strings[j]; j++) {\r
+            const TCHAR *hook_action = hook_action_strings[j];\r
+            if (! valid_hook_name(hook_event, hook_action, true)) continue;\r
+            if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) continue;\r
+            if (hook_env(hook_event, hook_action, hook_name, _countof(hook_name)) < 0) continue;\r
+            SetEnvironmentVariable(hook_name, cmd);\r
+          }\r
+        }\r
+      }\r
+      set_hook_tab(0, 0, false);\r
+\r
       return 1;\r
 \r
     /* Tab change. */\r