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