Try to build PDB files even for releases.
[nssm.git] / gui.cpp
diff --git a/gui.cpp b/gui.cpp
index 3f34943..5aaa932 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -1,6 +1,9 @@
 #include "nssm.h"\r
 \r
 #include "nssm.h"\r
 \r
-static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_PROCESS, 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
 static HWND tablist[NSSM_NUM_TABS];\r
 static int selected_tab;\r
 \r
@@ -23,6 +26,13 @@ static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function) {
   return dialog(templ, parent, function, 0);\r
 }\r
 \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 = dialog(MAKEINTRESOURCE(resource), 0, nssm_dlg, (LPARAM) service);\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
@@ -83,18 +93,34 @@ int nssm_gui(int resource, nssm_service_t *service) {
 \r
     /* Log on tab. */\r
     if (service->username) {\r
 \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
     }\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
       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
     /* Process tab. */\r
     if (service->priority) {\r
       int priority = priority_constant_to_index(service->priority);\r
@@ -117,6 +143,10 @@ int nssm_gui(int resource, nssm_service_t *service) {
       }\r
     }\r
 \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
     /* 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
@@ -136,6 +166,9 @@ 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->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
 \r
     /* Restart tab. */\r
     SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, service->throttle_delay, 0);\r
@@ -147,6 +180,7 @@ int nssm_gui(int resource, nssm_service_t *service) {
     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
     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
 \r
     /* Rotation tab. */\r
     if (service->stdout_disposition == CREATE_ALWAYS) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_SETCHECK, BST_CHECKED, 0);\r
@@ -160,8 +194,11 @@ int nssm_gui(int resource, nssm_service_t *service) {
     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
     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
     /* 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(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_STDIO);\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
     if (service->rotate_bytes_high) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ROTATE_BYTES);\r
 \r
     /* Environment tab. */\r
@@ -180,7 +217,7 @@ int nssm_gui(int resource, nssm_service_t *service) {
     if (envlen) {\r
       TCHAR *formatted;\r
       unsigned long newlen;\r
     if (envlen) {\r
       TCHAR *formatted;\r
       unsigned long newlen;\r
-      if (format_environment(env, envlen, &formatted, &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
         popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("nssm_dlg()"));\r
       }\r
       else {\r
@@ -242,13 +279,6 @@ static inline void set_timeout_enabled(unsigned long control, unsigned long depe
   EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], dependent), enabled);\r
 }\r
 \r
   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
-}\r
-\r
 static inline void set_affinity_enabled(unsigned char enabled) {\r
   EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), enabled);\r
 }\r
 static inline void set_affinity_enabled(unsigned char enabled) {\r
   EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), enabled);\r
 }\r
@@ -259,6 +289,100 @@ static inline void set_rotation_enabled(unsigned char enabled) {
   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), enabled);\r
 }\r
 \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) len)) return;\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
@@ -334,12 +458,23 @@ 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
     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
       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
   else {\r
     /* Username. */\r
     service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0);\r
@@ -363,13 +498,25 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
     }\r
 \r
     /*\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
       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
     }\r
     else {\r
       /* Password. */\r
@@ -411,7 +558,7 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
         /* Get first password. */\r
         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1, service->password, (int) service->passwordlen)) {\r
           HeapFree(GetProcessHeap(), 0, password);\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->password);\r
           service->password = 0;\r
           service->passwordlen = 0;\r
@@ -424,9 +571,9 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
 \r
         /* Get confirmation. */\r
         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2, password, (int) service->passwordlen)) {\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
           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->password);\r
           service->password = 0;\r
           service->passwordlen = 0;\r
@@ -440,9 +587,9 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
         /* Compare. */\r
         if (_tcsncmp(password, service->password, service->passwordlen)) {\r
           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\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);\r
+          SecureZeroMemory(password, service->passwordlen * sizeof(TCHAR));\r
           HeapFree(GetProcessHeap(), 0, password);\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->password);\r
           service->password = 0;\r
           service->passwordlen = 0;\r
@@ -455,6 +602,33 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
     }\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
   /* Remaining tabs are only for services we manage. */\r
   if (service->native) return 0;\r
 \r
@@ -478,6 +652,9 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
     }\r
   }\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
   /* 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
@@ -486,6 +663,8 @@ 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
   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
 \r
   /* Get exit action stuff. */\r
   check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay);\r
@@ -498,6 +677,8 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
   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
   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
 \r
   /* Override stdout and/or stderr. */\r
   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
@@ -508,11 +689,14 @@ 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
   /* 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;
+    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
     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
   /* Get environment. */\r
   unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);\r
   if (envlen) {\r
@@ -532,7 +716,7 @@ int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service
 \r
     TCHAR *newenv;\r
     unsigned long newlen;\r
 \r
     TCHAR *newenv;\r
     unsigned long newlen;\r
-    if (unformat_environment(env, envlen, &newenv, &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
       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
@@ -607,6 +791,8 @@ int install(HWND window) {
       return 6;\r
   }\r
 \r
       return 6;\r
   }\r
 \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
   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
   cleanup_nssm_service(service);\r
   return 0;\r
@@ -646,8 +832,8 @@ int remove(HWND window) {
 \r
     case 3:\r
       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
 \r
     case 3:\r
       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
-      return 3;\r
       cleanup_nssm_service(service);\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
 \r
     case 4:\r
       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
@@ -692,6 +878,8 @@ int edit(HWND window, nssm_service_t *orig_service) {
       return 6;\r
   }\r
 \r
       return 6;\r
   }\r
 \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
   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name);\r
   cleanup_nssm_service(service);\r
   return 0;\r
@@ -736,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
     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
       len += _tcslen(localised) + 1;\r
       LocalFree(localised);\r
       TCHAR *filter = browse_filter(i);\r
@@ -746,16 +934,18 @@ void browse(HWND window, TCHAR *current, unsigned long flags, ...) {
     va_end(arg);\r
     /* Remainder of the buffer is already zeroed */\r
   }\r
     va_end(arg);\r
     /* Remainder of the buffer is already zeroed */\r
   }\r
-  ofn.lpstrFile = new TCHAR[PATH_LENGTH];\r
-  if (flags & OFN_NOVALIDATE) {\r
-    /* Directory hack. */\r
-    _sntprintf_s(ofn.lpstrFile, _countof(ofn.lpstrFile), _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
-    ofn.nMaxFile = DIR_LENGTH;\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
   }\r
-  else {
-    _sntprintf_s(ofn.lpstrFile, _countof(ofn.lpstrFile), _TRUNCATE, _T("%s"), current);\r
-    ofn.nMaxFile = PATH_LENGTH;\r
-  }
   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
 \r
   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
 \r
@@ -765,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
     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
 }\r
 \r
 INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {\r
@@ -804,11 +993,15 @@ INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {
 \r
         /* Log on. */\r
         case IDC_LOCALSYSTEM:\r
 \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
           break;\r
 \r
         case IDC_ACCOUNT:\r
-          set_logon_enabled(1);\r
+          set_logon_enabled(0, 1);\r
           break;\r
 \r
         /* Affinity. */\r
           break;\r
 \r
         /* Affinity. */\r
@@ -864,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
           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
       }\r
       return 1;\r
   }\r
@@ -933,7 +1148,14 @@ 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
 \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
       /* Remaining tabs are only for services we manage. */\r
       if (service->native) return 1;\r
@@ -955,6 +1177,8 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
       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
       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
       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
       n = num_cpus();\r
       SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0);\r
@@ -968,17 +1192,17 @@ INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {
         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
         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
-        adding the strings.
+        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
       */\r
       if (n < 32) {\r
         int columns = (n - 1) / 4;\r
         RECT rect;\r
         GetWindowRect(list, &rect);\r
-        int width = rect.right - rect.left;
+        int width = rect.right - rect.left;\r
         width -= (7 - columns) * 16;\r
         int height = rect.bottom - rect.top;\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);
+        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
         SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\r
       }\r
       SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n));\r
@@ -1001,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_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
 \r
       /* Restart tab. */\r
       tab.pszText = message_string(NSSM_GUI_TAB_EXIT);\r
@@ -1026,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
       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
       /* Rotation tab. */\r
       tab.pszText = message_string(NSSM_GUI_TAB_ROTATION);\r
       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
@@ -1046,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
       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
       return 1;\r
 \r
     /* Tab change. */\r