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