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_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
355 print_message(stderr, NSSM_MESSAGE_CANNOT_RENAME_SERVICE);
359 int native_get_name(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
360 return value_from_string(name, value, service_name);
363 int native_set_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
364 SC_HANDLE service_handle = (SC_HANDLE) param;
365 if (! service_handle) return -1;
368 Logical syntax is: nssm set <service> ObjectName <username> <password>
369 That means the username is actually passed in the additional parameter.
371 bool localsystem = true;
372 TCHAR *username = NSSM_LOCALSYSTEM_ACCOUNT;
375 if (! str_equiv(additional, NSSM_LOCALSYSTEM_ACCOUNT)) {
377 username = (TCHAR *) additional;
378 if (value && value->string) password = value->string;
380 /* We need a password if the account is not LOCALSYSTEM. */
381 print_message(stderr, NSSM_MESSAGE_MISSING_PASSWORD, name);
388 ChangeServiceConfig() will fail to set the username if the service is set
389 to interact with the desktop.
391 unsigned long type = SERVICE_NO_CHANGE;
393 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
395 if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
399 type = qsc->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
400 HeapFree(GetProcessHeap(), 0, qsc);
403 if (grant_logon_as_service(username)) {
404 if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
405 print_message(stderr, NSSM_MESSAGE_GRANT_LOGON_AS_SERVICE_FAILED, username);
409 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, username, password, 0)) {
410 if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
411 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
414 if (password) SecureZeroMemory(password, _tcslen(password) * sizeof(TCHAR));
416 if (localsystem) return 0;
421 int native_get_objectname(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
422 SC_HANDLE service_handle = (SC_HANDLE) param;
423 if (! service_handle) return -1;
425 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
426 if (! qsc) return -1;
428 int ret = value_from_string(name, value, qsc->lpServiceStartName);
429 HeapFree(GetProcessHeap(), 0, qsc);
434 int native_set_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
435 SC_HANDLE service_handle = (SC_HANDLE) param;
436 if (! service_handle) return -1;
438 /* It makes no sense to try to reset the startup type. */
439 if (! value || ! value->string) {
440 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
444 /* Map NSSM_STARTUP_* constant to Windows SERVICE_*_START constant. */
445 int service_startup = -1;
447 for (i = 0; startup_strings[i]; i++) {
448 if (str_equiv(value->string, startup_strings[i])) {
454 if (service_startup < 0) {
455 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_STARTUP, value->string);
456 for (i = 0; startup_strings[i]; i++) _ftprintf(stderr, _T("%s\n"), startup_strings[i]);
460 unsigned long startup;
461 switch (service_startup) {
462 case NSSM_STARTUP_MANUAL: startup = SERVICE_DEMAND_START; break;
463 case NSSM_STARTUP_DISABLED: startup = SERVICE_DISABLED; break;
464 default: startup = SERVICE_AUTO_START;
467 if (! ChangeServiceConfig(service_handle, SERVICE_NO_CHANGE, startup, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
468 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
472 SERVICE_DELAYED_AUTO_START_INFO delayed;
473 ZeroMemory(&delayed, sizeof(delayed));
474 if (service_startup == NSSM_STARTUP_DELAYED) delayed.fDelayedAutostart = 1;
475 else delayed.fDelayedAutostart = 0;
476 if (! ChangeServiceConfig2(service_handle, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &delayed)) {
477 unsigned long error = GetLastError();
478 /* Pre-Vista we expect to fail with ERROR_INVALID_LEVEL */
479 if (error != ERROR_INVALID_LEVEL) {
480 log_event(EVENTLOG_ERROR_TYPE, NSSM_MESSAGE_SERVICE_CONFIG_DELAYED_AUTO_START_INFO_FAILED, service_name, error_string(error), 0);
487 int native_get_startup(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
488 SC_HANDLE service_handle = (SC_HANDLE) param;
489 if (! service_handle) return -1;
491 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
492 if (! qsc) return -1;
494 unsigned long startup;
495 int ret = get_service_startup(service_name, service_handle, qsc, &startup);
496 HeapFree(GetProcessHeap(), 0, qsc);
501 for (i = 0; startup_strings[i]; i++);
502 if (startup >= i) return -1;
504 return value_from_string(name, value, startup_strings[startup]);
507 int native_set_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
508 SC_HANDLE service_handle = (SC_HANDLE) param;
509 if (! service_handle) return -1;
511 /* It makes no sense to try to reset the service type. */
512 if (! value || ! value->string) {
513 print_message(stderr, NSSM_MESSAGE_NO_DEFAULT_VALUE, name);
518 We can only manage services of type SERVICE_WIN32_OWN_PROCESS
519 and SERVICE_INTERACTIVE_PROCESS.
521 unsigned long type = SERVICE_WIN32_OWN_PROCESS;
522 if (str_equiv(value->string, NSSM_INTERACTIVE_PROCESS)) type |= SERVICE_INTERACTIVE_PROCESS;
523 else if (! str_equiv(value->string, NSSM_WIN32_OWN_PROCESS)) {
524 print_message(stderr, NSSM_MESSAGE_INVALID_SERVICE_TYPE, value->string);
525 _ftprintf(stderr, _T("%s\n"), NSSM_WIN32_OWN_PROCESS);
526 _ftprintf(stderr, _T("%s\n"), NSSM_INTERACTIVE_PROCESS);
531 ChangeServiceConfig() will fail if the service runs under an account
532 other than LOCALSYSTEM and we try to make it interactive.
534 if (type & SERVICE_INTERACTIVE_PROCESS) {
535 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
536 if (! qsc) return -1;
538 if (! str_equiv(qsc->lpServiceStartName, NSSM_LOCALSYSTEM_ACCOUNT)) {
539 HeapFree(GetProcessHeap(), 0, qsc);
540 print_message(stderr, NSSM_MESSAGE_INTERACTIVE_NOT_LOCALSYSTEM, value->string, service_name, NSSM_LOCALSYSTEM_ACCOUNT);
544 HeapFree(GetProcessHeap(), 0, qsc);
547 if (! ChangeServiceConfig(service_handle, type, SERVICE_NO_CHANGE, SERVICE_NO_CHANGE, 0, 0, 0, 0, 0, 0, 0)) {
548 print_message(stderr, NSSM_MESSAGE_CHANGESERVICECONFIG_FAILED, error_string(GetLastError()));
555 int native_get_type(const TCHAR *service_name, void *param, const TCHAR *name, void *default_value, value_t *value, const TCHAR *additional) {
556 SC_HANDLE service_handle = (SC_HANDLE) param;
557 if (! service_handle) return -1;
559 QUERY_SERVICE_CONFIG *qsc = query_service_config(service_name, service_handle);
560 if (! qsc) return -1;
562 value->numeric = qsc->dwServiceType;
563 HeapFree(GetProcessHeap(), 0, qsc);
566 switch (value->numeric) {
567 case SERVICE_KERNEL_DRIVER: string = NSSM_KERNEL_DRIVER; break;
568 case SERVICE_FILE_SYSTEM_DRIVER: string = NSSM_FILE_SYSTEM_DRIVER; break;
569 case SERVICE_WIN32_OWN_PROCESS: string = NSSM_WIN32_OWN_PROCESS; break;
570 case SERVICE_WIN32_SHARE_PROCESS: string = NSSM_WIN32_SHARE_PROCESS; break;
571 case SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_INTERACTIVE_PROCESS; break;
572 case SERVICE_WIN32_SHARE_PROCESS|SERVICE_INTERACTIVE_PROCESS: string = NSSM_SHARE_INTERACTIVE_PROCESS; break;
573 default: string = NSSM_UNKNOWN;
576 return value_from_string(name, value, string);
579 int set_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
580 if (! key) return -1;
583 if (setting->set) ret = setting->set(service_name, (void *) key, setting->name, setting->default_value, value, additional);
586 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
587 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
588 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
593 int set_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
594 if (! service_handle) return -1;
597 if (setting->set) ret = setting->set(service_name, service_handle, setting->name, setting->default_value, value, additional);
600 if (! ret) print_message(stdout, NSSM_MESSAGE_RESET_SETTING, setting->name, service_name);
601 else if (ret > 0) print_message(stdout, NSSM_MESSAGE_SET_SETTING, setting->name, service_name);
602 else print_message(stderr, NSSM_MESSAGE_SET_SETTING_FAILED, setting->name, service_name);
608 Returns: 1 if the value was retrieved.
609 0 if the default value was retrieved.
612 int get_setting(const TCHAR *service_name, HKEY key, settings_t *setting, value_t *value, const TCHAR *additional) {
613 if (! key) return -1;
616 switch (setting->type) {
620 value->string = (TCHAR *) setting->default_value;
621 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
626 value->numeric = (unsigned long) setting->default_value;
627 if (setting->get) ret = setting->get(service_name, (void *) key, setting->name, setting->default_value, value, additional);
636 if (ret < 0) print_message(stderr, NSSM_MESSAGE_GET_SETTING_FAILED, setting->name, service_name);
641 int get_setting(const TCHAR *service_name, SC_HANDLE service_handle, settings_t *setting, value_t *value, const TCHAR *additional) {
642 if (! service_handle) return -1;
643 return setting->get(service_name, service_handle, setting->name, 0, value, additional);
646 settings_t settings[] = {
647 { NSSM_REG_EXE, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
648 { NSSM_REG_FLAGS, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
649 { NSSM_REG_DIR, REG_EXPAND_SZ, (void *) _T(""), false, 0, setting_set_string, setting_get_string },
650 { NSSM_REG_EXIT, REG_SZ, (void *) exit_action_strings[NSSM_EXIT_RESTART], false, ADDITIONAL_MANDATORY, setting_set_exit_action, setting_get_exit_action },
651 { NSSM_REG_ENV, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
652 { NSSM_REG_ENV_EXTRA, REG_MULTI_SZ, NULL, false, ADDITIONAL_CRLF, setting_set_environment, setting_get_environment },
653 { NSSM_REG_STDIN, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
654 { NSSM_REG_STDIN NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDIN_SHARING, false, 0, setting_set_number, setting_get_number },
655 { NSSM_REG_STDIN NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDIN_DISPOSITION, false, 0, setting_set_number, setting_get_number },
656 { NSSM_REG_STDIN NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDIN_FLAGS, false, 0, setting_set_number, setting_get_number },
657 { NSSM_REG_STDOUT, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
658 { NSSM_REG_STDOUT NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDOUT_SHARING, false, 0, setting_set_number, setting_get_number },
659 { NSSM_REG_STDOUT NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDOUT_DISPOSITION, false, 0, setting_set_number, setting_get_number },
660 { NSSM_REG_STDOUT NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDOUT_FLAGS, false, 0, setting_set_number, setting_get_number },
661 { NSSM_REG_STDERR, REG_EXPAND_SZ, NULL, false, 0, setting_set_string, setting_get_string },
662 { NSSM_REG_STDERR NSSM_REG_STDIO_SHARING, REG_DWORD, (void *) NSSM_STDERR_SHARING, false, 0, setting_set_number, setting_get_number },
663 { NSSM_REG_STDERR NSSM_REG_STDIO_DISPOSITION, REG_DWORD, (void *) NSSM_STDERR_DISPOSITION, false, 0, setting_set_number, setting_get_number },
664 { NSSM_REG_STDERR NSSM_REG_STDIO_FLAGS, REG_DWORD, (void *) NSSM_STDERR_FLAGS, false, 0, setting_set_number, setting_get_number },
665 { NSSM_REG_STOP_METHOD_SKIP, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
666 { NSSM_REG_KILL_CONSOLE_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_CONSOLE_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
667 { NSSM_REG_KILL_WINDOW_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_WINDOW_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
668 { NSSM_REG_KILL_THREADS_GRACE_PERIOD, REG_DWORD, (void *) NSSM_KILL_THREADS_GRACE_PERIOD, false, 0, setting_set_number, setting_get_number },
669 { NSSM_REG_THROTTLE, REG_DWORD, (void *) NSSM_RESET_THROTTLE_RESTART, false, 0, setting_set_number, setting_get_number },
670 { NSSM_REG_ROTATE, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
671 { NSSM_REG_ROTATE_SECONDS, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
672 { NSSM_REG_ROTATE_BYTES_LOW, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
673 { NSSM_REG_ROTATE_BYTES_HIGH, REG_DWORD, 0, false, 0, setting_set_number, setting_get_number },
674 { NSSM_NATIVE_DESCRIPTION, REG_SZ, _T(""), true, 0, native_set_description, native_get_description },
675 { NSSM_NATIVE_DISPLAYNAME, REG_SZ, NULL, true, 0, native_set_displayname, native_get_displayname },
676 { NSSM_NATIVE_IMAGEPATH, REG_EXPAND_SZ, NULL, true, 0, native_set_imagepath, native_get_imagepath },
677 { NSSM_NATIVE_OBJECTNAME, REG_SZ, NSSM_LOCALSYSTEM_ACCOUNT, true, ADDITIONAL_SETTING, native_set_objectname, native_get_objectname },
678 { NSSM_NATIVE_NAME, REG_SZ, NULL, true, 0, native_set_name, native_get_name },
679 { NSSM_NATIVE_STARTUP, REG_SZ, NULL, true, 0, native_set_startup, native_get_startup },
680 { NSSM_NATIVE_TYPE, REG_SZ, NULL, true, 0, native_set_type, native_get_type },
681 { NULL, NULL, NULL, NULL, NULL }