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