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