Try to build PDB files even for releases.
[nssm.git] / gui.cpp
diff --git a/gui.cpp b/gui.cpp
index 1459d97..5aaa932 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -1,24 +1,62 @@
 #include "nssm.h"\r
 \r
-static enum { NSSM_TAB_APPLICATION, 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
-int nssm_gui(int resource, TCHAR *name) {\r
+static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function, LPARAM l) {\r
+  /* The caller will deal with GetLastError()... */\r
+  HRSRC resource = FindResourceEx(0, RT_DIALOG, templ, GetUserDefaultLangID());\r
+  if (! resource) {\r
+    if (GetLastError() != ERROR_RESOURCE_LANG_NOT_FOUND) return 0;\r
+    resource = FindResourceEx(0, RT_DIALOG, templ, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));\r
+    if (! resource) return 0;\r
+  }\r
+\r
+  HGLOBAL ret = LoadResource(0, resource);\r
+  if (! ret) return 0;\r
+\r
+  return CreateDialogIndirectParam(0, (DLGTEMPLATE *) ret, parent, function, l);\r
+}\r
+\r
+static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function) {\r
+  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 = CreateDialog(0, MAKEINTRESOURCE(resource), 0, install_dlg);\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
   /* Display the window */\r
   centre_window(dlg);\r
   ShowWindow(dlg, SW_SHOW);\r
 \r
   /* Set service name if given */\r
-  if (name) {\r
-    SetDlgItemText(dlg, IDC_NAME, name);\r
+  if (service->name[0]) {\r
+    SetDlgItemText(dlg, IDC_NAME, service->name);\r
     /* No point making user click remove if the name is already entered */\r
     if (resource == IDD_REMOVE) {\r
       HWND button = GetDlgItem(dlg, IDC_REMOVE);\r
@@ -29,6 +67,167 @@ int nssm_gui(int resource, TCHAR *name) {
     }\r
   }\r
 \r
+  if (resource == IDD_EDIT) {\r
+    /* We'll need the service handle later. */\r
+    SetWindowLongPtr(dlg, DWLP_USER, (LONG_PTR) service);\r
+\r
+    /* Service name can't be edited. */\r
+    EnableWindow(GetDlgItem(dlg, IDC_NAME), 0);\r
+    SetFocus(GetDlgItem(dlg, IDOK));\r
+\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
+    else SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe);\r
+    SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir);\r
+    SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags);\r
+\r
+    /* Details tab. */\r
+    SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname);\r
+    SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description);\r
+    combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
+    SendMessage(combo, CB_SETCURSEL, service->startup, 0);\r
+\r
+    /* Log on tab. */\r
+    if (service->username) {\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_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
+      EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE), 0);\r
+    }\r
+    SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, service->kill_console_delay, 0);\r
+    if (! (service->stop_method & NSSM_STOP_METHOD_WINDOW)) {\r
+      SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_UNCHECKED, 0);\r
+      EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW), 0);\r
+    }\r
+    SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, service->kill_window_delay, 0);\r
+    if (! (service->stop_method & NSSM_STOP_METHOD_THREADS)) {\r
+      SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_UNCHECKED, 0);\r
+      EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS), 0);\r
+    }\r
+    SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, service->kill_threads_delay, 0);\r
+    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 && 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
+      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
+      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
+    }\r
+    if (service->envlen && service->env_extralen) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ENVIRONMENT);\r
+  }\r
+\r
   /* Go! */\r
   MSG message;\r
   while (GetMessage(&message, 0, 0, 0)) {\r
@@ -49,7 +248,7 @@ void centre_window(HWND window) {
 \r
   /* Find window size */\r
   if (! GetWindowRect(window, &size)) return;\r
-  \r
+\r
   /* Find desktop window */\r
   desktop = GetDesktopWindow();\r
   if (! desktop) return;\r
@@ -68,187 +267,533 @@ static inline void check_stop_method(nssm_service_t *service, unsigned long meth
   service->stop_method &= ~method;\r
 }\r
 \r
-static inline void check_method_timeout(HWND tab, unsigned long control, unsigned long *timeout) {\r
+static inline void check_number(HWND tab, unsigned long control, unsigned long *timeout) {\r
   BOOL translated;\r
   unsigned long configured = GetDlgItemInt(tab, control, &translated, 0);\r
   if (translated) *timeout = configured;\r
 }\r
 \r
-static inline void check_io(TCHAR *name, TCHAR *buffer, size_t bufsize, unsigned long control) {\r
+static inline void set_timeout_enabled(unsigned long control, unsigned long dependent) {\r
+  unsigned char enabled = 0;\r
+  if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
+  EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], dependent), enabled);\r
+}\r
+\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 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) bufsize)) return;\r
-  popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name);\r
-  ZeroMemory(buffer, bufsize);\r
+  if (GetDlgItemText(tablist[NSSM_TAB_IO], control, buffer, (int) len)) return;\r
+  popup_message(owner, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name);\r
+  ZeroMemory(buffer, len * sizeof(TCHAR));\r
 }\r
 \r
-/* Install the service. */\r
-int install(HWND window) {\r
-  if (! window) return 1;\r
+/* Set service parameters. */\r
+int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service) {\r
+  if (! service) return 1;\r
 \r
-  nssm_service_t *service = alloc_nssm_service();\r
-  if (service) {\r
-    set_nssm_service_defaults(service);\r
+  set_nssm_service_defaults(service);\r
 \r
-    /* Get service name. */\r
-    if (! GetDlgItemText(window, IDC_NAME, service->name, sizeof(service->name))) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
-      cleanup_nssm_service(service);\r
-      return 2;\r
-    }\r
+  if (orig_service) {\r
+    service->native = orig_service->native;\r
+    service->handle = orig_service->handle;\r
+  }\r
+\r
+  /* Get service name. */\r
+  if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(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
-    /* Get executable name */\r
-    if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe, sizeof(service->exe))) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
+  /* Get executable name */\r
+  if (! service->native) {\r
+    if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe, _countof(service->exe))) {\r
+      popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
       return 3;\r
     }\r
 \r
     /* Get startup directory. */\r
-    if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir, sizeof(service->dir))) {\r
+    if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir, _countof(service->dir))) {\r
       _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
       strip_basename(service->dir);\r
     }\r
 \r
     /* 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, sizeof(service->flags))) {\r
-        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
+      if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags, _countof(service->flags))) {\r
+        popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
         return 4;\r
       }\r
     }\r
+  }\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
-    check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS);\r
-    check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE);\r
-    check_method_timeout(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay);\r
-    check_method_timeout(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay);\r
-    check_method_timeout(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay);\r
+  /* 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(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME);\r
+      return 5;\r
+    }\r
+  }\r
 \r
-    /* Get exit action stuff. */\r
-    check_method_timeout(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay);\r
-    HWND 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
+  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(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION);\r
+      return 5;\r
+    }\r
+  }\r
 \r
-    /* Get I/O stuff. */\r
-    check_io(_T("stdin"), service->stdin_path, sizeof(service->stdin_path), IDC_STDIN);\r
-    check_io(_T("stdout"), service->stdout_path, sizeof(service->stdout_path), IDC_STDOUT);\r
-    check_io(_T("stderr"), service->stderr_path, sizeof(service->stderr_path), IDC_STDERR);\r
+  HWND combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
+  service->startup = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
+  if (service->startup == CB_ERR) service->startup = 0;\r
 \r
-    /* Override stdout and/or stderr. */\r
-    if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
-      if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS;\r
-      if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS;\r
+  /* Get logon stuff. */\r
+  if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
+    if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
+      service->type |= SERVICE_INTERACTIVE_PROCESS;\r
     }\r
-\r
-    /* 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 (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
+    service->username = 0;\r
+    service->usernamelen = 0;\r
+    if (service->password) {\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
-    if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS_ENABLED, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
-      check_method_timeout(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds);\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(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(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
+      return 6;\r
     }\r
-    if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW_ENABLED, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
-      check_method_timeout(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low);\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(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME);\r
+      return 6;\r
     }\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
-        cleanup_nssm_service(service);\r
-        return 5;\r
+    /*\r
+      Special case for well-known accounts.\r
+      Ignore the password if we're editing and the username hasn't changed.\r
+    */\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
-\r
-      if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {\r
-        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
-        HeapFree(GetProcessHeap(), 0, env);\r
-        cleanup_nssm_service(service);\r
-        return 5;\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
-      /* 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
-        HeapFree(GetProcessHeap(), 0, env);\r
-        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
-        cleanup_nssm_service(service);\r
-        return 5;\r
+    }\r
+    else {\r
+      /* Password. */\r
+      service->passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), WM_GETTEXTLENGTH, 0, 0);\r
+      size_t passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), WM_GETTEXTLENGTH, 0, 0);\r
+\r
+      if (! orig_service || ! orig_service->username || ! str_equiv(service->username, orig_service->username) || service->passwordlen || passwordlen) {\r
+        if (! service->passwordlen) {\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
+          return 6;\r
+        }\r
+        if (passwordlen != service->passwordlen) {\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
+          return 6;\r
+        }\r
+        service->passwordlen++;\r
+\r
+        /* Temporary buffer for password validation. */\r
+        TCHAR *password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
+        if (! password) {\r
+          HeapFree(GetProcessHeap(), 0, service->username);\r
+          service->username = 0;\r
+          service->usernamelen = 0;\r
+          popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password confirmation"), _T("install()"));\r
+          return 6;\r
+        }\r
+\r
+        /* Actual password buffer. */\r
+        service->password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
+        if (! service->password) {\r
+          HeapFree(GetProcessHeap(), 0, password);\r
+          HeapFree(GetProcessHeap(), 0, service->username);\r
+          service->username = 0;\r
+          service->usernamelen = 0;\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 * 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(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 * sizeof(TCHAR));\r
+          HeapFree(GetProcessHeap(), 0, password);\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(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(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 * 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
+          return 6;\r
+        }\r
       }\r
+    }\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
-      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
+  /* 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
+  check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS);\r
+  check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE);\r
+  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(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
+    if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS;\r
+    if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS;\r
+  }\r
+\r
+  /* 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(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(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\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
-        HeapFree(GetProcessHeap(), 0, env);\r
-        cleanup_nssm_service(service);\r
-        return 5;\r
-      }\r
+      cleanup_nssm_service(service);\r
+      return 5;\r
+    }\r
 \r
-      if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
-        service->env = env;\r
-        service->envlen = envlen;\r
-      }\r
-      else {\r
-        service->env_extra = env;\r
-        service->env_extralen = envlen;\r
-      }\r
+    TCHAR *newenv;\r
+    unsigned long newlen;\r
+    if (unformat_double_null(env, envlen, &newenv, &newlen)) {\r
+      HeapFree(GetProcessHeap(), 0, env);\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
+    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(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
+    if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
+      service->env = env;\r
+      service->envlen = envlen;\r
     }\r
+    else {\r
+      service->env_extra = env;\r
+      service->env_extralen = envlen;\r
+    }\r
+  }\r
+\r
+  return 0;\r
+}\r
+\r
+/* Install the service. */\r
+int install(HWND window) {\r
+  if (! window) return 1;\r
+\r
+  nssm_service_t *service = alloc_nssm_service();\r
+  if (service) {\r
+    int ret = configure(window, service, 0);\r
+    if (ret) return ret;\r
   }\r
 \r
   /* 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
@@ -261,14 +806,14 @@ int remove(HWND window) {
   nssm_service_t *service = alloc_nssm_service();\r
   if (service) {\r
     /* Get service name */\r
-    if (! GetDlgItemText(window, IDC_NAME, service->name, sizeof(service->name))) {\r
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
+    if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(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
@@ -276,27 +821,66 @@ 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
+      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(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
+      cleanup_nssm_service(service);\r
+      return 4;\r
+  }\r
+\r
+  popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
+  cleanup_nssm_service(service);\r
+  return 0;\r
+}\r
+\r
+int edit(HWND window, nssm_service_t *orig_service) {\r
+  if (! window) return 1;\r
+\r
+  nssm_service_t *service = alloc_nssm_service();\r
+  if (service) {\r
+    int ret = configure(window, service, orig_service);\r
+    if (ret) return ret;\r
+  }\r
+\r
+  switch (edit_service(service, true)) {\r
+    case 1:\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(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_REMOVE_SERVICE_FAILED);\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(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_REMOVED, 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
@@ -340,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
@@ -350,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
@@ -366,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
@@ -378,47 +966,80 @@ 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
-      unsigned long state;\r
+      TCHAR buffer[PATH_LENGTH];\r
+      unsigned char enabled;\r
 \r
       switch (LOWORD(w)) {\r
         /* Browse for application. */\r
         case IDC_BROWSE:\r
           dlg = GetDlgItem(tab, IDC_PATH);\r
-          GetDlgItemText(tab, IDC_PATH, buffer, sizeof(buffer));\r
+          GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
           browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
           /* Fill in startup directory if it wasn't already specified. */\r
-          GetDlgItemText(tab, IDC_DIR, buffer, sizeof(buffer));\r
+          GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
           if (! buffer[0]) {\r
-            GetDlgItemText(tab, IDC_PATH, buffer, sizeof(buffer));\r
+            GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
             strip_basename(buffer);\r
             SetDlgItemText(tab, IDC_DIR, buffer);\r
           }\r
           break;\r
 \r
-          /* Browse for startup directory. */\r
+        /* Browse for startup directory. */\r
         case IDC_BROWSE_DIR:\r
           dlg = GetDlgItem(tab, IDC_DIR);\r
-          GetDlgItemText(tab, IDC_DIR, buffer, sizeof(buffer));\r
+          GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
           browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0);\r
           break;\r
 \r
+        /* Log on. */\r
+        case IDC_LOCALSYSTEM:\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(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
+        case IDC_METHOD_CONSOLE:\r
+          set_timeout_enabled(LOWORD(w), IDC_KILL_CONSOLE);\r
+          break;\r
+\r
+        case IDC_METHOD_WINDOW:\r
+          set_timeout_enabled(LOWORD(w), IDC_KILL_WINDOW);\r
+          break;\r
+\r
+        case IDC_METHOD_THREADS:\r
+          set_timeout_enabled(LOWORD(w), IDC_KILL_THREADS);\r
+          break;\r
+\r
         /* Browse for stdin. */\r
         case IDC_BROWSE_STDIN:\r
           dlg = GetDlgItem(tab, IDC_STDIN);\r
-          GetDlgItemText(tab, IDC_STDIN, buffer, sizeof(buffer));\r
+          GetDlgItemText(tab, IDC_STDIN, buffer, _countof(buffer));\r
           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
           break;\r
 \r
         /* Browse for stdout. */\r
         case IDC_BROWSE_STDOUT:\r
           dlg = GetDlgItem(tab, IDC_STDOUT);\r
-          GetDlgItemText(tab, IDC_STDOUT, buffer, sizeof(buffer));\r
+          GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
           /* Fill in stderr if it wasn't already specified. */\r
-          GetDlgItemText(tab, IDC_STDERR, buffer, sizeof(buffer));\r
+          GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
           if (! buffer[0]) {\r
-            GetDlgItemText(tab, IDC_STDOUT, buffer, sizeof(buffer));\r
+            GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
             SetDlgItemText(tab, IDC_STDERR, buffer);\r
           }\r
           break;\r
@@ -426,19 +1047,37 @@ INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {
         /* Browse for stderr. */\r
         case IDC_BROWSE_STDERR:\r
           dlg = GetDlgItem(tab, IDC_STDERR);\r
-          GetDlgItemText(tab, IDC_STDERR, buffer, sizeof(buffer));\r
+          GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
           break;\r
 \r
         /* Rotation. */\r
         case IDC_ROTATE:\r
-        case IDC_ROTATE_SECONDS_ENABLED:\r
-        case IDC_ROTATE_BYTES_LOW_ENABLED:\r
-          if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) state = BST_CHECKED;\r
-          else state = BST_UNCHECKED;\r
-          SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_SETCHECK, state, 0);\r
-          SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS_ENABLED, BM_SETCHECK, state, 0);\r
-          SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW_ENABLED, BM_SETCHECK, state, 0);\r
+          if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
+          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
@@ -448,14 +1087,20 @@ INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {
 }\r
 \r
 /* Install/remove dialogue callback */\r
-INT_PTR CALLBACK install_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {\r
+INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {\r
+  nssm_service_t *service;\r
+\r
   switch (message) {\r
     /* Creating the dialogue */\r
     case WM_INITDIALOG:\r
+      service = (nssm_service_t *) l;\r
+\r
       SetFocus(GetDlgItem(window, IDC_NAME));\r
 \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
@@ -464,18 +1109,112 @@ INT_PTR CALLBACK install_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       ZeroMemory(&tab, sizeof(tab));\r
       tab.mask = TCIF_TEXT;\r
 \r
+      selected_tab = 0;\r
+\r
       /* Application tab. */\r
-      tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION);\r
+      if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE);\r
+      else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION);\r
       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab);\r
-      tablist[NSSM_TAB_APPLICATION] = CreateDialog(0, MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg);\r
+      if (service->native) {\r
+        tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg);\r
+        EnableWindow(tablist[NSSM_TAB_APPLICATION], 0);\r
+        EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0);\r
+      }\r
+      else tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg);\r
       ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW);\r
 \r
+      /* Details tab. */\r
+      tab.pszText = message_string(NSSM_GUI_TAB_DETAILS);\r
+      tab.cchTextMax = (int) _tcslen(tab.pszText);\r
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DETAILS, (LPARAM) &tab);\r
+      tablist[NSSM_TAB_DETAILS] = dialog(MAKEINTRESOURCE(IDD_DETAILS), window, tab_dlg);\r
+      ShowWindow(tablist[NSSM_TAB_DETAILS], SW_HIDE);\r
+\r
+      /* Set defaults. */\r
+      combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_AUTOMATIC, (LPARAM) message_string(NSSM_GUI_STARTUP_AUTOMATIC));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DELAYED, (LPARAM) message_string(NSSM_GUI_STARTUP_DELAYED));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_MANUAL, (LPARAM) message_string(NSSM_GUI_STARTUP_MANUAL));\r
+      SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DISABLED, (LPARAM) message_string(NSSM_GUI_STARTUP_DISABLED));\r
+      SendMessage(combo, CB_SETCURSEL, NSSM_STARTUP_AUTOMATIC, 0);\r
+\r
+      /* Logon tab. */\r
+      tab.pszText = message_string(NSSM_GUI_TAB_LOGON);\r
+      tab.cchTextMax = (int) _tcslen(tab.pszText);\r
+      SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_LOGON, (LPARAM) &tab);\r
+      tablist[NSSM_TAB_LOGON] = dialog(MAKEINTRESOURCE(IDD_LOGON), window, tab_dlg);\r
+      ShowWindow(tablist[NSSM_TAB_LOGON], SW_HIDE);\r
+\r
+      /* Set defaults. */\r
+      CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\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
       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab);\r
-      tablist[NSSM_TAB_SHUTDOWN] = CreateDialog(0, MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg);\r
+      tablist[NSSM_TAB_SHUTDOWN] = dialog(MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg);\r
       ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE);\r
 \r
       /* Set defaults. */\r
@@ -486,12 +1225,13 @@ INT_PTR CALLBACK install_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
       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab);\r
-      tablist[NSSM_TAB_EXIT] = CreateDialog(0, MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg);\r
+      tablist[NSSM_TAB_EXIT] = dialog(MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg);\r
       ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE);\r
 \r
       /* Set defaults. */\r
@@ -502,33 +1242,69 @@ INT_PTR CALLBACK install_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
       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab);\r
-      tablist[NSSM_TAB_IO] = CreateDialog(0, MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
+      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
       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ROTATION, (LPARAM) &tab);\r
-      tablist[NSSM_TAB_ROTATION] = CreateDialog(0, MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg);\r
+      tablist[NSSM_TAB_ROTATION] = dialog(MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg);\r
       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
 \r
       /* Environment tab. */\r
       tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);\r
       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);\r
-      tablist[NSSM_TAB_ENVIRONMENT] = CreateDialog(0, MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
+      tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
       ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
 \r
-      selected_tab = 0;\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
@@ -548,12 +1324,8 @@ INT_PTR CALLBACK install_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
           selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0);\r
           if (selection != selected_tab) {\r
             ShowWindow(tablist[selected_tab], SW_HIDE);\r
-            /*\r
-              XXX: Sets focus to the service name which isn't ideal but is\r
-                   better than leaving it in another tab.\r
-            */\r
             ShowWindow(tablist[selection], SW_SHOWDEFAULT);\r
-            SetFocus(tablist[selection]);\r
+            SetFocus(GetDlgItem(window, IDOK));\r
             selected_tab = selection;\r
           }\r
           return 1;\r
@@ -566,7 +1338,10 @@ INT_PTR CALLBACK install_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       switch (LOWORD(w)) {\r
         /* OK button */\r
         case IDOK:\r
-          if (! install(window)) PostQuitMessage(0);\r
+          if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) {\r
+            if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0);\r
+          }\r
+          else if (! install(window)) PostQuitMessage(0);\r
           break;\r
 \r
         /* Cancel button */\r