2 /* XXX: (value && value->string) is probably bogus because value is probably never null */
4 extern const TCHAR *exit_action_strings[];
5 extern const TCHAR *startup_strings[];
7 /* Does the parameter refer to the default value of the AppExit setting? */
8 static inline int is_default_exit_action(const TCHAR *value) {
9 return (str_equiv(value, _T("default")) || str_equiv(value, _T("*")) || ! value[0]);
12 static int value_from_string(const TCHAR *name, value_t *value, const TCHAR *string) {
13 size_t len = _tcslen(string);
19 value->string = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, len * sizeof(TCHAR));
20 if (! value->string) {
21 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
25 if (_sntprintf_s(value->string, len, _TRUNCATE, _T("%s"), string) < 0) {
26 HeapFree(GetProcessHeap(), 0, value->string);
27 print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, name, _T("value_from_string()"));
34 /* Functions to manage NSSM-specific settings in the registry. */
35 static int setting_set_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
36 HKEY key = (HKEY) param;
42 /* Resetting to default? */
43 if (! value || ! value->string) {
44 error = RegDeleteValue(key, name);
45 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
46 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
49 if (str_number(value->string, &number)) return -1;
51 if (default_value && number == (unsigned long) default_value) {
52 error = RegDeleteValue(key, name);
53 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
54 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
58 if (set_number(key, (TCHAR *) name, number)) return -1;
63 static int setting_get_number(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
64 HKEY key = (HKEY) param;
65 return get_number(key, (TCHAR *) name, &value->numeric, false);
68 static int setting_set_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
69 HKEY key = (HKEY) param;
74 /* Resetting to default? */
75 if (! value || ! value->string) {
76 if (default_value) value->string = (TCHAR *) default_value;
78 error = RegDeleteValue(key, name);
79 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
80 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
84 if (default_value && _tcslen((TCHAR *) default_value) && str_equiv(value->string, (TCHAR *) default_value)) {
85 error = RegDeleteValue(key, name);
86 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
87 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
91 if (set_expand_string(key, (TCHAR *) name, value->string)) return -1;
96 static int setting_get_string(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
97 HKEY key = (HKEY) param;
98 TCHAR buffer[VALUE_LENGTH];
100 if (expand_parameter(key, (TCHAR *) name, (TCHAR *) buffer, (unsigned long) sizeof(buffer), false, false)) return -1;
102 return value_from_string(name, value, buffer);
105 static int setting_set_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
106 unsigned long exitcode;
108 TCHAR action_string[ACTION_LEN];
111 /* Default action? */
112 if (is_default_exit_action(additional)) code = 0;
114 if (str_number(additional, &exitcode)) return -1;
115 code = (TCHAR *) additional;
119 HKEY key = open_registry(service_name, name, KEY_WRITE);
120 if (! key) return -1;
125 /* Resetting to default? */
126 if (value && value->string) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), value->string);
129 /* Delete explicit action. */
130 error = RegDeleteValue(key, code);
132 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
133 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(error));
137 /* Explicitly keep the default action. */
138 if (default_value) _sntprintf_s(action_string, _countof(action_string), _TRUNCATE, _T("%s"), (TCHAR *) default_value);
143 /* Validate the string. */
144 for (int i = 0; exit_action_strings[i]; i++) {
145 if (! _tcsnicmp((const TCHAR *) action_string, exit_action_strings[i], ACTION_LEN)) {
146 if (default_value && str_equiv(action_string, (TCHAR *) default_value)) ret = 0;
147 if (RegSetValueEx(key, code, 0, REG_SZ, (const unsigned char *) action_string, (unsigned long) (_tcslen(action_string) + 1) * sizeof(TCHAR)) != ERROR_SUCCESS) {
148 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, code, service_name, error_string(GetLastError()));
158 print_message(stderr, NSSM_MESSAGE_INVALID_EXIT_ACTION, action_string);
159 for (int i = 0; exit_action_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), exit_action_strings[i]);
164 static int setting_get_exit_action(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
165 unsigned long exitcode = 0;
166 unsigned long *code = 0;
169 if (! is_default_exit_action(additional)) {
170 if (str_number(additional, &exitcode)) return -1;
175 TCHAR action_string[ACTION_LEN];
177 if (get_exit_action(service_name, code, action_string, &default_action)) return -1;
179 value_from_string(name, value, action_string);
181 if (default_action && ! _tcsnicmp((const TCHAR *) action_string, (TCHAR *) default_value, ACTION_LEN)) return 0;
185 static int setting_set_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
186 HKEY key = (HKEY) param;
187 if (! param) return -1;
189 if (! value || ! value->string || ! value->string[0]) {
190 long error = RegDeleteValue(key, name);
191 if (error == ERROR_SUCCESS || error == ERROR_FILE_NOT_FOUND) return 0;
192 print_message(stderr, NSSM_MESSAGE_REGDELETEVALUE_FAILED, name, service_name, error_string(error));
196 unsigned long envlen = (unsigned long) _tcslen(value->string) + 1;
197 TCHAR *unformatted = 0;
198 unsigned long newlen;
199 if (unformat_environment(value->string, envlen, &unformatted, &newlen)) return -1;
201 if (test_environment(unformatted)) {
202 HeapFree(GetProcessHeap(), 0, unformatted);
203 print_message(stderr, NSSM_GUI_INVALID_ENVIRONMENT);
207 if (RegSetValueEx(key, name, 0, REG_MULTI_SZ, (const unsigned char *) unformatted, (unsigned long) newlen * sizeof(TCHAR)) != ERROR_SUCCESS) {
208 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
209 log_event(EVENTLOG_ERROR_TYPE, NSSM_EVENT_SETVALUE_FAILED, NSSM_REG_ENV, error_string(GetLastError()), 0);
213 if (newlen) HeapFree(GetProcessHeap(), 0, unformatted);
217 static int setting_get_environment(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
218 HKEY key = (HKEY) param;
219 if (! param) return -1;
222 unsigned long envlen;
223 if (set_environment((TCHAR *) service_name, key, (TCHAR *) name, &env, &envlen)) return -1;
224 if (! envlen) return 0;
227 unsigned long newlen;
228 if (format_environment(env, envlen, &formatted, &newlen)) return -1;
232 /* Find named environment variable. */
234 size_t len = _tcslen(additional);
235 for (s = env; *s; s++) {
236 /* Look for <additional>=<string> NULL NULL */
237 if (! _tcsnicmp(s, additional, len) && s[len] == _T('=')) {
240 ret = value_from_string(name, value, s);
241 HeapFree(GetProcessHeap(), 0, env);
245 /* Skip this string. */
248 HeapFree(GetProcessHeap(), 0, env);
252 HeapFree(GetProcessHeap(), 0, env);
254 ret = value_from_string(name, value, formatted);
255 if (newlen) HeapFree(GetProcessHeap(), 0, formatted);
259 /* Functions to manage native service settings. */
260 int native_set_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
261 SC_HANDLE service_handle = (SC_HANDLE) param;
262 if (! service_handle) return -1;
264 TCHAR *description = 0;
265 if (value) description = value->string;
266 if (set_service_description(service_name, service_handle, description)) return -1;
268 if (description && description[0]) return 1;
273 int native_get_description(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
274 SC_HANDLE service_handle = (SC_HANDLE) param;
275 if (! service_handle) return -1;
277 TCHAR buffer[VALUE_LENGTH];
278 if (get_service_description(service_name, service_handle, _countof(buffer), buffer)) return -1;
280 if (buffer[0]) return value_from_string(name, value, buffer);
286 int native_set_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
287 SC_HANDLE service_handle = (SC_HANDLE) param;
288 if (! service_handle) return -1;
290 TCHAR *displayname = 0;
291 if (value && value->string) displayname = value->string;
292 else displayname = (TCHAR *) service_name;
294 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, displayname)) {
295 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
300 If the display name and service name differ only in case,
301 ChangeServiceConfig() will return success but the display name will be
302 set to the service name, NOT the value passed to the function.
303 This appears to be a quirk of Windows rather than a bug here.
305 if (displayname != service_name && ! str_equiv(displayname, service_name)) return 1;
310 int native_get_displayname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
311 SC_HANDLE service_handle = (SC_HANDLE) param;
312 if (! service_handle) return -1;
314 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
315 if (! qsc) return -1;
317 int ret = value_from_string(name, value, qsc->lpDisplayName);
318 HeapFree(GetProcessHeap(), 0, qsc);
323 int native_set_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
324 SC_HANDLE service_handle = (SC_HANDLE) param;
325 if (! service_handle) return -1;
327 /* It makes no sense to try to reset the image path. */
328 if (! value || ! value->string) {
329 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
333 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, value->string, 0, 0, 0, 0, 0, 0)) {
334 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
341 int native_get_imagepath(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
342 SC_HANDLE service_handle = (SC_HANDLE) param;
343 if (! service_handle) return -1;
345 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
346 if (! qsc) return -1;
348 int ret = value_from_string(name, value, qsc->lpBinaryPathName);
349 HeapFree(GetProcessHeap(), 0, qsc);
354 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
355 SC_HANDLE service_handle = (SC_HANDLE) param;
356 if (! service_handle) return -1;
359 Logical syntax is: nssm set <service> ObjectName <username> <password>
360 That means the username is actually passed in the additional parameter.
362 bool localsystem = true;
363 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
366 if (! str_equiv(additional, NSSM_LOCALSYSTEM_ACCOUNT)) {
368 username = (TCHAR *) additional;
369 if (value && value->string) password = value->string;
371 /* We need a password if the account is not LOCALSYSTEM. */
372 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
379 ChangeServiceConfig() will fail to set the username if the service is set
380 to interact with the desktop.
382 unsigned long type = SERVICE_NO_CHANGE;
384 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
386 if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
390 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
391 HeapFree(GetProcessHeap(), 0, qsc);
394 if (grant_logon_as_service(username)) {
395 if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
396 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
400 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
401 if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
402 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
405 if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
407 if (localsystem) return 0;
412 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
413 SC_HANDLE service_handle = (SC_HANDLE) param;
414 if (! service_handle) return -1;
416 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
417 if (! qsc) return -1;
419 int ret = value_from_string(name, value, qsc->lpServiceStartName);
420 HeapFree(GetProcessHeap(), 0, qsc);
425 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
426 SC_HANDLE service_handle = (SC_HANDLE) param;
427 if (! service_handle) return -1;
429 /* It makes no sense to try to reset the startup type. */
430 if (! value || ! value->string) {
431 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
435 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
436 int service_startup = -1;
438 for (i = 0; startup_strings[i]; i++) {
439 if (str_equiv(value->string, startup_strings[i])) {
445 if (service_startup < 0) {
446 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
447 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
451 unsigned long startup;
452 switch (service_startup) {
453 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
454 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
455 default: startup = SERVICE_AUTO_START;
458 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
459 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
463 SERVICE_DELAYED_AUTO_START_INFO delayed;
464 ZeroMemory(&delayed, sizeof(delayed));
465 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
466 else delayed.fDelayedAutostart = 0;
467 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
468 unsigned long error = GetLastError();
469 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
470 if (error != ERROR_INVALID_LEVEL) {
471 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
478 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
479 SC_HANDLE service_handle = (SC_HANDLE) param;
480 if (! service_handle) return -1;
482 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
483 if (! qsc) return -1;
485 unsigned long startup;
486 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
487 HeapFree(GetProcessHeap(), 0, qsc);
492 for (i = 0; startup_strings[i]; i++);
493 if (startup >= i) return -1;
495 return value_from_string(name, value, startup_strings[startup]);
498 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
499 SC_HANDLE service_handle = (SC_HANDLE) param;
500 if (! service_handle) return -1;
502 /* It makes no sense to try to reset the service type. */
503 if (! value || ! value->string) {
504 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
509 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
510 and SERVICE_INTERACTIVE_PROCESS.
512 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
513 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
514 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
515 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
516 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
517 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
522 ChangeServiceConfig() will fail if the service runs under an account
523 other than LOCALSYSTEM and we try to make it interactive.
525 if (type & SERVICE_INTERACTIVE_PROCESS) {
526 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
527 if (! qsc) return -1;
529 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
530 HeapFree(GetProcessHeap(), 0, qsc);
531 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
535 HeapFree(GetProcessHeap(), 0, qsc);
538 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
539 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
546 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
547 SC_HANDLE service_handle = (SC_HANDLE) param;
548 if (! service_handle) return -1;
550 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
551 if (! qsc) return -1;
553 value->numeric = qsc->dwServiceType;
554 HeapFree(GetProcessHeap(), 0, qsc);
557 switch (value->numeric) {
558 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
559 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
560 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
561 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
562 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
563 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
564 default: string = NSSM_UNKNOWN;
567 return value_from_string(name, value, string);
570 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
571 if (! key) return -1;
574 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
577 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
578 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
579 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
584 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
585 if (! service_handle) return -1;
588 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
591 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
592 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
593 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
599 Returns: 1 if the value was retrieved.
600 0 if the default value was retrieved.
603 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
604 if (! key) return -1;
607 switch (setting->type) {
611 value->string = (TCHAR *) setting->default_value;
612 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
617 value->numeric = (unsigned long) setting->default_value;
618 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
627 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
632 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
633 if (! service_handle) return -1;
634 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
637 settings_t settings[] = {
638 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
639 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
640 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
641 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
642 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
643 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
644 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
645 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
646 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
647 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
648 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
649 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
650 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
651 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
652 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
653 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
654 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
655 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
656 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
657 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
658 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
659 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
660 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
661 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
662 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
663 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
664 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
665 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
666 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
667 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
668 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, ADDITIONAL_SETTING, native_set_objectname, native_get_objectname },
669 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
670 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
671 { NULL, NULL, NULL, NULL, NULL }