From b3a4446ab363b5895aa2e04c645ed5d5dfa00442 Mon Sep 17 00:00:00 2001 From: Iain Patterson Date: Wed, 20 Nov 2013 15:15:09 +0000 Subject: [PATCH] GUI overhaul. We now have a much less sucky GUI with tabs, combo boxes and all sorts of whizzbang stuff. The new GUI allows setting most of the advanced features we support directly from the service installer without having to poke about in the registry. --- ChangeLog.txt | 4 + README.txt | 3 + gui.cpp | 276 +++++++++++++++++++++++++++++++++++++++++++++----- messages.mc | 122 ++++++++++++++++++++-- nssm.h | 7 ++ nssm.rc | 239 +++++++++++++++++++++++++++++++++++-------- registry.cpp | 27 +++-- resource.h | 29 +++++- service.cpp | 3 +- service.h | 4 + 10 files changed, 620 insertions(+), 94 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 0317979..668cebb 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,7 @@ +Changes since 2.18 +----------------- + * The GUI is significantly less sucky. + Changes since 2.17 ----------------- * Timeouts for each shutdown method can be configured in diff --git a/README.txt b/README.txt index f5ddd2f..f5e6ed8 100644 --- a/README.txt +++ b/README.txt @@ -46,6 +46,9 @@ to an arbitrary path. Since version 2.18, NSSM can be configured to wait a user-specified amount of time for the application to exit when shutting down. +Since version 2.19, many more service options can be configured with the +GUI installer as well as via the registry. + Usage ----- diff --git a/gui.cpp b/gui.cpp index 7773a18..013f2f0 100644 --- a/gui.cpp +++ b/gui.cpp @@ -1,5 +1,9 @@ #include "nssm.h" +static enum { NSSM_TAB_APPLICATION, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_NUM_TABS }; +static HWND tablist[NSSM_NUM_TABS]; +static int selected_tab; + static void strip_basename(char *buffer) { size_t len = strlen(buffer); size_t i; @@ -68,7 +72,25 @@ void centre_window(HWND window) { MoveWindow(window, x, y, size.right - size.left, size.bottom - size.top, 0); } -/* Install the service */ +static inline void check_stop_method(nssm_service_t *service, unsigned long method, unsigned long control) { + if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) return; + service->stop_method &= ~method; +} + +static inline void check_method_timeout(HWND tab, unsigned long control, unsigned long *timeout) { + BOOL translated; + unsigned long configured = GetDlgItemInt(tab, control, &translated, 0); + if (translated) *timeout = configured; +} + +static inline void check_io(char *name, char *buffer, size_t bufsize, unsigned long control) { + if (! SendMessage(GetDlgItem(tablist[NSSM_TAB_IO], control), WM_GETTEXTLENGTH, 0, 0)) return; + if (GetDlgItemText(tablist[NSSM_TAB_IO], control, buffer, (int) bufsize)) return; + popup_message(MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name); + ZeroMemory(buffer, bufsize); +} + +/* Install the service. */ int install(HWND window) { if (! window) return 1; @@ -82,21 +104,45 @@ int install(HWND window) { } /* Get executable name */ - if (! GetDlgItemText(window, IDC_PATH, service->exe, sizeof(service->exe))) { + if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], 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)) { + + /* Get startup directory. */ + if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir, sizeof(service->dir))) { + memmove(service->dir, service->exe, sizeof(service->dir)); + strip_basename(service->dir); + } + + /* Get flags. */ + if (SendMessage(GetDlgItem(tablist[NSSM_TAB_APPLICATION], 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); + /* Get stop method stuff. */ + service->stop_method = ~0; + check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE); + check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW); + check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS); + check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE); + check_method_timeout(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay); + check_method_timeout(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay); + check_method_timeout(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay); + + /* Get exit action stuff. */ + check_method_timeout(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay); + HWND combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); + service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0); + if (service->default_exit_action == CB_ERR) service->default_exit_action = 0; + + /* Get I/O stuff. */ + check_io("stdin", service->stdin_path, sizeof(service->stdin_path), IDC_STDIN); + check_io("stdout", service->stdout_path, sizeof(service->stdout_path), IDC_STDOUT); + check_io("stderr", service->stderr_path, sizeof(service->stderr_path), IDC_STDERR); } /* See if it works. */ @@ -185,12 +231,33 @@ int remove(HWND window) { return 0; } +static char *browse_filter(int message) { + switch (message) { + case NSSM_GUI_BROWSE_FILTER_APPLICATIONS: return "*.exe;*.bat;*.cmd"; + case NSSM_GUI_BROWSE_FILTER_DIRECTORIES: return "."; + case NSSM_GUI_BROWSE_FILTER_ALL_FILES: /* Fall through. */ + default: return "*.*"; + } +} + +UINT_PTR CALLBACK browse_hook(HWND dlg, UINT message, WPARAM w, LPARAM l) { + switch (message) { + case WM_INITDIALOG: + return 1; + } + + return 0; +} + /* Browse for application */ -void browse(HWND window) { +void browse(HWND window, char *current, unsigned long flags, ...) { if (! window) return; + va_list arg; size_t bufsize = 256; size_t len = bufsize; + int i; + OPENFILENAME ofn; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); @@ -198,28 +265,34 @@ void browse(HWND window) { /* XXX: Escaping nulls with FormatMessage is tricky */ if (ofn.lpstrFilter) { ZeroMemory((void *) ofn.lpstrFilter, bufsize); - char *localised = message_string(NSSM_GUI_BROWSE_FILTER_APPLICATIONS); - _snprintf_s((char *) ofn.lpstrFilter, bufsize, _TRUNCATE, localised); + len = 0; /* "Applications" + NULL + "*.exe" + NULL */ - len = strlen(localised) + 1; - LocalFree(localised); - _snprintf_s((char *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, "*.exe"); - /* "All files" + NULL + "*.*" + NULL */ - len += 6; - localised = message_string(NSSM_GUI_BROWSE_FILTER_ALL_FILES); - _snprintf_s((char *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, localised); - len += strlen(localised) + 1; - LocalFree(localised); - _snprintf_s((char *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, "*.*"); + va_start(arg, flags); + while (i = va_arg(arg, int)) { + char *localised = message_string(i); + _snprintf_s((char *) ofn.lpstrFilter + len, bufsize, _TRUNCATE, localised); + len += strlen(localised) + 1; + LocalFree(localised); + char *filter = browse_filter(i); + _snprintf_s((char *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, "%s", filter); + len += strlen(filter) + 1; + } + va_end(arg); /* Remainder of the buffer is already zeroed */ } ofn.lpstrFile = new char[MAX_PATH]; - ofn.lpstrFile[0] = '\0'; + if (flags & OFN_NOVALIDATE) { + /* Directory hack. */ + _snprintf_s(ofn.lpstrFile, MAX_PATH, _TRUNCATE, ":%s:", message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES)); + } + else _snprintf_s(ofn.lpstrFile, MAX_PATH, _TRUNCATE, "%s", current); ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE); ofn.nMaxFile = MAX_PATH; - ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY; + ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags; if (GetOpenFileName(&ofn)) { + /* Directory hack. */ + if (flags & OFN_NOVALIDATE) strip_basename(ofn.lpstrFile); SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile); } if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter); @@ -227,18 +300,170 @@ void browse(HWND window) { delete[] ofn.lpstrFile; } +INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) { + switch (message) { + case WM_INITDIALOG: + return 1; + + /* Button was pressed or control was controlled. */ + case WM_COMMAND: + HWND dlg; + char buffer[MAX_PATH]; + + switch (LOWORD(w)) { + /* Browse for application. */ + case IDC_BROWSE: + dlg = GetDlgItem(tab, IDC_PATH); + GetDlgItemText(tab, IDC_PATH, buffer, sizeof(buffer)); + browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0); + /* Fill in startup directory if it wasn't already specified. */ + GetDlgItemText(tab, IDC_DIR, buffer, sizeof(buffer)); + if (! buffer[0]) { + GetDlgItemText(tab, IDC_PATH, buffer, sizeof(buffer)); + strip_basename(buffer); + SetDlgItemText(tab, IDC_DIR, buffer); + } + break; + + /* Browse for startup directory. */ + case IDC_BROWSE_DIR: + dlg = GetDlgItem(tab, IDC_DIR); + GetDlgItemText(tab, IDC_DIR, buffer, sizeof(buffer)); + browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0); + break; + + /* Browse for stdin. */ + case IDC_BROWSE_STDIN: + dlg = GetDlgItem(tab, IDC_STDIN); + GetDlgItemText(tab, IDC_STDIN, buffer, sizeof(buffer)); + browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0); + break; + + /* Browse for stdout. */ + case IDC_BROWSE_STDOUT: + dlg = GetDlgItem(tab, IDC_STDOUT); + GetDlgItemText(tab, IDC_STDOUT, buffer, sizeof(buffer)); + browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0); + /* Fill in stderr if it wasn't already specified. */ + GetDlgItemText(tab, IDC_STDERR, buffer, sizeof(buffer)); + if (! buffer[0]) { + GetDlgItemText(tab, IDC_STDOUT, buffer, sizeof(buffer)); + SetDlgItemText(tab, IDC_STDERR, buffer); + } + break; + + /* Browse for stderr. */ + case IDC_BROWSE_STDERR: + dlg = GetDlgItem(tab, IDC_STDERR); + GetDlgItemText(tab, IDC_STDERR, buffer, sizeof(buffer)); + browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0); + break; + } + return 1; + } + + return 0; +} + /* Install/remove dialogue callback */ INT_PTR CALLBACK install_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { switch (message) { /* Creating the dialogue */ case WM_INITDIALOG: + HWND tabs; + HWND combo; + tabs = GetDlgItem(window, IDC_TAB1); + if (! tabs) return 0; + + /* Set up tabs. */ + TCITEM tab; + ZeroMemory(&tab, sizeof(tab)); + tab.mask = TCIF_TEXT; + + /* Application tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION); + tab.cchTextMax = (int) strlen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab); + tablist[NSSM_TAB_APPLICATION] = CreateDialog(0, MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW); + + /* Shutdown tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN); + tab.cchTextMax = (int) strlen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab); + tablist[NSSM_TAB_SHUTDOWN] = CreateDialog(0, MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE); + + /* Set defaults. */ + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_CHECKED, 0); + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, NSSM_KILL_CONSOLE_GRACE_PERIOD, 0); + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_CHECKED, 0); + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, NSSM_KILL_WINDOW_GRACE_PERIOD, 0); + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0); + SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0); + SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0); + + /* Restart tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_EXIT); + tab.cchTextMax = (int) strlen(tab.pszText); + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab); + tablist[NSSM_TAB_EXIT] = CreateDialog(0, MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE); + + /* Set defaults. */ + SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, NSSM_RESET_THROTTLE_RESTART, 0); + combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT); + SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_RESTART, (LPARAM) message_string(NSSM_GUI_EXIT_RESTART)); + SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_IGNORE, (LPARAM) message_string(NSSM_GUI_EXIT_IGNORE)); + SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY)); + SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN)); + SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0); + + /* I/O tab. */ + tab.pszText = message_string(NSSM_GUI_TAB_IO); + tab.cchTextMax = (int) strlen(tab.pszText) + 1; + SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab); + tablist[NSSM_TAB_IO] = CreateDialog(0, MAKEINTRESOURCE(IDD_IO), window, tab_dlg); + ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE); + + selected_tab = 0; + return 1; + /* Tab change. */ + case WM_NOTIFY: + NMHDR *notification; + + notification = (NMHDR *) l; + switch (notification->code) { + case TCN_SELCHANGE: + HWND tabs; + int selection; + + tabs = GetDlgItem(window, IDC_TAB1); + if (! tabs) return 0; + + selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0); + if (selection != selected_tab) { + ShowWindow(tablist[selected_tab], SW_HIDE); + /* + XXX: Sets focus to the service name which isn't ideal but is + better than leaving it in another tab. + */ + ShowWindow(tablist[selection], SW_SHOWDEFAULT); + SetFocus(tablist[selection]); + selected_tab = selection; + } + return 1; + } + + return 0; + /* Button was pressed or control was controlled */ case WM_COMMAND: switch (LOWORD(w)) { /* OK button */ - case IDC_OK: + case IDOK: if (! install(window)) PostQuitMessage(0); break; @@ -247,11 +472,6 @@ INT_PTR CALLBACK install_dlg(HWND window, UINT message, WPARAM w, LPARAM l) { DestroyWindow(window); break; - /* Browse button */ - case IDC_BROWSE: - browse(GetDlgItem(window, IDC_PATH)); - break; - /* Remove button */ case IDC_REMOVE: if (! remove(window)) PostQuitMessage(0); diff --git a/messages.mc b/messages.mc index 930e9e5..f88da49 100644 --- a/messages.mc +++ b/messages.mc @@ -382,29 +382,29 @@ automatico, riavviare il computer e tentare di nuovo la rimozione. . MessageId = +1 -SymbolicName = NSSM_GUI_BROWSE_FILTER +SymbolicName = NSSM_GUI_BROWSE_FILTER_APPLICATIONS Severity = Informational Language = English -Applications%sAll files%s%0 +Applications%0 . Language = French -Applications%sTous les fichiers%s%0 +Applications%0 . Language = Italian -Applicazioni%sTutti i files%s%0 +Applicazioni%0 . MessageId = +1 -SymbolicName = NSSM_GUI_BROWSE_FILTER_APPLICATIONS +SymbolicName = NSSM_GUI_BROWSE_FILTER_DIRECTORIES Severity = Informational Language = English -Applications%0 +Directories%0 . Language = French -Applications%0 +Répertoires%0 . Language = Italian -Applicazioni%0 +Cartelle%0 . MessageId = +1 @@ -433,6 +433,110 @@ Language = Italian Ricerca file applicazione . +MessageId = +1 +SymbolicName = NSSM_GUI_TAB_APPLICATION +Severity = Informational +Language = English +Application +. +Language = French +Application +. +Language = Italian +Applicazione +. + +MessageId = +1 +SymbolicName = NSSM_GUI_TAB_SHUTDOWN +Severity = Informational +Language = English +Shutdown +. +Language = French +Shutdown +. +Language = Italian +Shutdown +. + +MessageId = +1 +SymbolicName = NSSM_GUI_TAB_EXIT +Severity = Informational +Language = English +Exit actions +. +Language = French +Exit actions +. +Language = Italian +Exit actions +. + +MessageId = +1 +SymbolicName = NSSM_GUI_TAB_IO +Severity = Informational +Language = English +I/O +. +Language = French +I/O +. +Language = Italian +I/O +. + +MessageId = +1 +SymbolicName = NSSM_GUI_EXIT_RESTART +Severity = Informational +Language = English +Restart application +. +Language = French +Redémarrer l'application +. +Language = Italian +Riavvare l'applicazione +. + +MessageId = +1 +SymbolicName = NSSM_GUI_EXIT_IGNORE +Severity = Informational +Language = English +No action (srvany compatible) +. +Language = French +No action (srvany compatible) +. +Language = Italian +No action (srvany compatible) +. + +MessageId = +1 +SymbolicName = NSSM_GUI_EXIT_REALLY +Severity = Informational +Language = English +Stop service (oneshot mode) +. +Language = French +Stop service (oneshot mode) +. +Language = Italian +Stop service (oneshot mode) +. + +MessageId = +1 +SymbolicName = NSSM_GUI_EXIT_UNCLEAN +Severity = Informational +Language = English +Fake crash (pre-Vista) +. +Language = French +Fake crash (pre-Vista) +. +Language = Italian +Fake crash (pre-Vista) +. + MessageId = 1001 SymbolicName = NSSM_EVENT_DISPATCHER_FAILED Severity = Error @@ -1041,7 +1145,7 @@ Language = French La déclaration de l'environnement %1 pour le service %2 n'est pas du type REG_MULTI_SZ. Cette déclaration sera ignorée. . Language = Italian -Dichiarazione di ambiente %1 per il servizio %2 non è di tipo REG_MULTI_SZ e verrà quindi ingnorata. +Dichiarazione di ambiente %1 per il servizio %2 non è di tipo REG_MULTI_SZ e verrà quindi ignorata. . MessageId = +1 diff --git a/nssm.h b/nssm.h index 6edfedf..566a66f 100644 --- a/nssm.h +++ b/nssm.h @@ -53,6 +53,13 @@ int str_equiv(const char *, const char *); #define NSSM_STOP_METHOD_THREADS (1 << 2) #define NSSM_STOP_METHOD_TERMINATE (1 << 3) +/* Exit actions. */ +#define NSSM_EXIT_RESTART 0 +#define NSSM_EXIT_IGNORE 1 +#define NSSM_EXIT_REALLY 2 +#define NSSM_EXIT_UNCLEAN 3 +#define NSSM_NUM_EXIT_ACTIONS 4 + /* How many milliseconds to wait before updating service status. */ #define NSSM_SERVICE_STATUS_DEADLINE 20000 diff --git a/nssm.rc b/nssm.rc index 8574f6c..f5a5191 100644 --- a/nssm.rc +++ b/nssm.rc @@ -63,22 +63,18 @@ IDI_NSSM ICON "nssm.ico" // Dialog // -IDD_INSTALL DIALOG 0, 0, 220, 90 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "NSSM service installer" LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_INSTALL DIALOG 0, 0, 286, 126 +STYLE DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU +CAPTION "NSSM service installer" FONT 8, "MS Sans Serif" -BEGIN - DEFPUSHBUTTON "Install service",IDC_OK,55,69,50,14 - PUSHBUTTON "Cancel",IDCANCEL,111,69,50,14 - EDITTEXT IDC_PATH,48,7,110,14,ES_AUTOHSCROLL - PUSHBUTTON "Browse",IDC_BROWSE,163,7,50,14 - EDITTEXT IDC_FLAGS,48,28,165,14,ES_AUTOHSCROLL - LTEXT "Options:",IDC_STATIC,7,31,27,8 - LTEXT "Service\nname:",IDC_STATIC,7,49,41,18 - EDITTEXT IDC_NAME,48,49,77,14,ES_AUTOHSCROLL - LTEXT "Application:",IDC_STATIC,7,9,38,8 -END +{ + CONTROL "", IDC_TAB1, WC_TABCONTROL, 0, 7, 7, 269, 93 + LTEXT "Service name:", IDC_STATIC, 7, 106, 52, 8, SS_LEFT + EDITTEXT IDC_NAME, 64, 104, 98, 12, ES_AUTOHSCROLL + DEFPUSHBUTTON "Install service", IDOK, 172, 104, 50, 14 + PUSHBUTTON "Cancel", IDCANCEL, 227, 104, 50, 14 +} IDD_REMOVE DIALOG 0, 0, 223, 28 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU @@ -90,6 +86,76 @@ BEGIN EDITTEXT IDC_NAME,59,7,87,14,ES_AUTOHSCROLL END +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +IDD_APPLICATION DIALOG 9, 20, 261, 73 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "Application", IDC_STATIC, 7, 7, 251, 58 + LTEXT "Path:", IDC_STATIC, 13, 18, 53, 8, SS_LEFT + EDITTEXT IDC_PATH, 70, 16, 167, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE, 239, 15, 15, 14 + LTEXT "Startup directory:", IDC_STATIC, 13, 34, 53, 8, SS_LEFT + EDITTEXT IDC_DIR, 70, 32, 167, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE_DIR, 239, 31, 15, 14 + LTEXT "Options:", IDC_STATIC, 13, 50, 53, 8, SS_LEFT + EDITTEXT IDC_FLAGS, 70, 48, 184, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES +} + + + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +IDD_SHUTDOWN DIALOG 9, 20, 261, 75 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "Shutdown", IDC_STATIC, 7, 7, 251, 68 + AUTOCHECKBOX "Generate Control-C", IDC_METHOD_CONSOLE, 13, 18, 76, 8 + LTEXT "Timeout:", IDC_STATIC, 135, 18, 26, 8, SS_LEFT + EDITTEXT IDC_KILL_CONSOLE, 163, 16, 29, 12, ES_AUTOHSCROLL | ES_NUMBER + LTEXT "ms", IDC_STATIC, 194, 18, 10, 8, SS_LEFT + AUTOCHECKBOX "Send WM_CLOSE to windows", IDC_METHOD_WINDOW, 13, 32, 113, 8 + LTEXT "Timeout:", IDC_STATIC, 135, 32, 26, 8, SS_LEFT + EDITTEXT IDC_KILL_WINDOW, 163, 30, 29, 12, ES_AUTOHSCROLL | ES_NUMBER + LTEXT "ms", IDC_STATIC, 194, 32, 10, 8, SS_LEFT + AUTOCHECKBOX "Post WM_QUIT to threads", IDC_METHOD_THREADS, 13, 46, 100, 8 + EDITTEXT IDC_KILL_THREADS, 163, 44, 29, 12, ES_AUTOHSCROLL | ES_NUMBER + LTEXT "ms", IDC_STATIC, 194, 46, 10, 8, SS_LEFT + LTEXT "Timeout:", IDC_STATIC, 135, 46, 26, 8, SS_LEFT + AUTOCHECKBOX "Terminate process", IDC_METHOD_TERMINATE, 13, 60, 74, 8 +} + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +IDD_APPEXIT DIALOG 9, 20, 261, 73 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "Throttling", IDC_STATIC, 7, 7, 251, 25 + LTEXT "Delay restart if application runs for less than", IDC_STATIC, 13, 18, 137, 8, SS_LEFT + EDITTEXT IDC_THROTTLE, 152, 16, 29, 12, ES_AUTOHSCROLL | ES_NUMBER + LTEXT "ms", IDC_STATIC, 186, 18, 10, 8, SS_LEFT + GROUPBOX "Restart", IDC_STATIC, 7, 33, 251, 35 + LTEXT "Action to take when application exits other\nthan in response to a controlled service\nshutdown:", IDC_STATIC, 14, 42, 134, 24, SS_LEFT + COMBOBOX IDC_APPEXIT, 153, 47, 100, 120, CBS_DROPDOWNLIST | CBS_HASSTRINGS | WS_TABSTOP +} + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL +IDD_IO DIALOG 9, 20, 261, 73 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "I/O redirection", IDC_STATIC, 7, 7, 251, 58 + LTEXT "Input (stdin):", IDC_STATIC, 13, 18, 53, 8, SS_LEFT + EDITTEXT IDC_STDIN, 70, 16, 167, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE_STDIN, 239, 15, 15, 14 + LTEXT "Output (stdout):", IDC_STATIC, 13, 34, 53, 8, SS_LEFT + EDITTEXT IDC_STDOUT, 70, 32, 167, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE_STDOUT, 239, 31, 15, 14 + LTEXT "Error (stderr):", IDC_STATIC, 13, 50, 53, 8, SS_LEFT + EDITTEXT IDC_STDERR, 70, 48, 167, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE_STDERR, 239, 47, 15, 14 +} + ///////////////////////////////////////////////////////////////////////////// // @@ -149,27 +215,32 @@ LANGUAGE LANG_FRENCH, SUBLANG_FRENCH #pragma code_page(1252) #endif //_WIN32 +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_NSSM ICON "nssm.ico" + ///////////////////////////////////////////////////////////////////////////// // // Dialog // -IDD_INSTALL DIALOG 0, 0, 220, 90 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "Installation d'un service NSSM" LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +IDD_INSTALL DIALOG 0, 0, 282, 126 +STYLE DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU +CAPTION "Installation d'un service NSSM" FONT 8, "MS Sans Serif" -BEGIN - DEFPUSHBUTTON "Installer le service",IDC_OK,49,69,75,14 - PUSHBUTTON "Annuler",IDCANCEL,131,69,50,14 - EDITTEXT IDC_PATH,48,7,110,14,ES_AUTOHSCROLL - PUSHBUTTON "Parcourir",IDC_BROWSE,163,7,50,14 - EDITTEXT IDC_FLAGS,48,28,165,14,ES_AUTOHSCROLL - LTEXT "Options:",IDC_STATIC,7,31,27,8 - LTEXT "Nom du\nservice:",IDC_STATIC,7,49,41,18 - EDITTEXT IDC_NAME,48,49,77,14,ES_AUTOHSCROLL - LTEXT "Application:",IDC_STATIC,7,9,38,8 -END +{ + CONTROL "", IDC_TAB1, WC_TABCONTROL, 0, 7, 7, 269, 93 + LTEXT "Nom du service:", IDC_STATIC, 7, 106, 52, 8, SS_LEFT + EDITTEXT IDC_NAME, 64, 104, 98, 12, ES_AUTOHSCROLL + DEFPUSHBUTTON "Installer le service", IDOK, 172, 106, 50, 14 + PUSHBUTTON "Anuller", IDCANCEL, 227, 106, 50, 14 +} IDD_REMOVE DIALOG 0, 0, 223, 28 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU @@ -181,11 +252,54 @@ BEGIN EDITTEXT IDC_NAME,43,7,90,14,ES_AUTOHSCROLL END +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +IDD_APPLICATION DIALOG 9, 20, 261, 73 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "Application", IDC_STATIC, 7, 7, 251, 58 + LTEXT "Chemin:", IDC_STATIC, 13, 18, 53, 8, SS_LEFT + EDITTEXT IDC_PATH, 80, 16, 157, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE, 239, 15, 15, 14 + LTEXT "Rép. de démarrage:", IDC_STATIC, 13, 32, 64, 8, SS_LEFT + EDITTEXT IDC_DIR, 80, 30, 157, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE_DIR, 239, 30, 15, 14 + LTEXT "Options:", IDC_STATIC, 13, 47, 53, 8, SS_LEFT + EDITTEXT IDC_FLAGS, 80, 45, 174, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES +} + + ///////////////////////////////////////////////////////////////////////////// // // DESIGNINFO // +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION NSSM_VERSIONINFO + PRODUCTVERSION NSSM_VERSIONINFO + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + END + BLOCK "VarFileInfo" + BEGIN + END +END + #endif // French resources ///////////////////////////////////////////////////////////////////////////// @@ -240,22 +354,18 @@ IDI_NSSM ICON "nssm.ico" // Dialog // -IDD_INSTALL DIALOG 0, 0, 220, 90 -STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU -CAPTION "NSSM - Installazione Servizio" LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN +IDD_INSTALL DIALOG 0, 0, 282, 126 +STYLE DS_MODALFRAME | DS_SETFONT | WS_CAPTION | WS_POPUP | WS_SYSMENU +CAPTION "NSSM - Installazione Servizio" FONT 8, "MS Sans Serif" -BEGIN - DEFPUSHBUTTON "Installa servizio",IDC_OK,49,69,58,14 - PUSHBUTTON "Annulla",IDCANCEL,111,69,50,14 - EDITTEXT IDC_PATH,48,7,110,14,ES_AUTOHSCROLL - PUSHBUTTON "Sfoglia...",IDC_BROWSE,163,7,50,14 - EDITTEXT IDC_FLAGS,48,28,165,14,ES_AUTOHSCROLL - LTEXT "Opzioni:",IDC_STATIC,7,31,27,8 - LTEXT "Nome\nservizio:",IDC_STATIC,7,49,41,18 - EDITTEXT IDC_NAME,48,49,77,14,ES_AUTOHSCROLL - LTEXT "Applicazione:",IDC_STATIC,2,9,43,8 -END +{ + CONTROL "", IDC_TAB1, WC_TABCONTROL, 0, 7, 7, 269, 93 + LTEXT "Nome servizio:", IDC_STATIC, 7, 106, 52, 8, SS_LEFT + EDITTEXT IDC_NAME, 64, 104, 98, 12, ES_AUTOHSCROLL + DEFPUSHBUTTON "Installa servizio", IDOK, 172, 106, 50, 14 + PUSHBUTTON "Anulla", IDCANCEL, 227, 106, 50, 14 +} IDD_REMOVE DIALOG 0, 0, 223, 28 STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU @@ -267,6 +377,22 @@ BEGIN EDITTEXT IDC_NAME,59,7,87,14,ES_AUTOHSCROLL END +LANGUAGE LANG_ITALIAN, SUBLANG_ITALIAN +IDD_APPLICATION DIALOG 9, 20, 261, 73 +STYLE DS_SHELLFONT | WS_VISIBLE | WS_CHILD | DS_CONTROL +FONT 8, "MS Sans Serif" +{ + GROUPBOX "Applicazione", IDC_STATIC, 7, 7, 251, 58 + LTEXT "Path:", IDC_STATIC, 13, 18, 53, 8, SS_LEFT + EDITTEXT IDC_PATH, 70, 16, 167, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE, 239, 16, 15, 14 + LTEXT "Cartella di avvio:", IDC_STATIC, 13, 32, 53, 8, SS_LEFT + EDITTEXT IDC_DIR, 70, 30, 167, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES + DEFPUSHBUTTON "...", IDC_BROWSE_DIR, 239, 30, 15, 14 + LTEXT "Opzioni:", IDC_STATIC, 13, 47, 53, 8, SS_LEFT + EDITTEXT IDC_FLAGS, 70, 46, 184, 12, ES_AUTOHSCROLL, WS_EX_ACCEPTFILES +} + ///////////////////////////////////////////////////////////////////////////// // @@ -288,7 +414,33 @@ BEGIN END #endif // APSTUDIO_INVOKED -#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION NSSM_VERSIONINFO + PRODUCTVERSION NSSM_VERSIONINFO + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + END + BLOCK "VarFileInfo" + BEGIN + END +END + +#endif // Italian (Italy) resources ///////////////////////////////////////////////////////////////////////////// @@ -303,4 +455,3 @@ END #include "messages.rc" ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED - diff --git a/registry.cpp b/registry.cpp index 59d17d4..169cd08 100644 --- a/registry.cpp +++ b/registry.cpp @@ -1,5 +1,7 @@ #include "nssm.h" +extern const char *exit_action_strings[]; + int create_messages() { HKEY key; @@ -21,7 +23,7 @@ int create_messages() { /* Try to register the module but don't worry so much on failure */ RegSetValueEx(key, "EventMessageFile", 0, REG_SZ, (const unsigned char *) path, (unsigned long) strlen(path) + 1); unsigned long types = EVENTLOG_INFORMATION_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_ERROR_TYPE; - RegSetValueEx(key, "TypesSupported", 0, REG_DWORD, /*XXX*/(PBYTE) &types, sizeof(types)); + RegSetValueEx(key, "TypesSupported", 0, REG_DWORD, (const unsigned char *) &types, sizeof(types)); return 0; } @@ -42,26 +44,35 @@ int create_parameters(nssm_service_t *service) { } /* Try to create the parameters */ - 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); + if (set_expand_string(key, NSSM_REG_EXE, service->exe)) { RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY); RegCloseKey(key); return 3; } - 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); + if (set_expand_string(key, NSSM_REG_FLAGS, service->flags)) { RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY); RegCloseKey(key); return 4; } - 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); + if (set_expand_string(key, NSSM_REG_DIR, service->dir)) { RegDeleteKey(HKEY_LOCAL_MACHINE, NSSM_REGISTRY); RegCloseKey(key); return 5; } - /* Close registry */ + /* Other non-default parameters. May fail. */ + unsigned long stop_method_skip = ~service->stop_method; + if (stop_method_skip) set_number(key, NSSM_REG_STOP_METHOD_SKIP, stop_method_skip); + if (service->default_exit_action < NSSM_NUM_EXIT_ACTIONS) create_exit_action(service->name, exit_action_strings[service->default_exit_action]); + if (service->throttle_delay != NSSM_RESET_THROTTLE_RESTART) set_number(key, NSSM_REG_THROTTLE, service->throttle_delay); + if (service->kill_console_delay != NSSM_KILL_CONSOLE_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, service->kill_console_delay); + if (service->kill_window_delay != NSSM_KILL_WINDOW_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_WINDOW_GRACE_PERIOD, service->kill_window_delay); + if (service->kill_threads_delay != NSSM_KILL_THREADS_GRACE_PERIOD) set_number(key, NSSM_REG_KILL_THREADS_GRACE_PERIOD, service->kill_threads_delay); + if (service->stdin_path[0]) set_expand_string(key, NSSM_REG_STDIN, service->stdin_path); + if (service->stdout_path[0]) set_expand_string(key, NSSM_REG_STDOUT, service->stdout_path); + if (service->stderr_path[0]) set_expand_string(key, NSSM_REG_STDERR, service->stderr_path); + + /* Close registry. */ RegCloseKey(key); return 0; diff --git a/resource.h b/resource.h index 267e7ff..8d9b821 100644 --- a/resource.h +++ b/resource.h @@ -2,24 +2,47 @@ // Microsoft Developer Studio generated include file. // Used by nssm.rc // +#define IDC_STATIC (-1) #define IDI_NSSM 101 #define IDD_INSTALL 102 #define IDD_REMOVE 103 +#define IDD_APPLICATION 104 +#define IDD_IO 105 +#define IDD_APPEXIT 106 +#define IDD_SHUTDOWN 107 #define IDC_PATH 1000 -#define IDC_OK 1001 +#define IDC_TAB1 1001 #define IDC_CANCEL 1002 #define IDC_BROWSE 1003 #define IDC_FLAGS 1004 #define IDC_NAME 1005 #define IDC_REMOVE 1007 +#define IDC_METHOD_CONSOLE 1008 +#define IDC_METHOD_WINDOW 1009 +#define IDC_METHOD_THREADS 1010 +#define IDC_METHOD_TERMINATE 1011 +#define IDC_KILL_CONSOLE 1012 +#define IDC_KILL_WINDOW 1013 +#define IDC_KILL_THREADS 1014 +#define IDC_STDIN 1015 +#define IDC_STDOUT 1016 +#define IDC_STDERR 1017 +#define IDC_BROWSE_STDIN 1018 +#define IDC_BROWSE_STDOUT 1019 +#define IDC_BROWSE_STDERR 1020 +#define IDC_THROTTLE 1021 +#define IDC_APPEXIT 1022 +#define IDC_DIR 1023 +#define IDC_BROWSE_DIR 1024 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_RESOURCE_VALUE 108 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1009 +#define _APS_NEXT_CONTROL_VALUE 1024 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif + diff --git a/service.cpp b/service.cpp index dffd7aa..906ac64 100644 --- a/service.cpp +++ b/service.cpp @@ -5,8 +5,7 @@ bool use_critical_section; extern imports_t imports; -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 }; +const char *exit_action_strings[] = { "Restart", "Ignore", "Exit", "Suicide", 0 }; static inline int throttle_milliseconds(unsigned long throttle) { /* pow() operates on doubles. */ diff --git a/service.h b/service.h index 994a7ce..e9f36ba 100644 --- a/service.h +++ b/service.h @@ -22,6 +22,10 @@ typedef struct { char flags[VALUE_LENGTH]; char dir[MAX_PATH]; char *env; + char stdin_path[MAX_PATH]; + char stdout_path[MAX_PATH]; + char stderr_path[MAX_PATH]; + unsigned long default_exit_action; unsigned long throttle_delay; unsigned long stop_method; unsigned long kill_console_delay; -- 2.20.1