Handle well-known account names.
[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     if (! requires_password(service->username)) {\r
374       if (is_localsystem(service->username)) {
375         HeapFree(GetProcessHeap(), 0, service->username);\r
376         service->username = 0;\r
377         service->usernamelen = 0;\r
378       }
379       else {
380         TCHAR *canon = canonical_username(service->username);
381         HeapFree(GetProcessHeap(), 0, service->username);\r
382         service->username = 0;\r
383         service->usernamelen = 0;\r
384         if (canon) {
385           service->usernamelen = _tcslen(canon) + 1;
386           service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));
387           if (! service->username) {
388             LocalFree(canon);
389             print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("install()"));
390             return 6;
391           }
392           memmove(service->username, canon, service->usernamelen * sizeof(TCHAR));
393           LocalFree(canon);
394         }
395         else return 6;
396       }
397     }\r
398     else {\r
399       /* Password. */\r
400       service->passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), WM_GETTEXTLENGTH, 0, 0);\r
401       size_t passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), WM_GETTEXTLENGTH, 0, 0);\r
402 \r
403       if (! orig_service || ! orig_service->username || ! str_equiv(service->username, orig_service->username) || service->passwordlen || passwordlen) {\r
404         if (! service->passwordlen) {\r
405           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
406           return 6;\r
407         }\r
408         if (passwordlen != service->passwordlen) {\r
409           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
410           return 6;\r
411         }\r
412         service->passwordlen++;\r
413 \r
414         /* Temporary buffer for password validation. */\r
415         TCHAR *password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
416         if (! password) {\r
417           HeapFree(GetProcessHeap(), 0, service->username);\r
418           service->username = 0;\r
419           service->usernamelen = 0;\r
420           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password confirmation"), _T("install()"));\r
421           return 6;\r
422         }\r
423 \r
424         /* Actual password buffer. */\r
425         service->password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
426         if (! service->password) {\r
427           HeapFree(GetProcessHeap(), 0, password);\r
428           HeapFree(GetProcessHeap(), 0, service->username);\r
429           service->username = 0;\r
430           service->usernamelen = 0;\r
431           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password"), _T("install()"));\r
432           return 6;\r
433         }\r
434 \r
435         /* Get first password. */\r
436         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1, service->password, (int) service->passwordlen)) {\r
437           HeapFree(GetProcessHeap(), 0, password);\r
438           SecureZeroMemory(service->password, service->passwordlen);\r
439           HeapFree(GetProcessHeap(), 0, service->password);\r
440           service->password = 0;\r
441           service->passwordlen = 0;\r
442           HeapFree(GetProcessHeap(), 0, service->username);\r
443           service->username = 0;\r
444           service->usernamelen = 0;\r
445           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
446           return 6;\r
447         }\r
448 \r
449         /* Get confirmation. */\r
450         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2, password, (int) service->passwordlen)) {\r
451           SecureZeroMemory(password, service->passwordlen);\r
452           HeapFree(GetProcessHeap(), 0, password);\r
453           SecureZeroMemory(service->password, service->passwordlen);\r
454           HeapFree(GetProcessHeap(), 0, service->password);\r
455           service->password = 0;\r
456           service->passwordlen = 0;\r
457           HeapFree(GetProcessHeap(), 0, service->username);\r
458           service->username = 0;\r
459           service->usernamelen = 0;\r
460           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
461           return 6;\r
462         }\r
463 \r
464         /* Compare. */\r
465         if (_tcsncmp(password, service->password, service->passwordlen)) {\r
466           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
467           SecureZeroMemory(password, service->passwordlen);\r
468           HeapFree(GetProcessHeap(), 0, password);\r
469           SecureZeroMemory(service->password, service->passwordlen);\r
470           HeapFree(GetProcessHeap(), 0, service->password);\r
471           service->password = 0;\r
472           service->passwordlen = 0;\r
473           HeapFree(GetProcessHeap(), 0, service->username);\r
474           service->username = 0;\r
475           service->usernamelen = 0;\r
476           return 6;\r
477         }\r
478       }\r
479     }\r
480   }\r
481 \r
482   /* Remaining tabs are only for services we manage. */\r
483   if (service->native) return 0;\r
484 \r
485   /* Get process stuff. */\r
486   combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
487   service->priority = priority_index_to_constant((unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0));\r
488 \r
489   service->affinity = 0LL;\r
490   if (! (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_GETCHECK, 0, 0) & BST_CHECKED)) {\r
491     HWND list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
492     int selected = (int) SendMessage(list, LB_GETSELCOUNT, 0, 0);\r
493     int count = (int) SendMessage(list, LB_GETCOUNT, 0, 0);\r
494     if (! selected) {\r
495       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_WARN_AFFINITY_NONE);\r
496       return 5;\r
497     }\r
498     else if (selected < count) {\r
499       for (int i = 0; i < count; i++) {\r
500         if (SendMessage(list, LB_GETSEL, i, 0)) service->affinity |= (1LL << (__int64) i);\r
501       }\r
502     }\r
503   }\r
504 \r
505   if (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->no_console = 0;
506   else service->no_console = 1;
507
508   /* Get stop method stuff. */\r
509   check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE);\r
510   check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW);\r
511   check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS);\r
512   check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE);\r
513   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay);\r
514   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay);\r
515   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay);\r
516 \r
517   /* Get exit action stuff. */\r
518   check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay);\r
519   combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
520   service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
521   if (service->default_exit_action == CB_ERR) service->default_exit_action = 0;\r
522   check_number(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, &service->restart_delay);\r
523 \r
524   /* Get I/O stuff. */\r
525   check_io(window, _T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN);\r
526   check_io(window, _T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT);\r
527   check_io(window, _T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR);\r
528 \r
529   /* Override stdout and/or stderr. */\r
530   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
531     if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS;\r
532     if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS;\r
533   }\r
534 \r
535   /* Get rotation stuff. */\r
536   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
537     service->rotate_files = true;\r
538     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;
539     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds);\r
540     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low);\r
541   }\r
542 \r
543   /* Get environment. */\r
544   unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);\r
545   if (envlen) {\r
546     TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR));\r
547     if (! env) {\r
548       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
549       cleanup_nssm_service(service);\r
550       return 5;\r
551     }\r
552 \r
553     if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {\r
554       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
555       HeapFree(GetProcessHeap(), 0, env);\r
556       cleanup_nssm_service(service);\r
557       return 5;\r
558     }\r
559 \r
560     TCHAR *newenv;\r
561     unsigned long newlen;\r
562     if (unformat_environment(env, envlen, &newenv, &newlen)) {\r
563       HeapFree(GetProcessHeap(), 0, env);\r
564       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
565       cleanup_nssm_service(service);\r
566       return 5;\r
567     }\r
568 \r
569     HeapFree(GetProcessHeap(), 0, env);\r
570     env = newenv;\r
571     envlen = newlen;\r
572 \r
573     /* Test the environment is valid. */\r
574     if (test_environment(env)) {\r
575       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
576       HeapFree(GetProcessHeap(), 0, env);\r
577       cleanup_nssm_service(service);\r
578       return 5;\r
579     }\r
580 \r
581     if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
582       service->env = env;\r
583       service->envlen = envlen;\r
584     }\r
585     else {\r
586       service->env_extra = env;\r
587       service->env_extralen = envlen;\r
588     }\r
589   }\r
590 \r
591   return 0;\r
592 }\r
593 \r
594 /* Install the service. */\r
595 int install(HWND window) {\r
596   if (! window) return 1;\r
597 \r
598   nssm_service_t *service = alloc_nssm_service();\r
599   if (service) {\r
600     int ret = configure(window, service, 0);\r
601     if (ret) return ret;\r
602   }\r
603 \r
604   /* See if it works. */\r
605   switch (install_service(service)) {\r
606     case 1:\r
607       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("install()"));\r
608       cleanup_nssm_service(service);\r
609       return 1;\r
610 \r
611     case 2:\r
612       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
613       cleanup_nssm_service(service);\r
614       return 2;\r
615 \r
616     case 3:\r
617       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
618       cleanup_nssm_service(service);\r
619       return 3;\r
620 \r
621     case 4:\r
622       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
623       cleanup_nssm_service(service);\r
624       return 4;\r
625 \r
626     case 5:\r
627       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);\r
628       cleanup_nssm_service(service);\r
629       return 5;\r
630 \r
631     case 6:\r
632       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);\r
633       cleanup_nssm_service(service);\r
634       return 6;\r
635   }\r
636 \r
637   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
638   cleanup_nssm_service(service);\r
639   return 0;\r
640 }\r
641 \r
642 /* Remove the service */\r
643 int remove(HWND window) {\r
644   if (! window) return 1;\r
645 \r
646   /* See if it works */\r
647   nssm_service_t *service = alloc_nssm_service();\r
648   if (service) {\r
649     /* Get service name */\r
650     if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
651       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
652       cleanup_nssm_service(service);\r
653       return 2;\r
654     }\r
655 \r
656     /* Confirm */\r
657     if (popup_message(window, MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {\r
658       cleanup_nssm_service(service);\r
659       return 0;\r
660     }\r
661   }\r
662 \r
663   switch (remove_service(service)) {\r
664     case 1:\r
665       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("remove()"));\r
666       cleanup_nssm_service(service);\r
667       return 1;\r
668 \r
669     case 2:\r
670       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
671       cleanup_nssm_service(service);\r
672       return 2;\r
673 \r
674     case 3:\r
675       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
676       return 3;\r
677       cleanup_nssm_service(service);\r
678 \r
679     case 4:\r
680       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
681       cleanup_nssm_service(service);\r
682       return 4;\r
683   }\r
684 \r
685   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
686   cleanup_nssm_service(service);\r
687   return 0;\r
688 }\r
689 \r
690 int edit(HWND window, nssm_service_t *orig_service) {\r
691   if (! window) return 1;\r
692 \r
693   nssm_service_t *service = alloc_nssm_service();\r
694   if (service) {\r
695     int ret = configure(window, service, orig_service);\r
696     if (ret) return ret;\r
697   }\r
698 \r
699   switch (edit_service(service, true)) {\r
700     case 1:\r
701       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()"));\r
702       cleanup_nssm_service(service);\r
703       return 1;\r
704 \r
705     case 3:\r
706       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
707       cleanup_nssm_service(service);\r
708       return 3;\r
709 \r
710     case 4:\r
711       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
712       cleanup_nssm_service(service);\r
713       return 4;\r
714 \r
715     case 5:\r
716     case 6:\r
717       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED);\r
718       cleanup_nssm_service(service);\r
719       return 6;\r
720   }\r
721 \r
722   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name);\r
723   cleanup_nssm_service(service);\r
724   return 0;\r
725 }\r
726 \r
727 static TCHAR *browse_filter(int message) {\r
728   switch (message) {\r
729     case NSSM_GUI_BROWSE_FILTER_APPLICATIONS: return _T("*.exe;*.bat;*.cmd");\r
730     case NSSM_GUI_BROWSE_FILTER_DIRECTORIES: return _T(".");\r
731     case NSSM_GUI_BROWSE_FILTER_ALL_FILES: /* Fall through. */\r
732     default: return _T("*.*");\r
733   }\r
734 }\r
735 \r
736 UINT_PTR CALLBACK browse_hook(HWND dlg, UINT message, WPARAM w, LPARAM l) {\r
737   switch (message) {\r
738     case WM_INITDIALOG:\r
739       return 1;\r
740   }\r
741 \r
742   return 0;\r
743 }\r
744 \r
745 /* Browse for application */\r
746 void browse(HWND window, TCHAR *current, unsigned long flags, ...) {\r
747   if (! window) return;\r
748 \r
749   va_list arg;\r
750   size_t bufsize = 256;\r
751   size_t len = bufsize;\r
752   int i;\r
753 \r
754   OPENFILENAME ofn;\r
755   ZeroMemory(&ofn, sizeof(ofn));\r
756   ofn.lStructSize = sizeof(ofn);\r
757   ofn.lpstrFilter = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, bufsize * sizeof(TCHAR));\r
758   /* XXX: Escaping nulls with FormatMessage is tricky */\r
759   if (ofn.lpstrFilter) {\r
760     ZeroMemory((void *) ofn.lpstrFilter, bufsize);\r
761     len = 0;\r
762     /* "Applications" + NULL + "*.exe" + NULL */\r
763     va_start(arg, flags);\r
764     while (i = va_arg(arg, int)) {\r
765       TCHAR *localised = message_string(i);\r
766       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize, _TRUNCATE, localised);\r
767       len += _tcslen(localised) + 1;\r
768       LocalFree(localised);\r
769       TCHAR *filter = browse_filter(i);\r
770       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, _T("%s"), filter);\r
771       len += _tcslen(filter) + 1;\r
772     }\r
773     va_end(arg);\r
774     /* Remainder of the buffer is already zeroed */\r
775   }\r
776   ofn.lpstrFile = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH * sizeof(TCHAR));\r
777   if (ofn.lpstrFile) {
778     if (flags & OFN_NOVALIDATE) {\r
779       /* Directory hack. */\r
780       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
781       ofn.nMaxFile = DIR_LENGTH;\r
782     }\r
783     else {
784       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T("%s"), current);\r
785       ofn.nMaxFile = PATH_LENGTH;\r
786     }
787   }
788   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
789   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
790 \r
791   if (GetOpenFileName(&ofn)) {\r
792     /* Directory hack. */\r
793     if (flags & OFN_NOVALIDATE) strip_basename(ofn.lpstrFile);\r
794     SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile);\r
795   }\r
796   if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter);\r
797   if (ofn.lpstrFile) HeapFree(GetProcessHeap(), 0, ofn.lpstrFile);
798 }\r
799 \r
800 INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {\r
801   switch (message) {\r
802     case WM_INITDIALOG:\r
803       return 1;\r
804 \r
805     /* Button was pressed or control was controlled. */\r
806     case WM_COMMAND:\r
807       HWND dlg;\r
808       TCHAR buffer[PATH_LENGTH];\r
809       unsigned char enabled;\r
810 \r
811       switch (LOWORD(w)) {\r
812         /* Browse for application. */\r
813         case IDC_BROWSE:\r
814           dlg = GetDlgItem(tab, IDC_PATH);\r
815           GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
816           browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
817           /* Fill in startup directory if it wasn't already specified. */\r
818           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
819           if (! buffer[0]) {\r
820             GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
821             strip_basename(buffer);\r
822             SetDlgItemText(tab, IDC_DIR, buffer);\r
823           }\r
824           break;\r
825 \r
826         /* Browse for startup directory. */\r
827         case IDC_BROWSE_DIR:\r
828           dlg = GetDlgItem(tab, IDC_DIR);\r
829           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
830           browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0);\r
831           break;\r
832 \r
833         /* Log on. */\r
834         case IDC_LOCALSYSTEM:\r
835           set_logon_enabled(0);\r
836           break;\r
837 \r
838         case IDC_ACCOUNT:\r
839           set_logon_enabled(1);\r
840           break;\r
841 \r
842         /* Affinity. */\r
843         case IDC_AFFINITY_ALL:\r
844           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 0;\r
845           else enabled = 1;\r
846           set_affinity_enabled(enabled);\r
847           break;\r
848 \r
849         /* Shutdown methods. */\r
850         case IDC_METHOD_CONSOLE:\r
851           set_timeout_enabled(LOWORD(w), IDC_KILL_CONSOLE);\r
852           break;\r
853 \r
854         case IDC_METHOD_WINDOW:\r
855           set_timeout_enabled(LOWORD(w), IDC_KILL_WINDOW);\r
856           break;\r
857 \r
858         case IDC_METHOD_THREADS:\r
859           set_timeout_enabled(LOWORD(w), IDC_KILL_THREADS);\r
860           break;\r
861 \r
862         /* Browse for stdin. */\r
863         case IDC_BROWSE_STDIN:\r
864           dlg = GetDlgItem(tab, IDC_STDIN);\r
865           GetDlgItemText(tab, IDC_STDIN, buffer, _countof(buffer));\r
866           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
867           break;\r
868 \r
869         /* Browse for stdout. */\r
870         case IDC_BROWSE_STDOUT:\r
871           dlg = GetDlgItem(tab, IDC_STDOUT);\r
872           GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
873           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
874           /* Fill in stderr if it wasn't already specified. */\r
875           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
876           if (! buffer[0]) {\r
877             GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
878             SetDlgItemText(tab, IDC_STDERR, buffer);\r
879           }\r
880           break;\r
881 \r
882         /* Browse for stderr. */\r
883         case IDC_BROWSE_STDERR:\r
884           dlg = GetDlgItem(tab, IDC_STDERR);\r
885           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
886           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
887           break;\r
888 \r
889         /* Rotation. */\r
890         case IDC_ROTATE:\r
891           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
892           else enabled = 0;\r
893           set_rotation_enabled(enabled);\r
894           break;\r
895       }\r
896       return 1;\r
897   }\r
898 \r
899   return 0;\r
900 }\r
901 \r
902 /* Install/remove dialogue callback */\r
903 INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {\r
904   nssm_service_t *service;\r
905 \r
906   switch (message) {\r
907     /* Creating the dialogue */\r
908     case WM_INITDIALOG:\r
909       service = (nssm_service_t *) l;\r
910 \r
911       SetFocus(GetDlgItem(window, IDC_NAME));\r
912 \r
913       HWND tabs;\r
914       HWND combo;\r
915       HWND list;\r
916       int i, n;\r
917       tabs = GetDlgItem(window, IDC_TAB1);\r
918       if (! tabs) return 0;\r
919 \r
920       /* Set up tabs. */\r
921       TCITEM tab;\r
922       ZeroMemory(&tab, sizeof(tab));\r
923       tab.mask = TCIF_TEXT;\r
924 \r
925       selected_tab = 0;\r
926 \r
927       /* Application tab. */\r
928       if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE);\r
929       else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION);\r
930       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
931       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab);\r
932       if (service->native) {\r
933         tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg);\r
934         EnableWindow(tablist[NSSM_TAB_APPLICATION], 0);\r
935         EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0);\r
936       }\r
937       else tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg);\r
938       ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW);\r
939 \r
940       /* Details tab. */\r
941       tab.pszText = message_string(NSSM_GUI_TAB_DETAILS);\r
942       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
943       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DETAILS, (LPARAM) &tab);\r
944       tablist[NSSM_TAB_DETAILS] = dialog(MAKEINTRESOURCE(IDD_DETAILS), window, tab_dlg);\r
945       ShowWindow(tablist[NSSM_TAB_DETAILS], SW_HIDE);\r
946 \r
947       /* Set defaults. */\r
948       combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
949       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_AUTOMATIC, (LPARAM) message_string(NSSM_GUI_STARTUP_AUTOMATIC));\r
950       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DELAYED, (LPARAM) message_string(NSSM_GUI_STARTUP_DELAYED));\r
951       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_MANUAL, (LPARAM) message_string(NSSM_GUI_STARTUP_MANUAL));\r
952       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DISABLED, (LPARAM) message_string(NSSM_GUI_STARTUP_DISABLED));\r
953       SendMessage(combo, CB_SETCURSEL, NSSM_STARTUP_AUTOMATIC, 0);\r
954 \r
955       /* Logon tab. */\r
956       tab.pszText = message_string(NSSM_GUI_TAB_LOGON);\r
957       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
958       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_LOGON, (LPARAM) &tab);\r
959       tablist[NSSM_TAB_LOGON] = dialog(MAKEINTRESOURCE(IDD_LOGON), window, tab_dlg);\r
960       ShowWindow(tablist[NSSM_TAB_LOGON], SW_HIDE);\r
961 \r
962       /* Set defaults. */\r
963       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
964       set_logon_enabled(0);\r
965 \r
966       /* Remaining tabs are only for services we manage. */\r
967       if (service->native) return 1;\r
968 \r
969       /* Process tab. */\r
970       tab.pszText = message_string(NSSM_GUI_TAB_PROCESS);\r
971       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
972       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab);\r
973       tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg);\r
974       ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE);\r
975 \r
976       /* Set defaults. */\r
977       combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
978       SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS));\r
979       SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS));\r
980       SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS));\r
981       SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS));\r
982       SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS));\r
983       SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS));\r
984       SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0);\r
985 \r
986       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
987
988       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
989       n = num_cpus();\r
990       SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0);\r
991       for (i = 0; i < n; i++) {\r
992         TCHAR buffer[3];\r
993         _sntprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("%d"), i);\r
994         SendMessage(list, LB_ADDSTRING, 0, (LPARAM) buffer);\r
995       }\r
996 \r
997       /*\r
998         Size to fit.\r
999         The box is high enough for four rows.  It is wide enough for eight\r
1000         columns without scrolling.  With scrollbars it shrinks to two rows.\r
1001         Note that the above only holds if we set the column width BEFORE
1002         adding the strings.
1003       */\r
1004       if (n < 32) {\r
1005         int columns = (n - 1) / 4;\r
1006         RECT rect;\r
1007         GetWindowRect(list, &rect);\r
1008         int width = rect.right - rect.left;
1009         width -= (7 - columns) * 16;\r
1010         int height = rect.bottom - rect.top;\r
1011         if (n < 4) height -= (int) SendMessage(list, LB_GETITEMHEIGHT, 0, 0) * (4 - n);
1012         SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\r
1013       }\r
1014       SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n));\r
1015 \r
1016       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_CHECKED, 0);\r
1017       set_affinity_enabled(0);\r
1018 \r
1019       /* Shutdown tab. */\r
1020       tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN);\r
1021       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1022       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab);\r
1023       tablist[NSSM_TAB_SHUTDOWN] = dialog(MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg);\r
1024       ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE);\r
1025 \r
1026       /* Set defaults. */\r
1027       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
1028       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, NSSM_KILL_CONSOLE_GRACE_PERIOD, 0);\r
1029       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_CHECKED, 0);\r
1030       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, NSSM_KILL_WINDOW_GRACE_PERIOD, 0);\r
1031       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0);\r
1032       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0);\r
1033       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0);\r
1034 \r
1035       /* Restart tab. */\r
1036       tab.pszText = message_string(NSSM_GUI_TAB_EXIT);\r
1037       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1038       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab);\r
1039       tablist[NSSM_TAB_EXIT] = dialog(MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg);\r
1040       ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE);\r
1041 \r
1042       /* Set defaults. */\r
1043       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, NSSM_RESET_THROTTLE_RESTART, 0);\r
1044       combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
1045       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_RESTART, (LPARAM) message_string(NSSM_GUI_EXIT_RESTART));\r
1046       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_IGNORE, (LPARAM) message_string(NSSM_GUI_EXIT_IGNORE));\r
1047       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY));\r
1048       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN));\r
1049       SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0);\r
1050       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0);\r
1051 \r
1052       /* I/O tab. */\r
1053       tab.pszText = message_string(NSSM_GUI_TAB_IO);\r
1054       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1055       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab);\r
1056       tablist[NSSM_TAB_IO] = dialog(MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
1057       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);\r
1058 \r
1059       /* Rotation tab. */\r
1060       tab.pszText = message_string(NSSM_GUI_TAB_ROTATION);\r
1061       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1062       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ROTATION, (LPARAM) &tab);\r
1063       tablist[NSSM_TAB_ROTATION] = dialog(MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg);\r
1064       ShowWindow(tablist[NSSM_TAB_ROTATION], SW_HIDE);\r
1065 \r
1066       /* Set defaults. */\r
1067       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_UNCHECKED, 0);\r
1068       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, 0, 0);\r
1069       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, 0, 0);\r
1070       set_rotation_enabled(0);\r
1071 \r
1072       /* Environment tab. */\r
1073       tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);\r
1074       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1075       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);\r
1076       tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
1077       ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
1078 \r
1079       return 1;\r
1080 \r
1081     /* Tab change. */\r
1082     case WM_NOTIFY:\r
1083       NMHDR *notification;\r
1084 \r
1085       notification = (NMHDR *) l;\r
1086       switch (notification->code) {\r
1087         case TCN_SELCHANGE:\r
1088           HWND tabs;\r
1089           int selection;\r
1090 \r
1091           tabs = GetDlgItem(window, IDC_TAB1);\r
1092           if (! tabs) return 0;\r
1093 \r
1094           selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0);\r
1095           if (selection != selected_tab) {\r
1096             ShowWindow(tablist[selected_tab], SW_HIDE);\r
1097             ShowWindow(tablist[selection], SW_SHOWDEFAULT);\r
1098             SetFocus(GetDlgItem(window, IDOK));\r
1099             selected_tab = selection;\r
1100           }\r
1101           return 1;\r
1102       }\r
1103 \r
1104       return 0;\r
1105 \r
1106     /* Button was pressed or control was controlled */\r
1107     case WM_COMMAND:\r
1108       switch (LOWORD(w)) {\r
1109         /* OK button */\r
1110         case IDOK:\r
1111           if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) {\r
1112             if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0);\r
1113           }\r
1114           else if (! install(window)) PostQuitMessage(0);\r
1115           break;\r
1116 \r
1117         /* Cancel button */\r
1118         case IDCANCEL:\r
1119           DestroyWindow(window);\r
1120           break;\r
1121 \r
1122         /* Remove button */\r
1123         case IDC_REMOVE:\r
1124           if (! remove(window)) PostQuitMessage(0);\r
1125           break;\r
1126       }\r
1127       return 1;\r
1128 \r
1129     /* Window closing */\r
1130     case WM_CLOSE:\r
1131       DestroyWindow(window);\r
1132       return 0;\r
1133     case WM_DESTROY:\r
1134       PostQuitMessage(0);\r
1135   }\r
1136   return 0;\r
1137 }\r