فهرست منبع

Pass parameters around in a data structure.

Instead of static variables and functions taking a dozen arguments we
now use a single data structure, nssm_service_t, for the service.

The code is easier to read and understand, especially when adding more
variables through to functions.
Iain Patterson 12 سال پیش
والد
کامیت
fce252d07b
8فایلهای تغییر یافته به همراه409 افزوده شده و 344 حذف شده
  1. 61 36
      gui.cpp
  2. 1 14
      nssm.h
  3. 36 36
      process.cpp
  4. 4 4
      process.h
  5. 31 28
      registry.cpp
  6. 2 2
      registry.h
  7. 222 216
      service.cpp
  8. 52 8
      service.h

+ 61 - 36
gui.cpp

@@ -72,56 +72,68 @@ void centre_window(HWND window) {
 int install(HWND window) {
 int install(HWND window) {
   if (! window) return 1;
   if (! window) return 1;
 
 
-  /* Check parameters in the window */
-  char name[VALUE_LENGTH];
-  char exe[EXE_LENGTH];
-  char flags[VALUE_LENGTH];
-
-  /* Get service name */
-  if (! GetDlgItemText(window, IDC_NAME, name, sizeof(name))) {
-    popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);
-    return 2;
-  }
-
-  /* Get executable name */
-  if (! GetDlgItemText(window, IDC_PATH, exe, sizeof(exe))) {
-    popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);
-    return 3;
-  }
+  nssm_service_t *service = alloc_nssm_service();
+  if (service) {
+    /* Get service name. */
+    if (! GetDlgItemText(window, IDC_NAME, service->name, sizeof(service->name))) {
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);
+      cleanup_nssm_service(service);
+      return 2;
+    }
 
 
-  /* Get flags */
-  if (SendMessage(GetDlgItem(window, IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) {
-    if (! GetDlgItemText(window, IDC_FLAGS, flags, sizeof(flags))) {
-      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);
-      return 4;
+    /* Get executable name */
+    if (! GetDlgItemText(window, IDC_PATH, service->exe, sizeof(service->exe))) {
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);
+      return 3;
     }
     }
+  
+    /* Get flags */
+    if (SendMessage(GetDlgItem(window, IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) {
+      if (! GetDlgItemText(window, IDC_FLAGS, service->flags, sizeof(service->flags))) {
+        popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);
+        return 4;
+      }
+    }
+
+    memmove(service->dir, service->exe, strlen(service->exe));
+    strip_basename(service->dir);
   }
   }
-  else ZeroMemory(&flags, sizeof(flags));
 
 
-  /* See if it works */
-  switch (install_service(name, exe, flags)) {
+  /* See if it works. */
+  switch (install_service(service)) {
+    case 1:
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "service", "install()");
+      cleanup_nssm_service(service);
+      return 1;
+
     case 2:
     case 2:
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
+      cleanup_nssm_service(service);
       return 2;
       return 2;
 
 
     case 3:
     case 3:
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);
+      cleanup_nssm_service(service);
       return 3;
       return 3;
 
 
     case 4:
     case 4:
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);
+      cleanup_nssm_service(service);
       return 4;
       return 4;
 
 
     case 5:
     case 5:
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);
+      cleanup_nssm_service(service);
       return 5;
       return 5;
 
 
     case 6:
     case 6:
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);
+      cleanup_nssm_service(service);
       return 6;
       return 6;
   }
   }
 
 
-  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, name);
+  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
+  cleanup_nssm_service(service);
   return 0;
   return 0;
 }
 }
 
 
@@ -129,34 +141,47 @@ int install(HWND window) {
 int remove(HWND window) {
 int remove(HWND window) {
   if (! window) return 1;
   if (! window) return 1;
 
 
-  /* Check parameters in the window */
-  char name[VALUE_LENGTH];
+  /* See if it works */
+  nssm_service_t *service = alloc_nssm_service();
+  if (service) {
+    /* Get service name */
+    if (! GetDlgItemText(window, IDC_NAME, service->name, sizeof(service->name))) {
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);
+      cleanup_nssm_service(service);
+      return 2;
+    }
 
 
-  /* Get service name */
-  if (! GetDlgItemText(window, IDC_NAME, name, sizeof(name))) {
-    popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);
-    return 2;
+    /* Confirm */
+    if (popup_message(MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {
+      cleanup_nssm_service(service);
+      return 0;
+    }
   }
   }
 
 
-  /* Confirm */
-  if (popup_message(MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, name) != IDYES) return 0;
+  switch (remove_service(service)) {
+    case 1:
+      popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, "service", "remove()");
+      cleanup_nssm_service(service);
+      return 1;
 
 
-  /* See if it works */
-  switch (remove_service(name)) {
     case 2:
     case 2:
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
+      cleanup_nssm_service(service);
       return 2;
       return 2;
 
 
     case 3:
     case 3:
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);
       return 3;
       return 3;
+      cleanup_nssm_service(service);
 
 
     case 4:
     case 4:
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);
       popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);
+      cleanup_nssm_service(service);
       return 4;
       return 4;
   }
   }
 
 
-  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, name);
+  popup_message(MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
+  cleanup_nssm_service(service);
   return 0;
   return 0;
 }
 }
 
 

+ 1 - 14
nssm.h

@@ -6,13 +6,13 @@
 #include <stdarg.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdio.h>
 #include <windows.h>
 #include <windows.h>
+#include "service.h"
 #include "event.h"
 #include "event.h"
 #include "imports.h"
 #include "imports.h"
 #include "messages.h"
 #include "messages.h"
 #include "process.h"
 #include "process.h"
 #include "registry.h"
 #include "registry.h"
 #include "io.h"
 #include "io.h"
-#include "service.h"
 #include "gui.h"
 #include "gui.h"
 
 
 int str_equiv(const char *, const char *);
 int str_equiv(const char *, const char *);
@@ -22,19 +22,6 @@ int str_equiv(const char *, const char *);
 #define NSSM_VERSIONINFO 2,18,0,0
 #define NSSM_VERSIONINFO 2,18,0,0
 #define NSSM_DATE "2013-11-15"
 #define NSSM_DATE "2013-11-15"
 
 
-/*
-  MSDN says the commandline in CreateProcess() is limited to 32768 characters
-  and the application name to MAX_PATH.
-  A registry key is limited to 255 characters.
-  A registry value is limited to 16383 characters.
-  Therefore we limit the service name to accommodate the path under HKLM.
-*/
-#define EXE_LENGTH MAX_PATH
-#define CMD_LENGTH 32768
-#define KEY_LENGTH 255
-#define VALUE_LENGTH 16383
-#define SERVICE_NAME_LENGTH KEY_LENGTH - 55
-
 /*
 /*
   Throttle the restart of the service if it stops before this many
   Throttle the restart of the service if it stops before this many
   milliseconds have elapsed since startup.  Override in registry.
   milliseconds have elapsed since startup.  Override in registry.

+ 36 - 36
process.cpp

@@ -1,9 +1,6 @@
 #include "nssm.h"
 #include "nssm.h"
 
 
 extern imports_t imports;
 extern imports_t imports;
-extern unsigned long kill_console_delay;
-extern unsigned long kill_window_delay;
-extern unsigned long kill_threads_delay;
 
 
 int get_process_creation_time(HANDLE process_handle, FILETIME *ft) {
 int get_process_creation_time(HANDLE process_handle, FILETIME *ft) {
   FILETIME creation_time, exit_time, kernel_time, user_time;
   FILETIME creation_time, exit_time, kernel_time, user_time;
@@ -31,7 +28,7 @@ int get_process_exit_time(HANDLE process_handle, FILETIME *ft) {
   return 0;
   return 0;
 }
 }
 
 
-int check_parent(char *service_name, PROCESSENTRY32 *pe, unsigned long ppid, FILETIME *pft, FILETIME *exit_time) {
+int check_parent(nssm_service_t *service, PROCESSENTRY32 *pe, unsigned long ppid) {
   /* Check parent process ID matches. */
   /* Check parent process ID matches. */
   if (pe->th32ParentProcessID != ppid) return 1;
   if (pe->th32ParentProcessID != ppid) return 1;
 
 
@@ -45,7 +42,7 @@ int check_parent(char *service_name, PROCESSENTRY32 *pe, unsigned long ppid, FIL
   if (! process_handle) {
   if (! process_handle) {
     char pid_string[16];
     char pid_string[16];
     _snprintf_s(pid_string, sizeof(pid_string), _TRUNCATE, "%lu", pe->th32ProcessID);
     _snprintf_s(pid_string, sizeof(pid_string), _TRUNCATE, "%lu", pe->th32ProcessID);
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service_name, error_string(GetLastError()), 0);
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0);
     return 2;
     return 2;
   }
   }
 
 
@@ -58,10 +55,10 @@ int check_parent(char *service_name, PROCESSENTRY32 *pe, unsigned long ppid, FIL
   CloseHandle(process_handle);
   CloseHandle(process_handle);
 
 
   /* Verify that the parent's creation time is not later. */
   /* Verify that the parent's creation time is not later. */
-  if (CompareFileTime(pft, &ft) > 0) return 4;
+  if (CompareFileTime(&service->creation_time, &ft) > 0) return 4;
 
 
   /* Verify that the parent's exit time is not earlier. */
   /* Verify that the parent's exit time is not earlier. */
-  if (CompareFileTime(exit_time, &ft) < 0) return 5;
+  if (CompareFileTime(&service->exit_time, &ft) < 0) return 5;
 
 
   return 0;
   return 0;
 }
 }
@@ -139,8 +136,9 @@ int kill_threads(char *service_name, kill_t *k) {
 }
 }
 
 
 /* Give the process a chance to die gracefully. */
 /* Give the process a chance to die gracefully. */
-int kill_process(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long stop_method, HANDLE process_handle, unsigned long pid, unsigned long exitcode) {
+int kill_process(nssm_service_t *service, HANDLE process_handle, unsigned long pid, unsigned long exitcode) {
   /* Shouldn't happen. */
   /* Shouldn't happen. */
+  if (! service) return 1;
   if (! pid) return 1;
   if (! pid) return 1;
   if (! process_handle) return 1;
   if (! process_handle) return 1;
 
 
@@ -152,8 +150,8 @@ int kill_process(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI
   kill_t k = { pid, exitcode, 0 };
   kill_t k = { pid, exitcode, 0 };
 
 
   /* Try to send a Control-C event to the console. */
   /* Try to send a Control-C event to the console. */
-  if (stop_method & NSSM_STOP_METHOD_CONSOLE) {
-    if (! kill_console(service_name, service_handle, service_status, process_handle, pid)) return 1;
+  if (service->stop_method & NSSM_STOP_METHOD_CONSOLE) {
+    if (! kill_console(service)) return 1;
   }
   }
 
 
   /*
   /*
@@ -161,10 +159,10 @@ int kill_process(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI
     If the process is a console application it won't have any windows so there's
     If the process is a console application it won't have any windows so there's
     no guarantee of success.
     no guarantee of success.
   */
   */
-  if (stop_method & NSSM_STOP_METHOD_WINDOW) {
+  if (service->stop_method & NSSM_STOP_METHOD_WINDOW) {
     EnumWindows((WNDENUMPROC) kill_window, (LPARAM) &k);
     EnumWindows((WNDENUMPROC) kill_window, (LPARAM) &k);
     if (k.signalled) {
     if (k.signalled) {
-      if (! await_shutdown(__FUNCTION__, service_name, service_handle, service_status, process_handle, kill_window_delay)) return 1;
+      if (! await_shutdown(service, __FUNCTION__, service->kill_window_delay)) return 1;
     }
     }
   }
   }
 
 
@@ -173,29 +171,31 @@ int kill_process(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI
     process.  Console applications might have them (but probably won't) so
     process.  Console applications might have them (but probably won't) so
     there's still no guarantee of success.
     there's still no guarantee of success.
   */
   */
-  if (stop_method & NSSM_STOP_METHOD_THREADS) {
-    if (kill_threads(service_name, &k)) {
-      if (! await_shutdown(__FUNCTION__, service_name, service_handle, service_status, process_handle, kill_threads_delay)) return 1;
+  if (service->stop_method & NSSM_STOP_METHOD_THREADS) {
+    if (kill_threads(service->name, &k)) {
+      if (! await_shutdown(service, __FUNCTION__, service->kill_threads_delay)) return 1;
     }
     }
   }
   }
 
 
   /* We tried being nice.  Time for extreme prejudice. */
   /* We tried being nice.  Time for extreme prejudice. */
-  if (stop_method & NSSM_STOP_METHOD_TERMINATE) {
-    return TerminateProcess(process_handle, exitcode);
+  if (service->stop_method & NSSM_STOP_METHOD_TERMINATE) {
+    return TerminateProcess(service->process_handle, exitcode);
   }
   }
 
 
   return 0;
   return 0;
 }
 }
 
 
 /* Simulate a Control-C event to our console (shared with the app). */
 /* Simulate a Control-C event to our console (shared with the app). */
-int kill_console(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, HANDLE process_handle, unsigned long pid) {
+int kill_console(nssm_service_t *service) {
   unsigned long ret;
   unsigned long ret;
 
 
+  if (! service) return 1;
+
   /* Check we loaded AttachConsole(). */
   /* Check we loaded AttachConsole(). */
   if (! imports.AttachConsole) return 4;
   if (! imports.AttachConsole) return 4;
 
 
   /* Try to attach to the process's console. */
   /* Try to attach to the process's console. */
-  if (! imports.AttachConsole(pid)) {
+  if (! imports.AttachConsole(service->pid)) {
     ret = GetLastError();
     ret = GetLastError();
 
 
     switch (ret) {
     switch (ret) {
@@ -210,7 +210,7 @@ int kill_console(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI
       case ERROR_ACCESS_DENIED:
       case ERROR_ACCESS_DENIED:
       default:
       default:
         /* We already have a console. */
         /* We already have a console. */
-        log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, service_name, error_string(ret), 0);
+        log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_ATTACHCONSOLE_FAILED, service->name, error_string(ret), 0);
         return 3;
         return 3;
     }
     }
   }
   }
@@ -218,42 +218,42 @@ int kill_console(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVI
   /* Ignore the event ourselves. */
   /* Ignore the event ourselves. */
   ret = 0;
   ret = 0;
   if (! SetConsoleCtrlHandler(0, TRUE)) {
   if (! SetConsoleCtrlHandler(0, TRUE)) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, service_name, error_string(GetLastError()), 0);
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETCONSOLECTRLHANDLER_FAILED, service->name, error_string(GetLastError()), 0);
     ret = 4;
     ret = 4;
   }
   }
 
 
   /* Send the event. */
   /* Send the event. */
   if (! ret) {
   if (! ret) {
     if (! GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {
     if (! GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0)) {
-      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, service_name, error_string(GetLastError()), 0);
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GENERATECONSOLECTRLEVENT_FAILED, service->name, error_string(GetLastError()), 0);
       ret = 5;
       ret = 5;
     }
     }
   }
   }
 
 
   /* Detach from the console. */
   /* Detach from the console. */
   if (! FreeConsole()) {
   if (! FreeConsole()) {
-    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, service_name, error_string(GetLastError()), 0);
+    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_FREECONSOLE_FAILED, service->name, error_string(GetLastError()), 0);
   }
   }
 
 
   /* Wait for process to exit. */
   /* Wait for process to exit. */
-  if (await_shutdown(__FUNCTION__, service_name, service_handle, service_status, process_handle, kill_console_delay)) ret = 6;
+  if (await_shutdown(service, __FUNCTION__, service->kill_console_delay)) ret = 6;
 
 
   return ret;
   return ret;
 }
 }
 
 
-void kill_process_tree(char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, unsigned long stop_method, unsigned long pid, unsigned long exitcode, unsigned long ppid, FILETIME *parent_creation_time, FILETIME *parent_exit_time) {
+void kill_process_tree(nssm_service_t *service, unsigned long pid, unsigned long exitcode, unsigned long ppid) {
   /* Shouldn't happen unless the service failed to start. */
   /* Shouldn't happen unless the service failed to start. */
   if (! pid) return;
   if (! pid) return;
 
 
   char pid_string[16], code[16];
   char pid_string[16], code[16];
   _snprintf_s(pid_string, sizeof(pid_string), _TRUNCATE, "%lu", pid);
   _snprintf_s(pid_string, sizeof(pid_string), _TRUNCATE, "%lu", pid);
   _snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode);
   _snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode);
-  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, service_name, pid_string, code, 0);
+  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILLING, service->name, pid_string, code, 0);
 
 
   /* Get a snapshot of all processes in the system. */
   /* Get a snapshot of all processes in the system. */
   HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
   HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
   if (! snapshot) {
   if (! snapshot) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, service_name, error_string(GetLastError()), 0);
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETOOLHELP32SNAPSHOT_PROCESS_FAILED, service->name, error_string(GetLastError()), 0);
     return;
     return;
   }
   }
 
 
@@ -262,25 +262,25 @@ void kill_process_tree(char *service_name, SERVICE_STATUS_HANDLE service_handle,
   pe.dwSize = sizeof(pe);
   pe.dwSize = sizeof(pe);
 
 
   if (! Process32First(snapshot, &pe)) {
   if (! Process32First(snapshot, &pe)) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0);
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service->name, error_string(GetLastError()), 0);
     CloseHandle(snapshot);
     CloseHandle(snapshot);
     return;
     return;
   }
   }
 
 
   /* This is a child of the doomed process so kill it. */
   /* This is a child of the doomed process so kill it. */
-  if (! check_parent(service_name, &pe, pid, parent_creation_time, parent_exit_time)) kill_process_tree(service_name, service_handle, service_status, stop_method, pe.th32ProcessID, exitcode, ppid, parent_creation_time, parent_exit_time);
+  if (! check_parent(service, &pe, pid)) kill_process_tree(service, pe.th32ProcessID, exitcode, ppid);
 
 
   while (true) {
   while (true) {
     /* Try to get the next process. */
     /* Try to get the next process. */
     if (! Process32Next(snapshot, &pe)) {
     if (! Process32Next(snapshot, &pe)) {
       unsigned long ret = GetLastError();
       unsigned long ret = GetLastError();
       if (ret == ERROR_NO_MORE_FILES) break;
       if (ret == ERROR_NO_MORE_FILES) break;
-      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service_name, error_string(GetLastError()), 0);
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_PROCESS_ENUMERATE_FAILED, service->name, error_string(GetLastError()), 0);
       CloseHandle(snapshot);
       CloseHandle(snapshot);
       return;
       return;
     }
     }
 
 
-    if (! check_parent(service_name, &pe, pid, parent_creation_time, parent_exit_time)) kill_process_tree(service_name, service_handle, service_status, stop_method, pe.th32ProcessID, exitcode, ppid, parent_creation_time, parent_exit_time);
+    if (! check_parent(service, &pe, pid)) kill_process_tree(service, pe.th32ProcessID, exitcode, ppid);
   }
   }
 
 
   CloseHandle(snapshot);
   CloseHandle(snapshot);
@@ -288,19 +288,19 @@ void kill_process_tree(char *service_name, SERVICE_STATUS_HANDLE service_handle,
   /* We will need a process handle in order to call TerminateProcess() later. */
   /* We will need a process handle in order to call TerminateProcess() later. */
   HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid);
   HANDLE process_handle = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION | PROCESS_VM_READ | PROCESS_TERMINATE, false, pid);
   if (! process_handle) {
   if (! process_handle) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service_name, error_string(GetLastError()), 0);
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OPENPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0);
     return;
     return;
   }
   }
 
 
   char ppid_string[16];
   char ppid_string[16];
   _snprintf_s(ppid_string, sizeof(ppid_string), _TRUNCATE, "%lu", ppid);
   _snprintf_s(ppid_string, sizeof(ppid_string), _TRUNCATE, "%lu", ppid);
-  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, service_name, 0);
-  if (! kill_process(service_name, service_handle, service_status, stop_method, process_handle, pid, exitcode)) {
+  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_KILL_PROCESS_TREE, pid_string, ppid_string, service->name, 0);
+  if (! kill_process(service, process_handle, pid, exitcode)) {
     /* Maybe it already died. */
     /* Maybe it already died. */
     unsigned long ret;
     unsigned long ret;
     if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) {
     if (! GetExitCodeProcess(process_handle, &ret) || ret == STILL_ACTIVE) {
-      if (stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, service_name, error_string(GetLastError()), 0);
-      else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, service_name, pid_string, NSSM, NSSM_REG_STOP_METHOD_SKIP, 0);
+      if (service->stop_method & NSSM_STOP_METHOD_TERMINATE) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_TERMINATEPROCESS_FAILED, pid_string, service->name, error_string(GetLastError()), 0);
+      else log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_PROCESS_STILL_ACTIVE, service->name, pid_string, NSSM, NSSM_REG_STOP_METHOD_SKIP, 0);
     }
     }
   }
   }
 
 

+ 4 - 4
process.h

@@ -13,9 +13,9 @@ int get_process_creation_time(HANDLE, FILETIME *);
 int get_process_exit_time(HANDLE, FILETIME *);
 int get_process_exit_time(HANDLE, FILETIME *);
 int check_parent(char *, PROCESSENTRY32 *, unsigned long, FILETIME *, FILETIME *);
 int check_parent(char *, PROCESSENTRY32 *, unsigned long, FILETIME *, FILETIME *);
 int CALLBACK kill_window(HWND, LPARAM);
 int CALLBACK kill_window(HWND, LPARAM);
-int kill_threads(char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, kill_t *);
-int kill_console(char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, HANDLE, unsigned long);
-int kill_process(char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, unsigned long, HANDLE, unsigned long, unsigned long);
-void kill_process_tree(char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, unsigned long, unsigned long, unsigned long, unsigned long, FILETIME *, FILETIME *);
+int kill_threads(nssm_service_t *, kill_t *);
+int kill_console(nssm_service_t *);
+int kill_process(nssm_service_t *, HANDLE, unsigned long, unsigned long);
+void kill_process_tree(nssm_service_t *, unsigned long, unsigned long, unsigned long);
 
 
 #endif
 #endif

+ 31 - 28
registry.cpp

@@ -26,10 +26,10 @@ int create_messages() {
   return 0;
   return 0;
 }
 }
 
 
-int create_parameters(char *service_name, char *exe, char *flags, char *dir) {
+int create_parameters(nssm_service_t *service) {
   /* Get registry */
   /* Get registry */
   char registry[KEY_LENGTH];
   char registry[KEY_LENGTH];
-  if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service_name) < 0) {
+  if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service->name) < 0) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "NSSM_REGISTRY", "create_parameters()", 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "NSSM_REGISTRY", "create_parameters()", 0);
     return 1;
     return 1;
   }
   }
@@ -42,19 +42,19 @@ int create_parameters(char *service_name, char *exe, char *flags, char *dir) {
   }
   }
 
 
   /* Try to create the parameters */
   /* Try to create the parameters */
-  if (RegSetValueEx(key, NSSM_REG_EXE, 0, REG_EXPAND_SZ, (const unsigned char *) exe, (unsigned long) strlen(exe) + 1) != ERROR_SUCCESS) {
+  if (RegSetValueEx(key, NSSM_REG_EXE, 0, REG_EXPAND_SZ, (const unsigned char *) service->exe, (unsigned long) strlen(service->exe) + 1) != ERROR_SUCCESS) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXE, error_string(GetLastError()), 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_EXE, error_string(GetLastError()), 0);
     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
     RegCloseKey(key);
     RegCloseKey(key);
     return 3;
     return 3;
   }
   }
-  if (RegSetValueEx(key, NSSM_REG_FLAGS, 0, REG_EXPAND_SZ, (const unsigned char *) flags, (unsigned long) strlen(flags) + 1) != ERROR_SUCCESS) {
+  if (RegSetValueEx(key, NSSM_REG_FLAGS, 0, REG_EXPAND_SZ, (const unsigned char *) service->flags, (unsigned long) strlen(service->flags) + 1) != ERROR_SUCCESS) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_FLAGS, error_string(GetLastError()), 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_FLAGS, error_string(GetLastError()), 0);
     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
     RegCloseKey(key);
     RegCloseKey(key);
     return 4;
     return 4;
   }
   }
-  if (RegSetValueEx(key, NSSM_REG_DIR, 0, REG_EXPAND_SZ, (const unsigned char *) dir, (unsigned long) strlen(dir) + 1) != ERROR_SUCCESS) {
+  if (RegSetValueEx(key, NSSM_REG_DIR, 0, REG_EXPAND_SZ, (const unsigned char *) service->dir, (unsigned long) strlen(service->dir) + 1) != ERROR_SUCCESS) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_DIR, error_string(GetLastError()), 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_DIR, error_string(GetLastError()), 0);
     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
     RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY);
     RegCloseKey(key);
     RegCloseKey(key);
@@ -123,6 +123,9 @@ int set_environment(char *service_name, HKEY key, char **env) {
   /* Probably not possible */
   /* Probably not possible */
   if (! envlen) return 0;
   if (! envlen) return 0;
 
 
+  /* Previously initialised? */
+  if (*env) HeapFree(GetProcessHeap(), 0, *env);
+
   *env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen);
   *env = (char *) HeapAlloc(GetProcessHeap(), 0, envlen);
   if (! *env) {
   if (! *env) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "environment registry", "set_environment()", 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "environment registry", "set_environment()", 0);
@@ -261,12 +264,12 @@ void override_milliseconds(char *service_name, HKEY key, char *value, unsigned l
   if (! ok) *buffer = default_value;
   if (! ok) *buffer = default_value;
 }
 }
 
 
-int get_parameters(char *service_name, char *exe, unsigned long exelen, char *flags, unsigned long flagslen, char *dir, unsigned long dirlen, char **env, unsigned long *throttle_delay, unsigned long *stop_method, unsigned long *kill_console_delay, unsigned long *kill_window_delay, unsigned long *kill_threads_delay, STARTUPINFO *si) {
+int get_parameters(nssm_service_t *service, STARTUPINFO *si) {
   unsigned long ret;
   unsigned long ret;
 
 
   /* Get registry */
   /* Get registry */
   char registry[KEY_LENGTH];
   char registry[KEY_LENGTH];
-  if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service_name) < 0) {
+  if (_snprintf_s(registry, sizeof(registry), _TRUNCATE, NSSM_REGISTRY, service->name) < 0) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "NSSM_REGISTRY", "get_parameters()", 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "NSSM_REGISTRY", "get_parameters()", 0);
     return 1;
     return 1;
   }
   }
@@ -279,50 +282,50 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl
   }
   }
 
 
   /* Try to get executable file - MUST succeed */
   /* Try to get executable file - MUST succeed */
-  if (expand_parameter(key, NSSM_REG_EXE, exe, exelen, false)) {
+  if (expand_parameter(key, NSSM_REG_EXE, service->exe, sizeof(service->exe), false)) {
     RegCloseKey(key);
     RegCloseKey(key);
     return 3;
     return 3;
   }
   }
 
 
   /* Try to get flags - may fail and we don't care */
   /* Try to get flags - may fail and we don't care */
-  if (expand_parameter(key, NSSM_REG_FLAGS, flags, flagslen, false)) {
-    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service_name, exe, 0);
-    ZeroMemory(flags, flagslen);
+  if (expand_parameter(key, NSSM_REG_FLAGS, service->flags, sizeof(service->flags), false)) {
+    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_FLAGS, NSSM_REG_FLAGS, service->name, service->exe, 0);
+    ZeroMemory(service->flags, sizeof(service->flags));
   }
   }
 
 
   /* Try to get startup directory - may fail and we fall back to a default */
   /* Try to get startup directory - may fail and we fall back to a default */
-  if (expand_parameter(key, NSSM_REG_DIR, dir, dirlen, true) || ! dir[0]) {
+  if (expand_parameter(key, NSSM_REG_DIR, service->dir, sizeof(service->dir), true) || ! service->dir[0]) {
     /* Our buffers are defined to be long enough for this to be safe */
     /* Our buffers are defined to be long enough for this to be safe */
     size_t i;
     size_t i;
-    for (i = strlen(exe); i && exe[i] != '\\' && exe[i] != '/'; i--);
+    for (i = strlen(service->exe); i && service->exe[i] != '\\' && service->exe[i] != '/'; i--);
     if (i) {
     if (i) {
-      memmove(dir, exe, i);
-      dir[i] = '\0';
+      memmove(service->dir, service->exe, i);
+      service->dir[i] = '\0';
     }
     }
     else {
     else {
       /* Help! */
       /* Help! */
-      ret = GetWindowsDirectory(dir, dirlen);
-      if (! ret || ret > dirlen) {
-        log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service_name, 0);
+      ret = GetWindowsDirectory(service->dir, sizeof(service->dir));
+      if (! ret || ret > sizeof(service->dir)) {
+        log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_NO_DIR_AND_NO_FALLBACK, NSSM_REG_DIR, service->name, 0);
         RegCloseKey(key);
         RegCloseKey(key);
         return 4;
         return 4;
       }
       }
     }
     }
-    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service_name, dir, 0);
+    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_NO_DIR, NSSM_REG_DIR, service->name, service->dir, 0);
   }
   }
 
 
   /* Try to get environment variables - may fail */
   /* Try to get environment variables - may fail */
-  set_environment(service_name, key, env);
+  set_environment(service->name, key, &service->env);
 
 
   /* Try to get stdout and stderr */
   /* Try to get stdout and stderr */
   if (get_output_handles(key, si)) {
   if (get_output_handles(key, si)) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service_name, 0);
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_OUTPUT_HANDLES_FAILED, service->name, 0);
     RegCloseKey(key);
     RegCloseKey(key);
     return 5;
     return 5;
   }
   }
 
 
   /* Try to get throttle restart delay */
   /* Try to get throttle restart delay */
-  override_milliseconds(service_name, key, NSSM_REG_THROTTLE, throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
+  override_milliseconds(service->name, key, NSSM_REG_THROTTLE, &service->throttle_delay, NSSM_RESET_THROTTLE_RESTART, NSSM_EVENT_BOGUS_THROTTLE);
 
 
   /* Try to get service stop flags. */
   /* Try to get service stop flags. */
   unsigned long type = REG_DWORD;
   unsigned long type = REG_DWORD;
@@ -333,7 +336,7 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl
   if (ret != ERROR_SUCCESS) {
   if (ret != ERROR_SUCCESS) {
     if (ret != ERROR_FILE_NOT_FOUND) {
     if (ret != ERROR_FILE_NOT_FOUND) {
       if (type != REG_DWORD) {
       if (type != REG_DWORD) {
-        log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service_name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
+        log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_BOGUS_STOP_METHOD_SKIP, service->name, NSSM_REG_STOP_METHOD_SKIP, NSSM, 0);
       }
       }
       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
       else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_QUERYVALUE_FAILED, NSSM_REG_STOP_METHOD_SKIP, error_string(GetLastError()), 0);
     }
     }
@@ -341,13 +344,13 @@ int get_parameters(char *service_name, char *exe, unsigned long exelen, char *fl
   else stop_ok = true;
   else stop_ok = true;
 
 
   /* Try all methods except those requested to be skipped. */
   /* Try all methods except those requested to be skipped. */
-  *stop_method = ~0;
-  if (stop_ok) *stop_method &= ~stop_method_skip;
+  service->stop_method = ~0;
+  if (stop_ok) service->stop_method &= ~stop_method_skip;
 
 
   /* Try to get kill delays - may fail. */
   /* Try to get kill delays - may fail. */
-  override_milliseconds(service_name, key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, kill_console_delay, NSSM_KILL_CONSOLE_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD);
-  override_milliseconds(service_name, key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, kill_window_delay, NSSM_KILL_WINDOW_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD);
-  override_milliseconds(service_name, key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, kill_threads_delay, NSSM_KILL_THREADS_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD);
+  override_milliseconds(service->name, key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, &service->kill_console_delay, NSSM_KILL_CONSOLE_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_CONSOLE_GRACE_PERIOD);
+  override_milliseconds(service->name, key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, &service->kill_window_delay, NSSM_KILL_WINDOW_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_WINDOW_GRACE_PERIOD);
+  override_milliseconds(service->name, key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, &service->kill_threads_delay, NSSM_KILL_THREADS_GRACE_PERIOD, NSSM_EVENT_BOGUS_KILL_THREADS_GRACE_PERIOD);
 
 
   /* Close registry */
   /* Close registry */
   RegCloseKey(key);
   RegCloseKey(key);

+ 2 - 2
registry.h

@@ -21,7 +21,7 @@
 #define NSSM_STDIO_LENGTH 29
 #define NSSM_STDIO_LENGTH 29
 
 
 int create_messages();
 int create_messages();
-int create_parameters(char *, char *, char *, char *);
+int create_parameters(nssm_service_t *);
 int create_exit_action(char *, const char *);
 int create_exit_action(char *, const char *);
 int set_environment(char *, HKEY, char **);
 int set_environment(char *, HKEY, char **);
 int expand_parameter(HKEY, char *, char *, unsigned long, bool, bool);
 int expand_parameter(HKEY, char *, char *, unsigned long, bool, bool);
@@ -31,7 +31,7 @@ int set_number(HKEY, char *, unsigned long);
 int get_number(HKEY, char *, unsigned long *, bool);
 int get_number(HKEY, char *, unsigned long *, bool);
 int get_number(HKEY, char *, unsigned long *);
 int get_number(HKEY, char *, unsigned long *);
 void override_milliseconds(char *, HKEY, char *, unsigned long *, unsigned long, unsigned long);
 void override_milliseconds(char *, HKEY, char *, unsigned long *, unsigned long, unsigned long);
-int get_parameters(char *, char *, unsigned long, char *, unsigned long, char *, unsigned long, char **, unsigned long *, unsigned long *, unsigned long *, unsigned long *, unsigned long *, STARTUPINFO *);
+int get_parameters(nssm_service_t *, STARTUPINFO *);
 int get_exit_action(char *, unsigned long *, unsigned char *, bool *);
 int get_exit_action(char *, unsigned long *, unsigned char *, bool *);
 
 
 #endif
 #endif

+ 222 - 216
service.cpp

@@ -1,37 +1,14 @@
 #include "nssm.h"
 #include "nssm.h"
 
 
 bool is_admin;
 bool is_admin;
-SERVICE_STATUS service_status;
-SERVICE_STATUS_HANDLE service_handle;
-HANDLE process_handle;
-HANDLE wait_handle;
-unsigned long pid;
-static char service_name[SERVICE_NAME_LENGTH];
-char exe[EXE_LENGTH];
-char flags[CMD_LENGTH];
-char dir[MAX_PATH];
-bool stopping;
-bool allow_restart;
-unsigned long throttle_delay;
-unsigned long stop_method;
-unsigned long kill_console_delay;
-unsigned long kill_window_delay;
-unsigned long kill_threads_delay;
-CRITICAL_SECTION throttle_section;
-CONDITION_VARIABLE throttle_condition;
-HANDLE throttle_timer;
-LARGE_INTEGER throttle_duetime;
 bool use_critical_section;
 bool use_critical_section;
-FILETIME creation_time;
 
 
 extern imports_t imports;
 extern imports_t imports;
 
 
 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
 static enum { NSSM_EXIT_RESTART, NSSM_EXIT_IGNORE, NSSM_EXIT_REALLY, NSSM_EXIT_UNCLEAN } exit_actions;
 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
 static const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 };
 
 
-static unsigned long throttle;
-
-static inline int throttle_milliseconds() {
+static inline int throttle_milliseconds(unsigned long throttle) {
   /* pow() operates on doubles. */
   /* pow() operates on doubles. */
   int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
   int ret = 1; for (unsigned long i = 1; i < throttle; i++) ret *= 2;
   return ret * 1000;
   return ret * 1000;
@@ -42,7 +19,7 @@ static inline int throttle_milliseconds() {
   control immediately.
   control immediately.
 */
 */
 static unsigned long WINAPI shutdown_service(void *arg) {
 static unsigned long WINAPI shutdown_service(void *arg) {
-  return stop_service(0, true, true);
+  return stop_service((nssm_service_t *) arg, 0, true, true);
 }
 }
 
 
 /* Connect to the service manager */
 /* Connect to the service manager */
@@ -56,26 +33,46 @@ SC_HANDLE open_service_manager() {
   return ret;
   return ret;
 }
 }
 
 
+/* Allocate and zero memory for a service. */
+nssm_service_t *alloc_nssm_service() {
+  nssm_service_t *service = (nssm_service_t *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(nssm_service_t));
+  if (! service) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service", "alloc_nssm_service()", 0);
+  return service;
+}
+
+/* Free memory for a service. */
+void cleanup_nssm_service(nssm_service_t *service) {
+  if (! service) return;
+  if (service->env) HeapFree(GetProcessHeap(), 0, service->env);
+  if (service->handle) CloseServiceHandle(service->handle);
+  if (service->process_handle) CloseHandle(service->process_handle);
+  if (service->wait_handle) UnregisterWait(service->process_handle);
+  if (service->throttle_section_initialised) DeleteCriticalSection(&service->throttle_section);
+  if (service->throttle_timer) CloseHandle(service->throttle_timer);
+  HeapFree(GetProcessHeap(), 0, service);
+}
+
 /* About to install the service */
 /* About to install the service */
 int pre_install_service(int argc, char **argv) {
 int pre_install_service(int argc, char **argv) {
   /* Show the dialogue box if we didn't give the service name and path */
   /* Show the dialogue box if we didn't give the service name and path */
   if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
   if (argc < 2) return nssm_gui(IDD_INSTALL, argv[0]);
 
 
+  nssm_service_t *service = alloc_nssm_service();
+  if (! service) {
+    print_message(stderr, NSSM_EVENT_OUT_OF_MEMORY, "service", "pre_install_service()");
+    return 1;
+  }
+
+  memmove(service->name, argv[0], strlen(argv[0]));
+  memmove(service->exe, argv[1], strlen(argv[1]));
+
   /* Arguments are optional */
   /* Arguments are optional */
-  char *flags;
   size_t flagslen = 0;
   size_t flagslen = 0;
   size_t s = 0;
   size_t s = 0;
-  int i;
+  size_t i;
   for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
   for (i = 2; i < argc; i++) flagslen += strlen(argv[i]) + 1;
   if (! flagslen) flagslen = 1;
   if (! flagslen) flagslen = 1;
 
 
-  flags = (char *) HeapAlloc(GetProcessHeap(), 0, flagslen);
-  if (! flags) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "flags", "pre_install_service()", 0);
-    return 2;
-  }
-  ZeroMemory(flags, flagslen);
-
   /*
   /*
     This probably isn't UTF8-safe and should use std::string or something
     This probably isn't UTF8-safe and should use std::string or something
     but it's been broken for the best part of a decade and due for a rewrite
     but it's been broken for the best part of a decade and due for a rewrite
@@ -84,29 +81,46 @@ int pre_install_service(int argc, char **argv) {
   */
   */
   for (i = 2; i < argc; i++) {
   for (i = 2; i < argc; i++) {
     size_t len = strlen(argv[i]);
     size_t len = strlen(argv[i]);
-    memmove(flags + s, argv[i], len);
+    memmove(service->flags + s, argv[i], len);
     s += len;
     s += len;
-    if (i < argc - 1) flags[s++] = ' ';
+    if (i < argc - 1) service->flags[s++] = ' ';
   }
   }
 
 
-  return install_service(argv[0], argv[1], flags);
+  /* Work out directory name */
+  size_t len = strlen(service->exe);
+  for (i = len; i && service->exe[i] != '\\' && service->exe[i] != '/'; i--);
+  memmove(service->dir, service->exe, i);
+  service->dir[i] = '\0';
+
+  int ret = install_service(service);
+  cleanup_nssm_service(service);
+  return ret;
 }
 }
 
 
 /* About to remove the service */
 /* About to remove the service */
 int pre_remove_service(int argc, char **argv) {
 int pre_remove_service(int argc, char **argv) {
   /* Show dialogue box if we didn't pass service name and "confirm" */
   /* Show dialogue box if we didn't pass service name and "confirm" */
   if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
   if (argc < 2) return nssm_gui(IDD_REMOVE, argv[0]);
-  if (str_equiv(argv[1], "confirm")) return remove_service(argv[0]);
+  if (str_equiv(argv[1], "confirm")) {
+    nssm_service_t *service = alloc_nssm_service();
+    memmove(service->name, argv[0], strlen(argv[0]));
+    int ret = remove_service(service);
+    cleanup_nssm_service(service);
+    return ret;
+  }
   print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
   print_message(stderr, NSSM_MESSAGE_PRE_REMOVE_SERVICE);
   return 100;
   return 100;
 }
 }
 
 
 /* Install the service */
 /* Install the service */
-int install_service(char *name, char *exe, char *flags) {
+int install_service(nssm_service_t *service) {
+  if (! service) return 1;
+
   /* Open service manager */
   /* Open service manager */
   SC_HANDLE services = open_service_manager();
   SC_HANDLE services = open_service_manager();
   if (! services) {
   if (! services) {
     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
     print_message(stderr, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);
+    cleanup_nssm_service(service);
     return 2;
     return 2;
   }
   }
 
 
@@ -126,42 +140,36 @@ int install_service(char *name, char *exe, char *flags) {
     return 4;
     return 4;
   }
   }
 
 
-  /* Work out directory name */
-  size_t len = strlen(exe);
-  size_t i;
-  for (i = len; i && exe[i] != '\\' && exe[i] != '/'; i--);
-  char dir[MAX_PATH];
-  memmove(dir, exe, i);
-  dir[i] = '\0';
-
   /* Create the service */
   /* Create the service */
-  SC_HANDLE service = CreateService(services, name, name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, command, 0, 0, 0, 0, 0);
-  if (! service) {
+  service->handle = CreateService(services, service->name, service->name, SC_MANAGER_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, command, 0, 0, 0, 0, 0);
+  if (! service->handle) {
     print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
     print_message(stderr, NSSM_MESSAGE_CREATESERVICE_FAILED);
     CloseServiceHandle(services);
     CloseServiceHandle(services);
     return 5;
     return 5;
   }
   }
 
 
   /* Now we need to put the parameters into the registry */
   /* Now we need to put the parameters into the registry */
-  if (create_parameters(name, exe, flags, dir)) {
+  if (create_parameters(service)) {
     print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
     print_message(stderr, NSSM_MESSAGE_CREATE_PARAMETERS_FAILED);
-    DeleteService(service);
+    DeleteService(service->handle);
     CloseServiceHandle(services);
     CloseServiceHandle(services);
     return 6;
     return 6;
   }
   }
 
 
-  set_service_recovery(service, name);
+  set_service_recovery(service);
+
+  print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);
 
 
   /* Cleanup */
   /* Cleanup */
-  CloseServiceHandle(service);
   CloseServiceHandle(services);
   CloseServiceHandle(services);
 
 
-  print_message(stdout, NSSM_MESSAGE_SERVICE_INSTALLED, name);
   return 0;
   return 0;
 }
 }
 
 
 /* Remove the service */
 /* Remove the service */
-int remove_service(char *name) {
+int remove_service(nssm_service_t *service) {
+  if (! service) return 1;
+
   /* Open service manager */
   /* Open service manager */
   SC_HANDLE services = open_service_manager();
   SC_HANDLE services = open_service_manager();
   if (! services) {
   if (! services) {
@@ -170,33 +178,34 @@ int remove_service(char *name) {
   }
   }
 
 
   /* Try to open the service */
   /* Try to open the service */
-  SC_HANDLE service = OpenService(services, name, SC_MANAGER_ALL_ACCESS);
-  if (! service) {
+  service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
+  if (! service->handle) {
     print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
     print_message(stderr, NSSM_MESSAGE_OPENSERVICE_FAILED);
     CloseServiceHandle(services);
     CloseServiceHandle(services);
     return 3;
     return 3;
   }
   }
 
 
   /* Try to delete the service */
   /* Try to delete the service */
-  if (! DeleteService(service)) {
+  if (! DeleteService(service->handle)) {
     print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
     print_message(stderr, NSSM_MESSAGE_DELETESERVICE_FAILED);
-    CloseServiceHandle(service);
     CloseServiceHandle(services);
     CloseServiceHandle(services);
     return 4;
     return 4;
   }
   }
 
 
   /* Cleanup */
   /* Cleanup */
-  CloseServiceHandle(service);
   CloseServiceHandle(services);
   CloseServiceHandle(services);
 
 
-  print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, name);
+  print_message(stdout, NSSM_MESSAGE_SERVICE_REMOVED, service->name);
   return 0;
   return 0;
 }
 }
 
 
 /* Service initialisation */
 /* Service initialisation */
 void WINAPI service_main(unsigned long argc, char **argv) {
 void WINAPI service_main(unsigned long argc, char **argv) {
-  if (_snprintf_s(service_name, sizeof(service_name), _TRUNCATE, "%s", argv[0]) < 0) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service_name", "service_main()", 0);
+  nssm_service_t *service = alloc_nssm_service();
+  if (! service) return;
+
+  if (_snprintf_s(service->name, sizeof(service->name), _TRUNCATE, "%s", argv[0]) < 0) {
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "service->name", "service_main()", 0);
     return;
     return;
   }
   }
 
 
@@ -205,95 +214,88 @@ void WINAPI service_main(unsigned long argc, char **argv) {
   else use_critical_section = false;
   else use_critical_section = false;
 
 
   /* Initialise status */
   /* Initialise status */
-  ZeroMemory(&service_status, sizeof(service_status));
-  service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
-  service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
-  service_status.dwWin32ExitCode = NO_ERROR;
-  service_status.dwServiceSpecificExitCode = 0;
-  service_status.dwCheckPoint = 0;
-  service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
+  ZeroMemory(&service->status, sizeof(service->status));
+  service->status.dwServiceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS;
+  service->status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
+  service->status.dwWin32ExitCode = NO_ERROR;
+  service->status.dwServiceSpecificExitCode = 0;
+  service->status.dwCheckPoint = 0;
+  service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
 
 
   /* Signal we AREN'T running the server */
   /* Signal we AREN'T running the server */
-  process_handle = 0;
-  pid = 0;
+  service->process_handle = 0;
+  service->pid = 0;
 
 
   /* Register control handler */
   /* Register control handler */
-  service_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, 0);
-  if (! service_handle) {
+  service->status_handle = RegisterServiceCtrlHandlerEx(NSSM, service_control_handler, (void *) service);
+  if (! service->status_handle) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_REGISTERSERVICECTRLHANDER_FAILED, error_string(GetLastError()), 0);
     return;
     return;
   }
   }
 
 
-  log_service_control(service_name, 0, true);
+  log_service_control(service->name, 0, true);
 
 
-  service_status.dwCurrentState = SERVICE_START_PENDING;
-  service_status.dwWaitHint = throttle_delay + NSSM_WAITHINT_MARGIN;
-  SetServiceStatus(service_handle, &service_status);
+  service->status.dwCurrentState = SERVICE_START_PENDING;
+  service->status.dwWaitHint = service->throttle_delay + NSSM_WAITHINT_MARGIN;
+  SetServiceStatus(service->status_handle, &service->status);
 
 
   if (is_admin) {
   if (is_admin) {
     /* Try to create the exit action parameters; we don't care if it fails */
     /* Try to create the exit action parameters; we don't care if it fails */
-    create_exit_action(argv[0], exit_action_strings[0]);
+    create_exit_action(service->name, exit_action_strings[0]);
 
 
-    set_service_recovery(0, service_name);
+    SC_HANDLE services = open_service_manager();
+    if (services) {
+      service->handle = OpenService(services, service->name, SC_MANAGER_ALL_ACCESS);
+      set_service_recovery(service);
+      CloseServiceHandle(services);
+    }
   }
   }
 
 
   /* Used for signalling a resume if the service pauses when throttled. */
   /* Used for signalling a resume if the service pauses when throttled. */
-  if (use_critical_section) InitializeCriticalSection(&throttle_section);
+  if (use_critical_section) {
+    InitializeCriticalSection(&service->throttle_section);
+    service->throttle_section_initialised = true;
+  }
   else {
   else {
-    throttle_timer = CreateWaitableTimer(0, 1, 0);
-    if (! throttle_timer) {
-      log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service_name, error_string(GetLastError()), 0);
+    service->throttle_timer = CreateWaitableTimer(0, 1, 0);
+    if (! service->throttle_timer) {
+      log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_CREATEWAITABLETIMER_FAILED, service->name, error_string(GetLastError()), 0);
     }
     }
   }
   }
 
 
-  monitor_service();
+  monitor_service(service);
 }
 }
 
 
 /* Make sure service recovery actions are taken where necessary */
 /* Make sure service recovery actions are taken where necessary */
-void set_service_recovery(SC_HANDLE service, char *service_name) {
-  SC_HANDLE services = 0;
-
-  if (! service) {
-    services = open_service_manager();
-    if (! services) return;
-
-    service = OpenService(services, service_name, SC_MANAGER_ALL_ACCESS);
-    if (! service) return;
-  }
-
+void set_service_recovery(nssm_service_t *service) {
   SERVICE_FAILURE_ACTIONS_FLAG flag;
   SERVICE_FAILURE_ACTIONS_FLAG flag;
   ZeroMemory(&flag, sizeof(flag));
   ZeroMemory(&flag, sizeof(flag));
   flag.fFailureActionsOnNonCrashFailures = true;
   flag.fFailureActionsOnNonCrashFailures = true;
 
 
   /* This functionality was added in Vista so the call may fail */
   /* This functionality was added in Vista so the call may fail */
-  if (! ChangeServiceConfig2(service, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
+  if (! ChangeServiceConfig2(service->handle, SERVICE_CONFIG_FAILURE_ACTIONS_FLAG, &flag)) {
     unsigned long error = GetLastError();
     unsigned long error = GetLastError();
     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
     /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
     if (error != ERROR_INVALID_LEVEL) {
     if (error != ERROR_INVALID_LEVEL) {
-      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service_name, error_string(error), 0);
+      log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CHANGESERVICECONFIG2_FAILED, service->name, error_string(error), 0);
     }
     }
   }
   }
-
-  if (services) {
-    CloseServiceHandle(service);
-    CloseServiceHandle(services);
-  }
 }
 }
 
 
-int monitor_service() {
+int monitor_service(nssm_service_t *service) {
   /* Set service status to started */
   /* Set service status to started */
-  int ret = start_service();
+  int ret = start_service(service);
   if (ret) {
   if (ret) {
     char code[16];
     char code[16];
     _snprintf_s(code, sizeof(code), _TRUNCATE, "%d", ret);
     _snprintf_s(code, sizeof(code), _TRUNCATE, "%d", ret);
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, exe, service_name, ret, 0);
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_START_SERVICE_FAILED, service->exe, service->name, ret, 0);
     return ret;
     return ret;
   }
   }
-  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, exe, flags, service_name, dir, 0);
+  log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_STARTED_SERVICE, service->exe, service->flags, service->name, service->dir, 0);
 
 
   /* Monitor service */
   /* Monitor service */
-  if (! RegisterWaitForSingleObject(&wait_handle, process_handle, end_service, (void *) pid, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
-    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service_name, exe, error_string(GetLastError()), 0);
+  if (! RegisterWaitForSingleObject(&service->wait_handle, service->process_handle, end_service, (void *) service, INFINITE, WT_EXECUTEONLYONCE | WT_EXECUTELONGFUNCTION)) {
+    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_REGISTERWAITFORSINGLEOBJECT_FAILED, service->name, service->exe, error_string(GetLastError()), 0);
   }
   }
 
 
   return 0;
   return 0;
@@ -343,6 +345,8 @@ void log_service_control(char *service_name, unsigned long control, bool handled
 
 
 /* Service control handler */
 /* Service control handler */
 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
 unsigned long WINAPI service_control_handler(unsigned long control, unsigned long event, void *data, void *context) {
+  nssm_service_t *service = (nssm_service_t *) context;
+
   switch (control) {
   switch (control) {
     case SERVICE_CONTROL_INTERROGATE:
     case SERVICE_CONTROL_INTERROGATE:
       /* We always keep the service status up-to-date so this is a no-op. */
       /* We always keep the service status up-to-date so this is a no-op. */
@@ -350,40 +354,40 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
 
 
     case SERVICE_CONTROL_SHUTDOWN:
     case SERVICE_CONTROL_SHUTDOWN:
     case SERVICE_CONTROL_STOP:
     case SERVICE_CONTROL_STOP:
-      log_service_control(service_name, control, true);
+      log_service_control(service->name, control, true);
       /*
       /*
         We MUST acknowledge the stop request promptly but we're committed to
         We MUST acknowledge the stop request promptly but we're committed to
         waiting for the application to exit.  Spawn a new thread to wait
         waiting for the application to exit.  Spawn a new thread to wait
         while we acknowledge the request.
         while we acknowledge the request.
       */
       */
-      if (! CreateThread(NULL, 0, shutdown_service, (void *) service_name, 0, NULL)) {
+      if (! CreateThread(NULL, 0, shutdown_service, context, 0, NULL)) {
         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
         log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATETHREAD_FAILED, error_string(GetLastError()), 0);
 
 
         /*
         /*
           We couldn't create a thread to tidy up so we'll have to force the tidyup
           We couldn't create a thread to tidy up so we'll have to force the tidyup
           to complete in time in this thread.
           to complete in time in this thread.
         */
         */
-        kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
-        kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
-        kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
+        service->kill_console_delay = NSSM_KILL_CONSOLE_GRACE_PERIOD;
+        service->kill_window_delay = NSSM_KILL_WINDOW_GRACE_PERIOD;
+        service->kill_threads_delay = NSSM_KILL_THREADS_GRACE_PERIOD;
 
 
-        stop_service(0, true, true);
+        stop_service(service, 0, true, true);
       }
       }
       return NO_ERROR;
       return NO_ERROR;
 
 
     case SERVICE_CONTROL_CONTINUE:
     case SERVICE_CONTROL_CONTINUE:
-      log_service_control(service_name, control, true);
-      throttle = 0;
-      if (use_critical_section) imports.WakeConditionVariable(&throttle_condition);
+      log_service_control(service->name, control, true);
+      service->throttle = 0;
+      if (use_critical_section) imports.WakeConditionVariable(&service->throttle_condition);
       else {
       else {
-        if (! throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
-        ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
-        SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
+        if (! service->throttle_timer) return ERROR_CALL_NOT_IMPLEMENTED;
+        ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
+        SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
       }
       }
-      service_status.dwCurrentState = SERVICE_CONTINUE_PENDING;
-      service_status.dwWaitHint = throttle_milliseconds() + NSSM_WAITHINT_MARGIN;
-      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service_name, 0);
-      SetServiceStatus(service_handle, &service_status);
+      service->status.dwCurrentState = SERVICE_CONTINUE_PENDING;
+      service->status.dwWaitHint = throttle_milliseconds(service->throttle) + NSSM_WAITHINT_MARGIN;
+      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_RESET_THROTTLE, service->name, 0);
+      SetServiceStatus(service->status_handle, &service->status);
       return NO_ERROR;
       return NO_ERROR;
 
 
     case SERVICE_CONTROL_PAUSE:
     case SERVICE_CONTROL_PAUSE:
@@ -391,21 +395,21 @@ unsigned long WINAPI service_control_handler(unsigned long control, unsigned lon
         We don't accept pause messages but it isn't possible to register
         We don't accept pause messages but it isn't possible to register
         only for continue messages so we have to handle this case.
         only for continue messages so we have to handle this case.
       */
       */
-      log_service_control(service_name, control, false);
+      log_service_control(service->name, control, false);
       return ERROR_CALL_NOT_IMPLEMENTED;
       return ERROR_CALL_NOT_IMPLEMENTED;
   }
   }
 
 
   /* Unknown control */
   /* Unknown control */
-  log_service_control(service_name, control, false);
+  log_service_control(service->name, control, false);
   return ERROR_CALL_NOT_IMPLEMENTED;
   return ERROR_CALL_NOT_IMPLEMENTED;
 }
 }
 
 
 /* Start the service */
 /* Start the service */
-int start_service() {
-  stopping = false;
-  allow_restart = true;
+int start_service(nssm_service_t *service) {
+  service->stopping = false;
+  service->allow_restart = true;
 
 
-  if (process_handle) return 0;
+  if (service->process_handle) return 0;
 
 
   /* Allocate a STARTUPINFO structure for a new process */
   /* Allocate a STARTUPINFO structure for a new process */
   STARTUPINFO si;
   STARTUPINFO si;
@@ -417,36 +421,35 @@ int start_service() {
   ZeroMemory(&pi, sizeof(pi));
   ZeroMemory(&pi, sizeof(pi));
 
 
   /* Get startup parameters */
   /* Get startup parameters */
-  char *env = 0;
-  int ret = get_parameters(service_name, exe, sizeof(exe), flags, sizeof(flags), dir, sizeof(dir), &env, &throttle_delay, &stop_method, &kill_console_delay, &kill_window_delay, &kill_threads_delay, &si);
+  int ret = get_parameters(service, &si);
   if (ret) {
   if (ret) {
-    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service_name, 0);
-    return stop_service(2, true, true);
+    log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_GET_PARAMETERS_FAILED, service->name, 0);
+    return stop_service(service, 2, true, true);
   }
   }
 
 
   /* Launch executable with arguments */
   /* Launch executable with arguments */
   char cmd[CMD_LENGTH];
   char cmd[CMD_LENGTH];
-  if (_snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "\"%s\" %s", exe, flags) < 0) {
+  if (_snprintf_s(cmd, sizeof(cmd), _TRUNCATE, "\"%s\" %s", service->exe, service->flags) < 0) {
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
     log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_OUT_OF_MEMORY, "command line", "start_service", 0);
     close_output_handles(&si);
     close_output_handles(&si);
-    return stop_service(2, true, true);
+    return stop_service(service, 2, true, true);
   }
   }
 
 
-  throttle_restart();
+  throttle_restart(service);
 
 
   bool inherit_handles = false;
   bool inherit_handles = false;
   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
   if (si.dwFlags & STARTF_USESTDHANDLES) inherit_handles = true;
-  if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, env, dir, &si, &pi)) {
+  if (! CreateProcess(0, cmd, 0, 0, inherit_handles, 0, service->env, service->dir, &si, &pi)) {
     unsigned long error = GetLastError();
     unsigned long error = GetLastError();
-    if (error == ERROR_INVALID_PARAMETER && env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service_name, exe, NSSM_REG_ENV, 0);
-    else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service_name, exe, error_string(error), 0);
+    if (error == ERROR_INVALID_PARAMETER && service->env) log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED_INVALID_ENVIRONMENT, service->name, service->exe, NSSM_REG_ENV, 0);
+    else log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_CREATEPROCESS_FAILED, service->name, service->exe, error_string(error), 0);
     close_output_handles(&si);
     close_output_handles(&si);
-    return stop_service(3, true, true);
+    return stop_service(service, 3, true, true);
   }
   }
-  process_handle = pi.hProcess;
-  pid = pi.dwProcessId;
+  service->process_handle = pi.hProcess;
+  service->pid = pi.dwProcessId;
 
 
-  if (get_process_creation_time(process_handle, &creation_time)) ZeroMemory(&creation_time, sizeof(creation_time));
+  if (get_process_creation_time(service->process_handle, &service->creation_time)) ZeroMemory(&service->creation_time, sizeof(service->creation_time));
 
 
   close_output_handles(&si);
   close_output_handles(&si);
 
 
@@ -455,71 +458,74 @@ int start_service() {
     but be mindful of the fact that we are blocking the service control manager
     but be mindful of the fact that we are blocking the service control manager
     so abandon the wait before too much time has elapsed.
     so abandon the wait before too much time has elapsed.
   */
   */
-  unsigned long delay = throttle_delay;
+  unsigned long delay = service->throttle_delay;
   if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
   if (delay > NSSM_SERVICE_STATUS_DEADLINE) {
     char delay_milliseconds[16];
     char delay_milliseconds[16];
     _snprintf_s(delay_milliseconds, sizeof(delay_milliseconds), _TRUNCATE, "%lu", delay);
     _snprintf_s(delay_milliseconds, sizeof(delay_milliseconds), _TRUNCATE, "%lu", delay);
     char deadline_milliseconds[16];
     char deadline_milliseconds[16];
     _snprintf_s(deadline_milliseconds, sizeof(deadline_milliseconds), _TRUNCATE, "%lu", NSSM_SERVICE_STATUS_DEADLINE);
     _snprintf_s(deadline_milliseconds, sizeof(deadline_milliseconds), _TRUNCATE, "%lu", NSSM_SERVICE_STATUS_DEADLINE);
-    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service_name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
+    log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_STARTUP_DELAY_TOO_LONG, service->name, delay_milliseconds, NSSM, deadline_milliseconds, 0);
     delay = NSSM_SERVICE_STATUS_DEADLINE;
     delay = NSSM_SERVICE_STATUS_DEADLINE;
   }
   }
-  unsigned long deadline = WaitForSingleObject(process_handle, delay);
+  unsigned long deadline = WaitForSingleObject(service->process_handle, delay);
 
 
   /* Signal successful start */
   /* Signal successful start */
-  service_status.dwCurrentState = SERVICE_RUNNING;
-  SetServiceStatus(service_handle, &service_status);
+  service->status.dwCurrentState = SERVICE_RUNNING;
+  SetServiceStatus(service->status_handle, &service->status);
 
 
   /* Continue waiting for a clean startup. */
   /* Continue waiting for a clean startup. */
   if (deadline == WAIT_TIMEOUT) {
   if (deadline == WAIT_TIMEOUT) {
-    if (throttle_delay > delay) {
-      if (WaitForSingleObject(process_handle, throttle_delay - delay) == WAIT_TIMEOUT) throttle = 0;
+    if (service->throttle_delay > delay) {
+      if (WaitForSingleObject(service->process_handle, service->throttle_delay - delay) == WAIT_TIMEOUT) service->throttle = 0;
     }
     }
-    else throttle = 0;
+    else service->throttle = 0;
   }
   }
 
 
   return 0;
   return 0;
 }
 }
 
 
 /* Stop the service */
 /* Stop the service */
-int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
-  allow_restart = false;
-  if (wait_handle) UnregisterWait(wait_handle);
+int stop_service(nssm_service_t *service, unsigned long exitcode, bool graceful, bool default_action) {
+  service->allow_restart = false;
+  if (service->wait_handle) {
+    UnregisterWait(service->wait_handle);
+    service->wait_handle = 0;
+  }
 
 
   if (default_action && ! exitcode && ! graceful) {
   if (default_action && ! exitcode && ! graceful) {
-    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service_name, exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY] ,0);
+    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_GRACEFUL_SUICIDE, service->name, service->exe, exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_UNCLEAN], exit_action_strings[NSSM_EXIT_REALLY] ,0);
     graceful = true;
     graceful = true;
   }
   }
 
 
   /* Signal we are stopping */
   /* Signal we are stopping */
   if (graceful) {
   if (graceful) {
-    service_status.dwCurrentState = SERVICE_STOP_PENDING;
-    service_status.dwWaitHint = NSSM_WAITHINT_MARGIN;
-    SetServiceStatus(service_handle, &service_status);
+    service->status.dwCurrentState = SERVICE_STOP_PENDING;
+    service->status.dwWaitHint = NSSM_WAITHINT_MARGIN;
+    SetServiceStatus(service->status_handle, &service->status);
   }
   }
 
 
   /* Nothing to do if service isn't running */
   /* Nothing to do if service isn't running */
-  if (pid) {
+  if (service->pid) {
     /* Shut down service */
     /* Shut down service */
-    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service_name, exe, 0);
-    kill_process(service_name, service_handle, &service_status, stop_method, process_handle, pid, 0);
+    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_TERMINATEPROCESS, service->name, service->exe, 0);
+    kill_process(service, service->process_handle, service->pid, 0);
   }
   }
-  else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service_name, exe, 0);
+  else log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_PROCESS_ALREADY_STOPPED, service->name, service->exe, 0);
 
 
-  end_service((void *) pid, true);
+  end_service((void *) service, true);
 
 
   /* Signal we stopped */
   /* Signal we stopped */
   if (graceful) {
   if (graceful) {
-    service_status.dwCurrentState = SERVICE_STOPPED;
+    service->status.dwCurrentState = SERVICE_STOPPED;
     if (exitcode) {
     if (exitcode) {
-      service_status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
-      service_status.dwServiceSpecificExitCode = exitcode;
+      service->status.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR;
+      service->status.dwServiceSpecificExitCode = exitcode;
     }
     }
     else {
     else {
-      service_status.dwWin32ExitCode = NO_ERROR;
-      service_status.dwServiceSpecificExitCode = 0;
+      service->status.dwWin32ExitCode = NO_ERROR;
+      service->status.dwServiceSpecificExitCode = 0;
     }
     }
-    SetServiceStatus(service_handle, &service_status);
+    SetServiceStatus(service->status_handle, &service->status);
   }
   }
 
 
   return exitcode;
   return exitcode;
@@ -527,19 +533,21 @@ int stop_service(unsigned long exitcode, bool graceful, bool default_action) {
 
 
 /* Callback function triggered when the server exits */
 /* Callback function triggered when the server exits */
 void CALLBACK end_service(void *arg, unsigned char why) {
 void CALLBACK end_service(void *arg, unsigned char why) {
-  if (stopping) return;
+  nssm_service_t *service = (nssm_service_t *) arg;
 
 
-  stopping = true;
+  if (service->stopping) return;
 
 
-  pid = (unsigned long) arg;
+  service->stopping = true;
 
 
   /* Check exit code */
   /* Check exit code */
   unsigned long exitcode = 0;
   unsigned long exitcode = 0;
   char code[16];
   char code[16];
-  FILETIME exit_time;
-  GetExitCodeProcess(process_handle, &exitcode);
-  if (exitcode == STILL_ACTIVE || get_process_exit_time(process_handle, &exit_time)) GetSystemTimeAsFileTime(&exit_time);
-  CloseHandle(process_handle);
+  GetExitCodeProcess(service->process_handle, &exitcode);
+  if (exitcode == STILL_ACTIVE || get_process_exit_time(service->process_handle, &service->exit_time)) GetSystemTimeAsFileTime(&service->exit_time);
+  CloseHandle(service->process_handle);
+
+  service->process_handle = 0;
+  service->pid = 0;
 
 
   /*
   /*
     Log that the service ended BEFORE logging about killing the process
     Log that the service ended BEFORE logging about killing the process
@@ -547,12 +555,12 @@ void CALLBACK end_service(void *arg, unsigned char why) {
   */
   */
   if (! why) {
   if (! why) {
     _snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode);
     _snprintf_s(code, sizeof(code), _TRUNCATE, "%lu", exitcode);
-    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, exe, service_name, code, 0);
+    log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_ENDED_SERVICE, service->exe, service->name, code, 0);
   }
   }
 
 
   /* Clean up. */
   /* Clean up. */
   if (exitcode == STILL_ACTIVE) exitcode = 0;
   if (exitcode == STILL_ACTIVE) exitcode = 0;
-  kill_process_tree(service_name, service_handle, &service_status, stop_method, pid, exitcode, pid, &creation_time, &exit_time);
+  kill_process_tree(service, service->pid, exitcode, service->pid);
 
 
   /*
   /*
     The why argument is true if our wait timed out or false otherwise.
     The why argument is true if our wait timed out or false otherwise.
@@ -561,13 +569,13 @@ void CALLBACK end_service(void *arg, unsigned char why) {
     this is a controlled shutdown, and don't take any restart action.
     this is a controlled shutdown, and don't take any restart action.
   */
   */
   if (why) return;
   if (why) return;
-  if (! allow_restart) return;
+  if (! service->allow_restart) return;
 
 
   /* What action should we take? */
   /* What action should we take? */
   int action = NSSM_EXIT_RESTART;
   int action = NSSM_EXIT_RESTART;
   unsigned char action_string[ACTION_LEN];
   unsigned char action_string[ACTION_LEN];
   bool default_action;
   bool default_action;
-  if (! get_exit_action(service_name, &exitcode, action_string, &default_action)) {
+  if (! get_exit_action(service->name, &exitcode, action_string, &default_action)) {
     for (int i = 0; exit_action_strings[i]; i++) {
     for (int i = 0; exit_action_strings[i]; i++) {
       if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
       if (! _strnicmp((const char *) action_string, exit_action_strings[i], ACTION_LEN)) {
         action = i;
         action = i;
@@ -576,69 +584,67 @@ void CALLBACK end_service(void *arg, unsigned char why) {
     }
     }
   }
   }
 
 
-  process_handle = 0;
-  pid = 0;
   switch (action) {
   switch (action) {
     /* Try to restart the service or return failure code to service manager */
     /* Try to restart the service or return failure code to service manager */
     case NSSM_EXIT_RESTART:
     case NSSM_EXIT_RESTART:
-      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service_name, code, exit_action_strings[action], exe, 0);
-      while (monitor_service()) {
-        log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, exe, service_name, 0);
+      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_RESTART, service->name, code, exit_action_strings[action], service->exe, 0);
+      while (monitor_service(service)) {
+        log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_RESTART_SERVICE_FAILED, service->exe, service->name, 0);
         Sleep(30000);
         Sleep(30000);
       }
       }
     break;
     break;
 
 
     /* Do nothing, just like srvany would */
     /* Do nothing, just like srvany would */
     case NSSM_EXIT_IGNORE:
     case NSSM_EXIT_IGNORE:
-      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service_name, code, exit_action_strings[action], exe, 0);
+      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_IGNORE, service->name, code, exit_action_strings[action], service->exe, 0);
       Sleep(INFINITE);
       Sleep(INFINITE);
     break;
     break;
 
 
     /* Tell the service manager we are finished */
     /* Tell the service manager we are finished */
     case NSSM_EXIT_REALLY:
     case NSSM_EXIT_REALLY:
-      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service_name, code, exit_action_strings[action], 0);
-      stop_service(exitcode, true, default_action);
+      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_REALLY, service->name, code, exit_action_strings[action], 0);
+      stop_service(service, exitcode, true, default_action);
     break;
     break;
 
 
     /* Fake a crash so pre-Vista service managers will run recovery actions. */
     /* Fake a crash so pre-Vista service managers will run recovery actions. */
     case NSSM_EXIT_UNCLEAN:
     case NSSM_EXIT_UNCLEAN:
-      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service_name, code, exit_action_strings[action], 0);
-      stop_service(exitcode, false, default_action);
+      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_EXIT_UNCLEAN, service->name, code, exit_action_strings[action], 0);
+      stop_service(service, exitcode, false, default_action);
       free_imports();
       free_imports();
       exit(exitcode);
       exit(exitcode);
     break;
     break;
   }
   }
 }
 }
 
 
-void throttle_restart() {
+void throttle_restart(nssm_service_t *service) {
   /* This can't be a restart if the service is already running. */
   /* This can't be a restart if the service is already running. */
-  if (! throttle++) return;
+  if (! service->throttle++) return;
 
 
-  int ms = throttle_milliseconds();
+  int ms = throttle_milliseconds(service->throttle);
 
 
-  if (throttle > 7) throttle = 8;
+  if (service->throttle > 7) service->throttle = 8;
 
 
   char threshold[8], milliseconds[8];
   char threshold[8], milliseconds[8];
-  _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%lu", throttle_delay);
+  _snprintf_s(threshold, sizeof(threshold), _TRUNCATE, "%lu", service->throttle_delay);
   _snprintf_s(milliseconds, sizeof(milliseconds), _TRUNCATE, "%lu", ms);
   _snprintf_s(milliseconds, sizeof(milliseconds), _TRUNCATE, "%lu", ms);
-  log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service_name, threshold, milliseconds, 0);
+  log_event(EVENTLOG_WARNING_TYPE, NSSM_EVENT_THROTTLED, service->name, threshold, milliseconds, 0);
 
 
-  if (use_critical_section) EnterCriticalSection(&throttle_section);
-  else if (throttle_timer) {
-    ZeroMemory(&throttle_duetime, sizeof(throttle_duetime));
-    throttle_duetime.QuadPart = 0 - (ms * 10000LL);
-    SetWaitableTimer(throttle_timer, &throttle_duetime, 0, 0, 0, 0);
+  if (use_critical_section) EnterCriticalSection(&service->throttle_section);
+  else if (service->throttle_timer) {
+    ZeroMemory(&service->throttle_duetime, sizeof(service->throttle_duetime));
+    service->throttle_duetime.QuadPart = 0 - (ms * 10000LL);
+    SetWaitableTimer(service->throttle_timer, &service->throttle_duetime, 0, 0, 0, 0);
   }
   }
 
 
-  service_status.dwCurrentState = SERVICE_PAUSED;
-  SetServiceStatus(service_handle, &service_status);
+  service->status.dwCurrentState = SERVICE_PAUSED;
+  SetServiceStatus(service->status_handle, &service->status);
 
 
   if (use_critical_section) {
   if (use_critical_section) {
-    imports.SleepConditionVariableCS(&throttle_condition, &throttle_section, ms);
-    LeaveCriticalSection(&throttle_section);
+    imports.SleepConditionVariableCS(&service->throttle_condition, &service->throttle_section, ms);
+    LeaveCriticalSection(&service->throttle_section);
   }
   }
   else {
   else {
-    if (throttle_timer) WaitForSingleObject(throttle_timer, INFINITE);
+    if (service->throttle_timer) WaitForSingleObject(service->throttle_timer, INFINITE);
     else Sleep(ms);
     else Sleep(ms);
   }
   }
 }
 }
@@ -671,7 +677,7 @@ void throttle_restart() {
            0 if the wait completed.
            0 if the wait completed.
           -1 on error.
           -1 on error.
 */
 */
-int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDLE service_handle, SERVICE_STATUS *service_status, HANDLE process_handle, unsigned long timeout) {
+int await_shutdown(nssm_service_t *service, char *function_name, unsigned long timeout) {
   unsigned long interval;
   unsigned long interval;
   unsigned long waithint;
   unsigned long waithint;
   unsigned long ret;
   unsigned long ret;
@@ -690,24 +696,24 @@ int await_shutdown(char *function_name, char *service_name, SERVICE_STATUS_HANDL
 
 
   _snprintf_s(timeout_milliseconds, sizeof(timeout_milliseconds), _TRUNCATE, "%lu", timeout);
   _snprintf_s(timeout_milliseconds, sizeof(timeout_milliseconds), _TRUNCATE, "%lu", timeout);
 
 
-  waithint = service_status->dwWaitHint;
+  waithint = service->status.dwWaitHint;
   waited = 0;
   waited = 0;
   while (waited < timeout) {
   while (waited < timeout) {
     interval = timeout - waited;
     interval = timeout - waited;
     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
     if (interval > NSSM_SERVICE_STATUS_DEADLINE) interval = NSSM_SERVICE_STATUS_DEADLINE;
 
 
-    service_status->dwCurrentState = SERVICE_STOP_PENDING;
-    service_status->dwWaitHint += interval;
-    service_status->dwCheckPoint++;
-    SetServiceStatus(service_handle, service_status);
+    service->status.dwCurrentState = SERVICE_STOP_PENDING;
+    service->status.dwWaitHint += interval;
+    service->status.dwCheckPoint++;
+    SetServiceStatus(service->status_handle, &service->status);
 
 
     if (waited) {
     if (waited) {
       _snprintf_s(waited_milliseconds, sizeof(waited_milliseconds), _TRUNCATE, "%lu", waited);
       _snprintf_s(waited_milliseconds, sizeof(waited_milliseconds), _TRUNCATE, "%lu", waited);
       _snprintf_s(interval_milliseconds, sizeof(interval_milliseconds), _TRUNCATE, "%lu", interval);
       _snprintf_s(interval_milliseconds, sizeof(interval_milliseconds), _TRUNCATE, "%lu", interval);
-      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service_name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
+      log_event(EVENTLOG_INFORMATION_TYPE, NSSM_EVENT_AWAITING_SHUTDOWN, function, service->name, waited_milliseconds, interval_milliseconds, timeout_milliseconds, 0);
     }
     }
 
 
-    switch (WaitForSingleObject(process_handle, interval)) {
+    switch (WaitForSingleObject(service->process_handle, interval)) {
       case WAIT_OBJECT_0:
       case WAIT_OBJECT_0:
         ret = 0;
         ret = 0;
         goto awaited;
         goto awaited;

+ 52 - 8
service.h

@@ -1,24 +1,68 @@
 #ifndef SERVICE_H
 #ifndef SERVICE_H
 #define SERVICE_H
 #define SERVICE_H
 
 
+/*
+  MSDN says the commandline in CreateProcess() is limited to 32768 characters
+  and the application name to MAX_PATH.
+  A registry key is limited to 255 characters.
+  A registry value is limited to 16383 characters.
+  Therefore we limit the service name to accommodate the path under HKLM.
+*/
+#define EXE_LENGTH MAX_PATH
+#define CMD_LENGTH 32768
+#define KEY_LENGTH 255
+#define VALUE_LENGTH 16383
+#define SERVICE_NAME_LENGTH KEY_LENGTH - 55
+
 #define ACTION_LEN 16
 #define ACTION_LEN 16
 
 
+typedef struct {
+  char name[SERVICE_NAME_LENGTH];
+  char exe[EXE_LENGTH];
+  char flags[VALUE_LENGTH];
+  char dir[MAX_PATH];
+  char *env;
+  unsigned long throttle_delay;
+  unsigned long stop_method;
+  unsigned long kill_console_delay;
+  unsigned long kill_window_delay;
+  unsigned long kill_threads_delay;
+  SC_HANDLE handle;
+  SERVICE_STATUS status;
+  SERVICE_STATUS_HANDLE status_handle;
+  HANDLE process_handle;
+  unsigned long pid;
+  HANDLE wait_handle;
+  bool stopping;
+  bool allow_restart;
+  unsigned long throttle;
+  CRITICAL_SECTION throttle_section;
+  bool throttle_section_initialised;
+  CONDITION_VARIABLE throttle_condition;
+  HANDLE throttle_timer;
+  LARGE_INTEGER throttle_duetime;
+  FILETIME creation_time;
+  FILETIME exit_time;
+} nssm_service_t;
+
 void WINAPI service_main(unsigned long, char **);
 void WINAPI service_main(unsigned long, char **);
 char *service_control_text(unsigned long);
 char *service_control_text(unsigned long);
 void log_service_control(char *, unsigned long, bool);
 void log_service_control(char *, unsigned long, bool);
 unsigned long WINAPI service_control_handler(unsigned long, unsigned long, void *, void *);
 unsigned long WINAPI service_control_handler(unsigned long, unsigned long, void *, void *);
 
 
+nssm_service_t *alloc_nssm_service();
+void cleanup_nssm_service(nssm_service_t *);
 SC_HANDLE open_service_manager();
 SC_HANDLE open_service_manager();
 int pre_install_service(int, char **);
 int pre_install_service(int, char **);
 int pre_remove_service(int, char **);
 int pre_remove_service(int, char **);
-int install_service(char *, char *, char *);
-int remove_service(char *);
-void set_service_recovery(SC_HANDLE, char *);
-int monitor_service();
-int start_service();
-int stop_service(unsigned long, bool, bool);
+int install_service(nssm_service_t *);
+int remove_service(nssm_service_t *);
+void set_service_recovery(nssm_service_t *);
+int monitor_service(nssm_service_t *);
+int start_service(nssm_service_t *);
+int stop_service(nssm_service_t *, unsigned long, bool, bool);
 void CALLBACK end_service(void *, unsigned char);
 void CALLBACK end_service(void *, unsigned char);
-void throttle_restart();
-int await_shutdown(char *, char *, SERVICE_STATUS_HANDLE, SERVICE_STATUS *, HANDLE, unsigned long);
+void throttle_restart(nssm_service_t *);
+int await_shutdown(nssm_service_t *, char *, unsigned long);
 
 
 #endif
 #endif