Use well-known account aliases.
[nssm.git] / gui.cpp
1 #include "nssm.h"\r
2 \r
3 static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_PROCESS, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_NUM_TABS };\r
4 static HWND tablist[NSSM_NUM_TABS];\r
5 static int selected_tab;\r
6 \r
7 static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function, LPARAM l) {\r
8   /* The caller will deal with GetLastError()... */\r
9   HRSRC resource = FindResourceEx(0, RT_DIALOG, templ, GetUserDefaultLangID());\r
10   if (! resource) {\r
11     if (GetLastError() != ERROR_RESOURCE_LANG_NOT_FOUND) return 0;\r
12     resource = FindResourceEx(0, RT_DIALOG, templ, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));\r
13     if (! resource) return 0;\r
14   }\r
15 \r
16   HGLOBAL ret = LoadResource(0, resource);\r
17   if (! ret) return 0;\r
18 \r
19   return CreateDialogIndirectParam(0, (DLGTEMPLATE *) ret, parent, function, l);\r
20 }\r
21 \r
22 static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function) {\r
23   return dialog(templ, parent, function, 0);\r
24 }\r
25 \r
26 int nssm_gui(int resource, nssm_service_t *service) {\r
27   /* Create window */\r
28   HWND dlg = dialog(MAKEINTRESOURCE(resource), 0, nssm_dlg, (LPARAM) service);\r
29   if (! dlg) {\r
30     popup_message(0, MB_OK, NSSM_GUI_CREATEDIALOG_FAILED, error_string(GetLastError()));\r
31     return 1;\r
32   }\r
33 \r
34   /* Load the icon. */\r
35   HANDLE icon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_NSSM), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);\r
36   if (icon) SendMessage(dlg, WM_SETICON, ICON_SMALL, (LPARAM) icon);\r
37   icon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_NSSM), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0);\r
38   if (icon) SendMessage(dlg, WM_SETICON, ICON_BIG, (LPARAM) icon);\r
39 \r
40   /* Remember what the window is for. */\r
41   SetWindowLongPtr(dlg, GWLP_USERDATA, (LONG_PTR) resource);\r
42 \r
43   /* Display the window */\r
44   centre_window(dlg);\r
45   ShowWindow(dlg, SW_SHOW);\r
46 \r
47   /* Set service name if given */\r
48   if (service->name[0]) {\r
49     SetDlgItemText(dlg, IDC_NAME, service->name);\r
50     /* No point making user click remove if the name is already entered */\r
51     if (resource == IDD_REMOVE) {\r
52       HWND button = GetDlgItem(dlg, IDC_REMOVE);\r
53       if (button) {\r
54         SendMessage(button, WM_LBUTTONDOWN, 0, 0);\r
55         SendMessage(button, WM_LBUTTONUP, 0, 0);\r
56       }\r
57     }\r
58   }\r
59 \r
60   if (resource == IDD_EDIT) {\r
61     /* We'll need the service handle later. */\r
62     SetWindowLongPtr(dlg, DWLP_USER, (LONG_PTR) service);\r
63 \r
64     /* Service name can't be edited. */\r
65     EnableWindow(GetDlgItem(dlg, IDC_NAME), 0);\r
66     SetFocus(GetDlgItem(dlg, IDOK));\r
67 \r
68     /* Set existing details. */\r
69     HWND combo;\r
70     HWND list;\r
71 \r
72     /* Application tab. */\r
73     if (service->native) SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->image);\r
74     else SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe);\r
75     SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir);\r
76     SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags);\r
77 \r
78     /* Details tab. */\r
79     SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname);\r
80     SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description);\r
81     combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
82     SendMessage(combo, CB_SETCURSEL, service->startup, 0);\r
83 \r
84     /* Log on tab. */\r
85     if (service->username) {\r
86       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_ACCOUNT);\r
87       SetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username);\r
88       EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), 0);\r
89       EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), 1);\r
90       EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), 1);\r
91       EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), 1);\r
92     }\r
93     else {\r
94       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
95       if (service->type & SERVICE_INTERACTIVE_PROCESS) SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_SETCHECK, BST_CHECKED, 0);\r
96     }\r
97 \r
98     /* Process tab. */\r
99     if (service->priority) {\r
100       int priority = priority_constant_to_index(service->priority);\r
101       combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
102       SendMessage(combo, CB_SETCURSEL, priority, 0);\r
103     }\r
104 \r
105     if (service->affinity) {\r
106       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
107       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_UNCHECKED, 0);\r
108       EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), 1);\r
109 \r
110       DWORD_PTR affinity, system_affinity;\r
111       if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {\r
112         if ((service->affinity & (__int64) system_affinity) != service->affinity) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_AFFINITY);\r
113       }\r
114 \r
115       for (int i = 0; i < num_cpus(); i++) {\r
116         if (! (service->affinity & (1LL << (__int64) i))) SendMessage(list, LB_SETSEL, 0, i);\r
117       }\r
118     }\r
119 \r
120     if (service->no_console) {
121       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0);\r
122     }
123
124     /* Shutdown tab. */\r
125     if (! (service->stop_method & NSSM_STOP_METHOD_CONSOLE)) {\r
126       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0);\r
127       EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE), 0);\r
128     }\r
129     SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, service->kill_console_delay, 0);\r
130     if (! (service->stop_method & NSSM_STOP_METHOD_WINDOW)) {\r
131       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_UNCHECKED, 0);\r
132       EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW), 0);\r
133     }\r
134     SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, service->kill_window_delay, 0);\r
135     if (! (service->stop_method & NSSM_STOP_METHOD_THREADS)) {\r
136       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_UNCHECKED, 0);\r
137       EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS), 0);\r
138     }\r
139     SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, service->kill_threads_delay, 0);\r
140     if (! (service->stop_method & NSSM_STOP_METHOD_TERMINATE)) {\r
141       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_UNCHECKED, 0);\r
142     }\r
143 \r
144     /* Restart tab. */\r
145     SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, service->throttle_delay, 0);\r
146     combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
147     SendMessage(combo, CB_SETCURSEL, service->default_exit_action, 0);\r
148     SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, service->restart_delay, 0);\r
149 \r
150     /* I/O tab. */\r
151     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDIN, service->stdin_path);\r
152     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDOUT, service->stdout_path);\r
153     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDERR, service->stderr_path);\r
154 \r
155     /* Rotation tab. */\r
156     if (service->stdout_disposition == CREATE_ALWAYS) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_SETCHECK, BST_CHECKED, 0);\r
157     if (service->rotate_files) {\r
158       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_SETCHECK, BST_CHECKED, 0);\r
159       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), 1);\r
160       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), 1);\r
161       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), 1);\r
162     }\r
163     if (service->rotate_stdout_online || service->rotate_stderr_online) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_CHECKED, 0);\r
164     SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, service->rotate_seconds, 0);\r
165     if (! service->rotate_bytes_high) SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, service->rotate_bytes_low, 0);\r
166 \r
167     /* Check if advanced settings are in use. */\r
168     if (service->stdout_disposition != service->stderr_disposition || (service->stdout_disposition && service->stdout_disposition != NSSM_STDOUT_DISPOSITION && service->stdout_disposition != CREATE_ALWAYS) || (service->stderr_disposition && service->stderr_disposition != NSSM_STDERR_DISPOSITION && service->stderr_disposition != CREATE_ALWAYS)) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_STDIO);\r
169     if (service->rotate_bytes_high) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ROTATE_BYTES);\r
170 \r
171     /* Environment tab. */\r
172     TCHAR *env;\r
173     unsigned long envlen;\r
174     if (service->env_extralen) {\r
175       env = service->env_extra;\r
176       envlen = service->env_extralen;\r
177     }\r
178     else {\r
179       env = service->env;\r
180       envlen = service->envlen;\r
181       if (envlen) SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_SETCHECK, BST_CHECKED, 0);\r
182     }\r
183 \r
184     if (envlen) {\r
185       TCHAR *formatted;\r
186       unsigned long newlen;\r
187       if (format_environment(env, envlen, &formatted, &newlen)) {\r
188         popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("nssm_dlg()"));\r
189       }\r
190       else {\r
191         SetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, formatted);\r
192         HeapFree(GetProcessHeap(), 0, formatted);\r
193       }\r
194     }\r
195     if (service->envlen && service->env_extralen) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ENVIRONMENT);\r
196   }\r
197 \r
198   /* Go! */\r
199   MSG message;\r
200   while (GetMessage(&message, 0, 0, 0)) {\r
201     if (IsDialogMessage(dlg, &message)) continue;\r
202     TranslateMessage(&message);\r
203     DispatchMessage(&message);\r
204   }\r
205 \r
206   return (int) message.wParam;\r
207 }\r
208 \r
209 void centre_window(HWND window) {\r
210   HWND desktop;\r
211   RECT size, desktop_size;\r
212   unsigned long x, y;\r
213 \r
214   if (! window) return;\r
215 \r
216   /* Find window size */\r
217   if (! GetWindowRect(window, &size)) return;\r
218 \r
219   /* Find desktop window */\r
220   desktop = GetDesktopWindow();\r
221   if (! desktop) return;\r
222 \r
223   /* Find desktop window size */\r
224   if (! GetWindowRect(desktop, &desktop_size)) return;\r
225 \r
226   /* Centre window */\r
227   x = (desktop_size.right - size.right) / 2;\r
228   y = (desktop_size.bottom - size.bottom) / 2;\r
229   MoveWindow(window, x, y, size.right - size.left, size.bottom - size.top, 0);\r
230 }\r
231 \r
232 static inline void check_stop_method(nssm_service_t *service, unsigned long method, unsigned long control) {\r
233   if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) return;\r
234   service->stop_method &= ~method;\r
235 }\r
236 \r
237 static inline void check_number(HWND tab, unsigned long control, unsigned long *timeout) {\r
238   BOOL translated;\r
239   unsigned long configured = GetDlgItemInt(tab, control, &translated, 0);\r
240   if (translated) *timeout = configured;\r
241 }\r
242 \r
243 static inline void set_timeout_enabled(unsigned long control, unsigned long dependent) {\r
244   unsigned char enabled = 0;\r
245   if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
246   EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], dependent), enabled);\r
247 }\r
248 \r
249 static inline void set_logon_enabled(unsigned char enabled) {\r
250   EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), ! enabled);\r
251   EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), enabled);\r
252   EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), enabled);\r
253   EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), enabled);\r
254 }\r
255 \r
256 static inline void set_affinity_enabled(unsigned char enabled) {\r
257   EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), enabled);\r
258 }\r
259 \r
260 static inline void set_rotation_enabled(unsigned char enabled) {\r
261   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), enabled);\r
262   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), enabled);\r
263   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), enabled);\r
264 }\r
265 \r
266 static inline void check_io(HWND owner, TCHAR *name, TCHAR *buffer, unsigned long len, unsigned long control) {\r
267   if (! SendMessage(GetDlgItem(tablist[NSSM_TAB_IO], control), WM_GETTEXTLENGTH, 0, 0)) return;\r
268   if (GetDlgItemText(tablist[NSSM_TAB_IO], control, buffer, (int) len)) return;\r
269   popup_message(owner, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name);\r
270   ZeroMemory(buffer, len * sizeof(TCHAR));\r
271 }\r
272 \r
273 /* Set service parameters. */\r
274 int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service) {\r
275   if (! service) return 1;\r
276 \r
277   set_nssm_service_defaults(service);\r
278 \r
279   if (orig_service) {\r
280     service->native = orig_service->native;\r
281     service->handle = orig_service->handle;\r
282   }\r
283 \r
284   /* Get service name. */\r
285   if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
286     popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
287     cleanup_nssm_service(service);\r
288     return 2;\r
289   }\r
290 \r
291   /* Get executable name */\r
292   if (! service->native) {\r
293     if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe, _countof(service->exe))) {\r
294       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
295       return 3;\r
296     }\r
297 \r
298     /* Get startup directory. */\r
299     if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir, _countof(service->dir))) {\r
300       _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
301       strip_basename(service->dir);\r
302     }\r
303 \r
304     /* Get flags. */\r
305     if (SendMessage(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) {\r
306       if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags, _countof(service->flags))) {\r
307         popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
308         return 4;\r
309       }\r
310     }\r
311   }\r
312 \r
313   /* Get details. */\r
314   if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME), WM_GETTEXTLENGTH, 0, 0)) {\r
315     if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname, _countof(service->displayname))) {\r
316       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME);\r
317       return 5;\r
318     }\r
319   }\r
320 \r
321   if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION), WM_GETTEXTLENGTH, 0, 0)) {\r
322     if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description, _countof(service->description))) {\r
323       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION);\r
324       return 5;\r
325     }\r
326   }\r
327 \r
328   HWND combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
329   service->startup = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
330   if (service->startup == CB_ERR) service->startup = 0;\r
331 \r
332   /* Get logon stuff. */\r
333   if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
334     if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
335       service->type |= SERVICE_INTERACTIVE_PROCESS;\r
336     }\r
337     if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
338     service->username = 0;\r
339     service->usernamelen = 0;\r
340     if (service->password) {\r
341       SecureZeroMemory(service->password, service->passwordlen);\r
342       HeapFree(GetProcessHeap(), 0, service->password);\r
343     }\r
344     service->password = 0;\r
345     service->passwordlen = 0;\r
346   }\r
347   else {\r
348     /* Username. */\r
349     service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0);\r
350     if (! service->usernamelen) {\r
351       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_USERNAME);\r
352       return 6;\r
353     }\r
354     service->usernamelen++;\r
355 \r
356     service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));\r
357     if (! service->username) {\r
358       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
359       return 6;\r
360     }\r
361     if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username, (int) service->usernamelen)) {\r
362       HeapFree(GetProcessHeap(), 0, service->username);\r
363       service->username = 0;\r
364       service->usernamelen = 0;\r
365       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME);\r
366       return 6;\r
367     }\r
368 \r
369     /*\r
370       Special case for well-known accounts.\r
371       Ignore the password if we're editing and the username hasn't changed.\r
372     */\r
373     const TCHAR *well_known = well_known_username(service->username);
374     if (well_known) {\r
375       if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) {
376         HeapFree(GetProcessHeap(), 0, service->username);\r
377         service->username = 0;\r
378         service->usernamelen = 0;\r
379       }
380       else {
381         service->usernamelen = _tcslen(well_known) + 1;
382         service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));
383         if (! service->username) {
384           print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("install()"));
385           return 6;
386         }
387         memmove(service->username, well_known, service->usernamelen * sizeof(TCHAR));
388       }
389     }\r
390     else {\r
391       /* Password. */\r
392       service->passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), WM_GETTEXTLENGTH, 0, 0);\r
393       size_t passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), WM_GETTEXTLENGTH, 0, 0);\r
394 \r
395       if (! orig_service || ! orig_service->username || ! str_equiv(service->username, orig_service->username) || service->passwordlen || passwordlen) {\r
396         if (! service->passwordlen) {\r
397           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
398           return 6;\r
399         }\r
400         if (passwordlen != service->passwordlen) {\r
401           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
402           return 6;\r
403         }\r
404         service->passwordlen++;\r
405 \r
406         /* Temporary buffer for password validation. */\r
407         TCHAR *password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
408         if (! password) {\r
409           HeapFree(GetProcessHeap(), 0, service->username);\r
410           service->username = 0;\r
411           service->usernamelen = 0;\r
412           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password confirmation"), _T("install()"));\r
413           return 6;\r
414         }\r
415 \r
416         /* Actual password buffer. */\r
417         service->password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
418         if (! service->password) {\r
419           HeapFree(GetProcessHeap(), 0, password);\r
420           HeapFree(GetProcessHeap(), 0, service->username);\r
421           service->username = 0;\r
422           service->usernamelen = 0;\r
423           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password"), _T("install()"));\r
424           return 6;\r
425         }\r
426 \r
427         /* Get first password. */\r
428         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1, service->password, (int) service->passwordlen)) {\r
429           HeapFree(GetProcessHeap(), 0, password);\r
430           SecureZeroMemory(service->password, service->passwordlen);\r
431           HeapFree(GetProcessHeap(), 0, service->password);\r
432           service->password = 0;\r
433           service->passwordlen = 0;\r
434           HeapFree(GetProcessHeap(), 0, service->username);\r
435           service->username = 0;\r
436           service->usernamelen = 0;\r
437           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
438           return 6;\r
439         }\r
440 \r
441         /* Get confirmation. */\r
442         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2, password, (int) service->passwordlen)) {\r
443           SecureZeroMemory(password, service->passwordlen);\r
444           HeapFree(GetProcessHeap(), 0, password);\r
445           SecureZeroMemory(service->password, service->passwordlen);\r
446           HeapFree(GetProcessHeap(), 0, service->password);\r
447           service->password = 0;\r
448           service->passwordlen = 0;\r
449           HeapFree(GetProcessHeap(), 0, service->username);\r
450           service->username = 0;\r
451           service->usernamelen = 0;\r
452           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
453           return 6;\r
454         }\r
455 \r
456         /* Compare. */\r
457         if (_tcsncmp(password, service->password, service->passwordlen)) {\r
458           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
459           SecureZeroMemory(password, service->passwordlen);\r
460           HeapFree(GetProcessHeap(), 0, password);\r
461           SecureZeroMemory(service->password, service->passwordlen);\r
462           HeapFree(GetProcessHeap(), 0, service->password);\r
463           service->password = 0;\r
464           service->passwordlen = 0;\r
465           HeapFree(GetProcessHeap(), 0, service->username);\r
466           service->username = 0;\r
467           service->usernamelen = 0;\r
468           return 6;\r
469         }\r
470       }\r
471     }\r
472   }\r
473 \r
474   /* Remaining tabs are only for services we manage. */\r
475   if (service->native) return 0;\r
476 \r
477   /* Get process stuff. */\r
478   combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
479   service->priority = priority_index_to_constant((unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0));\r
480 \r
481   service->affinity = 0LL;\r
482   if (! (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_GETCHECK, 0, 0) & BST_CHECKED)) {\r
483     HWND list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
484     int selected = (int) SendMessage(list, LB_GETSELCOUNT, 0, 0);\r
485     int count = (int) SendMessage(list, LB_GETCOUNT, 0, 0);\r
486     if (! selected) {\r
487       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_WARN_AFFINITY_NONE);\r
488       return 5;\r
489     }\r
490     else if (selected < count) {\r
491       for (int i = 0; i < count; i++) {\r
492         if (SendMessage(list, LB_GETSEL, i, 0)) service->affinity |= (1LL << (__int64) i);\r
493       }\r
494     }\r
495   }\r
496 \r
497   if (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->no_console = 0;
498   else service->no_console = 1;
499
500   /* Get stop method stuff. */\r
501   check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE);\r
502   check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW);\r
503   check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS);\r
504   check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE);\r
505   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay);\r
506   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay);\r
507   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay);\r
508 \r
509   /* Get exit action stuff. */\r
510   check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay);\r
511   combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
512   service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
513   if (service->default_exit_action == CB_ERR) service->default_exit_action = 0;\r
514   check_number(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, &service->restart_delay);\r
515 \r
516   /* Get I/O stuff. */\r
517   check_io(window, _T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN);\r
518   check_io(window, _T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT);\r
519   check_io(window, _T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR);\r
520 \r
521   /* Override stdout and/or stderr. */\r
522   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
523     if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS;\r
524     if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS;\r
525   }\r
526 \r
527   /* Get rotation stuff. */\r
528   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
529     service->rotate_files = true;\r
530     if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->rotate_stdout_online = service->rotate_stderr_online = NSSM_ROTATE_ONLINE;
531     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds);\r
532     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low);\r
533   }\r
534 \r
535   /* Get environment. */\r
536   unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);\r
537   if (envlen) {\r
538     TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR));\r
539     if (! env) {\r
540       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
541       cleanup_nssm_service(service);\r
542       return 5;\r
543     }\r
544 \r
545     if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {\r
546       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
547       HeapFree(GetProcessHeap(), 0, env);\r
548       cleanup_nssm_service(service);\r
549       return 5;\r
550     }\r
551 \r
552     TCHAR *newenv;\r
553     unsigned long newlen;\r
554     if (unformat_environment(env, envlen, &newenv, &newlen)) {\r
555       HeapFree(GetProcessHeap(), 0, env);\r
556       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
557       cleanup_nssm_service(service);\r
558       return 5;\r
559     }\r
560 \r
561     HeapFree(GetProcessHeap(), 0, env);\r
562     env = newenv;\r
563     envlen = newlen;\r
564 \r
565     /* Test the environment is valid. */\r
566     if (test_environment(env)) {\r
567       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
568       HeapFree(GetProcessHeap(), 0, env);\r
569       cleanup_nssm_service(service);\r
570       return 5;\r
571     }\r
572 \r
573     if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
574       service->env = env;\r
575       service->envlen = envlen;\r
576     }\r
577     else {\r
578       service->env_extra = env;\r
579       service->env_extralen = envlen;\r
580     }\r
581   }\r
582 \r
583   return 0;\r
584 }\r
585 \r
586 /* Install the service. */\r
587 int install(HWND window) {\r
588   if (! window) return 1;\r
589 \r
590   nssm_service_t *service = alloc_nssm_service();\r
591   if (service) {\r
592     int ret = configure(window, service, 0);\r
593     if (ret) return ret;\r
594   }\r
595 \r
596   /* See if it works. */\r
597   switch (install_service(service)) {\r
598     case 1:\r
599       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("install()"));\r
600       cleanup_nssm_service(service);\r
601       return 1;\r
602 \r
603     case 2:\r
604       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
605       cleanup_nssm_service(service);\r
606       return 2;\r
607 \r
608     case 3:\r
609       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
610       cleanup_nssm_service(service);\r
611       return 3;\r
612 \r
613     case 4:\r
614       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
615       cleanup_nssm_service(service);\r
616       return 4;\r
617 \r
618     case 5:\r
619       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);\r
620       cleanup_nssm_service(service);\r
621       return 5;\r
622 \r
623     case 6:\r
624       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);\r
625       cleanup_nssm_service(service);\r
626       return 6;\r
627   }\r
628 \r
629   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
630   cleanup_nssm_service(service);\r
631   return 0;\r
632 }\r
633 \r
634 /* Remove the service */\r
635 int remove(HWND window) {\r
636   if (! window) return 1;\r
637 \r
638   /* See if it works */\r
639   nssm_service_t *service = alloc_nssm_service();\r
640   if (service) {\r
641     /* Get service name */\r
642     if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
643       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
644       cleanup_nssm_service(service);\r
645       return 2;\r
646     }\r
647 \r
648     /* Confirm */\r
649     if (popup_message(window, MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {\r
650       cleanup_nssm_service(service);\r
651       return 0;\r
652     }\r
653   }\r
654 \r
655   switch (remove_service(service)) {\r
656     case 1:\r
657       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("remove()"));\r
658       cleanup_nssm_service(service);\r
659       return 1;\r
660 \r
661     case 2:\r
662       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
663       cleanup_nssm_service(service);\r
664       return 2;\r
665 \r
666     case 3:\r
667       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
668       return 3;\r
669       cleanup_nssm_service(service);\r
670 \r
671     case 4:\r
672       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
673       cleanup_nssm_service(service);\r
674       return 4;\r
675   }\r
676 \r
677   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
678   cleanup_nssm_service(service);\r
679   return 0;\r
680 }\r
681 \r
682 int edit(HWND window, nssm_service_t *orig_service) {\r
683   if (! window) return 1;\r
684 \r
685   nssm_service_t *service = alloc_nssm_service();\r
686   if (service) {\r
687     int ret = configure(window, service, orig_service);\r
688     if (ret) return ret;\r
689   }\r
690 \r
691   switch (edit_service(service, true)) {\r
692     case 1:\r
693       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()"));\r
694       cleanup_nssm_service(service);\r
695       return 1;\r
696 \r
697     case 3:\r
698       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
699       cleanup_nssm_service(service);\r
700       return 3;\r
701 \r
702     case 4:\r
703       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
704       cleanup_nssm_service(service);\r
705       return 4;\r
706 \r
707     case 5:\r
708     case 6:\r
709       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED);\r
710       cleanup_nssm_service(service);\r
711       return 6;\r
712   }\r
713 \r
714   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name);\r
715   cleanup_nssm_service(service);\r
716   return 0;\r
717 }\r
718 \r
719 static TCHAR *browse_filter(int message) {\r
720   switch (message) {\r
721     case NSSM_GUI_BROWSE_FILTER_APPLICATIONS: return _T("*.exe;*.bat;*.cmd");\r
722     case NSSM_GUI_BROWSE_FILTER_DIRECTORIES: return _T(".");\r
723     case NSSM_GUI_BROWSE_FILTER_ALL_FILES: /* Fall through. */\r
724     default: return _T("*.*");\r
725   }\r
726 }\r
727 \r
728 UINT_PTR CALLBACK browse_hook(HWND dlg, UINT message, WPARAM w, LPARAM l) {\r
729   switch (message) {\r
730     case WM_INITDIALOG:\r
731       return 1;\r
732   }\r
733 \r
734   return 0;\r
735 }\r
736 \r
737 /* Browse for application */\r
738 void browse(HWND window, TCHAR *current, unsigned long flags, ...) {\r
739   if (! window) return;\r
740 \r
741   va_list arg;\r
742   size_t bufsize = 256;\r
743   size_t len = bufsize;\r
744   int i;\r
745 \r
746   OPENFILENAME ofn;\r
747   ZeroMemory(&ofn, sizeof(ofn));\r
748   ofn.lStructSize = sizeof(ofn);\r
749   ofn.lpstrFilter = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, bufsize * sizeof(TCHAR));\r
750   /* XXX: Escaping nulls with FormatMessage is tricky */\r
751   if (ofn.lpstrFilter) {\r
752     ZeroMemory((void *) ofn.lpstrFilter, bufsize);\r
753     len = 0;\r
754     /* "Applications" + NULL + "*.exe" + NULL */\r
755     va_start(arg, flags);\r
756     while (i = va_arg(arg, int)) {\r
757       TCHAR *localised = message_string(i);\r
758       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize, _TRUNCATE, localised);\r
759       len += _tcslen(localised) + 1;\r
760       LocalFree(localised);\r
761       TCHAR *filter = browse_filter(i);\r
762       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, _T("%s"), filter);\r
763       len += _tcslen(filter) + 1;\r
764     }\r
765     va_end(arg);\r
766     /* Remainder of the buffer is already zeroed */\r
767   }\r
768   ofn.lpstrFile = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH * sizeof(TCHAR));\r
769   if (ofn.lpstrFile) {
770     if (flags & OFN_NOVALIDATE) {\r
771       /* Directory hack. */\r
772       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
773       ofn.nMaxFile = DIR_LENGTH;\r
774     }\r
775     else {
776       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T("%s"), current);\r
777       ofn.nMaxFile = PATH_LENGTH;\r
778     }
779   }
780   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
781   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
782 \r
783   if (GetOpenFileName(&ofn)) {\r
784     /* Directory hack. */\r
785     if (flags & OFN_NOVALIDATE) strip_basename(ofn.lpstrFile);\r
786     SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile);\r
787   }\r
788   if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter);\r
789   if (ofn.lpstrFile) HeapFree(GetProcessHeap(), 0, ofn.lpstrFile);
790 }\r
791 \r
792 INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {\r
793   switch (message) {\r
794     case WM_INITDIALOG:\r
795       return 1;\r
796 \r
797     /* Button was pressed or control was controlled. */\r
798     case WM_COMMAND:\r
799       HWND dlg;\r
800       TCHAR buffer[PATH_LENGTH];\r
801       unsigned char enabled;\r
802 \r
803       switch (LOWORD(w)) {\r
804         /* Browse for application. */\r
805         case IDC_BROWSE:\r
806           dlg = GetDlgItem(tab, IDC_PATH);\r
807           GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
808           browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
809           /* Fill in startup directory if it wasn't already specified. */\r
810           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
811           if (! buffer[0]) {\r
812             GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
813             strip_basename(buffer);\r
814             SetDlgItemText(tab, IDC_DIR, buffer);\r
815           }\r
816           break;\r
817 \r
818         /* Browse for startup directory. */\r
819         case IDC_BROWSE_DIR:\r
820           dlg = GetDlgItem(tab, IDC_DIR);\r
821           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
822           browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0);\r
823           break;\r
824 \r
825         /* Log on. */\r
826         case IDC_LOCALSYSTEM:\r
827           set_logon_enabled(0);\r
828           break;\r
829 \r
830         case IDC_ACCOUNT:\r
831           set_logon_enabled(1);\r
832           break;\r
833 \r
834         /* Affinity. */\r
835         case IDC_AFFINITY_ALL:\r
836           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 0;\r
837           else enabled = 1;\r
838           set_affinity_enabled(enabled);\r
839           break;\r
840 \r
841         /* Shutdown methods. */\r
842         case IDC_METHOD_CONSOLE:\r
843           set_timeout_enabled(LOWORD(w), IDC_KILL_CONSOLE);\r
844           break;\r
845 \r
846         case IDC_METHOD_WINDOW:\r
847           set_timeout_enabled(LOWORD(w), IDC_KILL_WINDOW);\r
848           break;\r
849 \r
850         case IDC_METHOD_THREADS:\r
851           set_timeout_enabled(LOWORD(w), IDC_KILL_THREADS);\r
852           break;\r
853 \r
854         /* Browse for stdin. */\r
855         case IDC_BROWSE_STDIN:\r
856           dlg = GetDlgItem(tab, IDC_STDIN);\r
857           GetDlgItemText(tab, IDC_STDIN, buffer, _countof(buffer));\r
858           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
859           break;\r
860 \r
861         /* Browse for stdout. */\r
862         case IDC_BROWSE_STDOUT:\r
863           dlg = GetDlgItem(tab, IDC_STDOUT);\r
864           GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
865           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
866           /* Fill in stderr if it wasn't already specified. */\r
867           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
868           if (! buffer[0]) {\r
869             GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
870             SetDlgItemText(tab, IDC_STDERR, buffer);\r
871           }\r
872           break;\r
873 \r
874         /* Browse for stderr. */\r
875         case IDC_BROWSE_STDERR:\r
876           dlg = GetDlgItem(tab, IDC_STDERR);\r
877           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
878           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
879           break;\r
880 \r
881         /* Rotation. */\r
882         case IDC_ROTATE:\r
883           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
884           else enabled = 0;\r
885           set_rotation_enabled(enabled);\r
886           break;\r
887       }\r
888       return 1;\r
889   }\r
890 \r
891   return 0;\r
892 }\r
893 \r
894 /* Install/remove dialogue callback */\r
895 INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {\r
896   nssm_service_t *service;\r
897 \r
898   switch (message) {\r
899     /* Creating the dialogue */\r
900     case WM_INITDIALOG:\r
901       service = (nssm_service_t *) l;\r
902 \r
903       SetFocus(GetDlgItem(window, IDC_NAME));\r
904 \r
905       HWND tabs;\r
906       HWND combo;\r
907       HWND list;\r
908       int i, n;\r
909       tabs = GetDlgItem(window, IDC_TAB1);\r
910       if (! tabs) return 0;\r
911 \r
912       /* Set up tabs. */\r
913       TCITEM tab;\r
914       ZeroMemory(&tab, sizeof(tab));\r
915       tab.mask = TCIF_TEXT;\r
916 \r
917       selected_tab = 0;\r
918 \r
919       /* Application tab. */\r
920       if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE);\r
921       else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION);\r
922       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
923       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab);\r
924       if (service->native) {\r
925         tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg);\r
926         EnableWindow(tablist[NSSM_TAB_APPLICATION], 0);\r
927         EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0);\r
928       }\r
929       else tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg);\r
930       ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW);\r
931 \r
932       /* Details tab. */\r
933       tab.pszText = message_string(NSSM_GUI_TAB_DETAILS);\r
934       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
935       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DETAILS, (LPARAM) &tab);\r
936       tablist[NSSM_TAB_DETAILS] = dialog(MAKEINTRESOURCE(IDD_DETAILS), window, tab_dlg);\r
937       ShowWindow(tablist[NSSM_TAB_DETAILS], SW_HIDE);\r
938 \r
939       /* Set defaults. */\r
940       combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
941       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_AUTOMATIC, (LPARAM) message_string(NSSM_GUI_STARTUP_AUTOMATIC));\r
942       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DELAYED, (LPARAM) message_string(NSSM_GUI_STARTUP_DELAYED));\r
943       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_MANUAL, (LPARAM) message_string(NSSM_GUI_STARTUP_MANUAL));\r
944       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DISABLED, (LPARAM) message_string(NSSM_GUI_STARTUP_DISABLED));\r
945       SendMessage(combo, CB_SETCURSEL, NSSM_STARTUP_AUTOMATIC, 0);\r
946 \r
947       /* Logon tab. */\r
948       tab.pszText = message_string(NSSM_GUI_TAB_LOGON);\r
949       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
950       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_LOGON, (LPARAM) &tab);\r
951       tablist[NSSM_TAB_LOGON] = dialog(MAKEINTRESOURCE(IDD_LOGON), window, tab_dlg);\r
952       ShowWindow(tablist[NSSM_TAB_LOGON], SW_HIDE);\r
953 \r
954       /* Set defaults. */\r
955       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
956       set_logon_enabled(0);\r
957 \r
958       /* Remaining tabs are only for services we manage. */\r
959       if (service->native) return 1;\r
960 \r
961       /* Process tab. */\r
962       tab.pszText = message_string(NSSM_GUI_TAB_PROCESS);\r
963       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
964       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab);\r
965       tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg);\r
966       ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE);\r
967 \r
968       /* Set defaults. */\r
969       combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
970       SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS));\r
971       SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS));\r
972       SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS));\r
973       SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS));\r
974       SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS));\r
975       SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS));\r
976       SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0);\r
977 \r
978       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
979
980       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
981       n = num_cpus();\r
982       SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0);\r
983       for (i = 0; i < n; i++) {\r
984         TCHAR buffer[3];\r
985         _sntprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("%d"), i);\r
986         SendMessage(list, LB_ADDSTRING, 0, (LPARAM) buffer);\r
987       }\r
988 \r
989       /*\r
990         Size to fit.\r
991         The box is high enough for four rows.  It is wide enough for eight\r
992         columns without scrolling.  With scrollbars it shrinks to two rows.\r
993         Note that the above only holds if we set the column width BEFORE
994         adding the strings.
995       */\r
996       if (n < 32) {\r
997         int columns = (n - 1) / 4;\r
998         RECT rect;\r
999         GetWindowRect(list, &rect);\r
1000         int width = rect.right - rect.left;
1001         width -= (7 - columns) * 16;\r
1002         int height = rect.bottom - rect.top;\r
1003         if (n < 4) height -= (int) SendMessage(list, LB_GETITEMHEIGHT, 0, 0) * (4 - n);
1004         SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\r
1005       }\r
1006       SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n));\r
1007 \r
1008       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_CHECKED, 0);\r
1009       set_affinity_enabled(0);\r
1010 \r
1011       /* Shutdown tab. */\r
1012       tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN);\r
1013       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1014       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab);\r
1015       tablist[NSSM_TAB_SHUTDOWN] = dialog(MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg);\r
1016       ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE);\r
1017 \r
1018       /* Set defaults. */\r
1019       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
1020       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, NSSM_KILL_CONSOLE_GRACE_PERIOD, 0);\r
1021       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_CHECKED, 0);\r
1022       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, NSSM_KILL_WINDOW_GRACE_PERIOD, 0);\r
1023       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0);\r
1024       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0);\r
1025       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0);\r
1026 \r
1027       /* Restart tab. */\r
1028       tab.pszText = message_string(NSSM_GUI_TAB_EXIT);\r
1029       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1030       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab);\r
1031       tablist[NSSM_TAB_EXIT] = dialog(MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg);\r
1032       ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE);\r
1033 \r
1034       /* Set defaults. */\r
1035       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, NSSM_RESET_THROTTLE_RESTART, 0);\r
1036       combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
1037       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_RESTART, (LPARAM) message_string(NSSM_GUI_EXIT_RESTART));\r
1038       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_IGNORE, (LPARAM) message_string(NSSM_GUI_EXIT_IGNORE));\r
1039       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY));\r
1040       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN));\r
1041       SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0);\r
1042       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0);\r
1043 \r
1044       /* I/O tab. */\r
1045       tab.pszText = message_string(NSSM_GUI_TAB_IO);\r
1046       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1047       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab);\r
1048       tablist[NSSM_TAB_IO] = dialog(MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
1049       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);\r
1050 \r
1051       /* Rotation tab. */\r
1052       tab.pszText = message_string(NSSM_GUI_TAB_ROTATION);\r
1053       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1054       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ROTATION, (LPARAM) &tab);\r
1055       tablist[NSSM_TAB_ROTATION] = dialog(MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg);\r
1056       ShowWindow(tablist[NSSM_TAB_ROTATION], SW_HIDE);\r
1057 \r
1058       /* Set defaults. */\r
1059       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_UNCHECKED, 0);\r
1060       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, 0, 0);\r
1061       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, 0, 0);\r
1062       set_rotation_enabled(0);\r
1063 \r
1064       /* Environment tab. */\r
1065       tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);\r
1066       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1067       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);\r
1068       tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
1069       ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
1070 \r
1071       return 1;\r
1072 \r
1073     /* Tab change. */\r
1074     case WM_NOTIFY:\r
1075       NMHDR *notification;\r
1076 \r
1077       notification = (NMHDR *) l;\r
1078       switch (notification->code) {\r
1079         case TCN_SELCHANGE:\r
1080           HWND tabs;\r
1081           int selection;\r
1082 \r
1083           tabs = GetDlgItem(window, IDC_TAB1);\r
1084           if (! tabs) return 0;\r
1085 \r
1086           selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0);\r
1087           if (selection != selected_tab) {\r
1088             ShowWindow(tablist[selected_tab], SW_HIDE);\r
1089             ShowWindow(tablist[selection], SW_SHOWDEFAULT);\r
1090             SetFocus(GetDlgItem(window, IDOK));\r
1091             selected_tab = selection;\r
1092           }\r
1093           return 1;\r
1094       }\r
1095 \r
1096       return 0;\r
1097 \r
1098     /* Button was pressed or control was controlled */\r
1099     case WM_COMMAND:\r
1100       switch (LOWORD(w)) {\r
1101         /* OK button */\r
1102         case IDOK:\r
1103           if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) {\r
1104             if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0);\r
1105           }\r
1106           else if (! install(window)) PostQuitMessage(0);\r
1107           break;\r
1108 \r
1109         /* Cancel button */\r
1110         case IDCANCEL:\r
1111           DestroyWindow(window);\r
1112           break;\r
1113 \r
1114         /* Remove button */\r
1115         case IDC_REMOVE:\r
1116           if (! remove(window)) PostQuitMessage(0);\r
1117           break;\r
1118       }\r
1119       return 1;\r
1120 \r
1121     /* Window closing */\r
1122     case WM_CLOSE:\r
1123       DestroyWindow(window);\r
1124       return 0;\r
1125     case WM_DESTROY:\r
1126       PostQuitMessage(0);\r
1127   }\r
1128   return 0;\r
1129 }\r