Fixed regression in file browser.
[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 = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH * sizeof(TCHAR));\r
750   if (ofn.lpstrFile) {
751     if (flags & OFN_NOVALIDATE) {\r
752       /* Directory hack. */\r
753       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
754       ofn.nMaxFile = DIR_LENGTH;\r
755     }\r
756     else {
757       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T("%s"), current);\r
758       ofn.nMaxFile = PATH_LENGTH;\r
759     }
760   }
761   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
762   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
763 \r
764   if (GetOpenFileName(&ofn)) {\r
765     /* Directory hack. */\r
766     if (flags & OFN_NOVALIDATE) strip_basename(ofn.lpstrFile);\r
767     SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile);\r
768   }\r
769   if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter);\r
770   if (ofn.lpstrFile) HeapFree(GetProcessHeap(), 0, ofn.lpstrFile);
771 }\r
772 \r
773 INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {\r
774   switch (message) {\r
775     case WM_INITDIALOG:\r
776       return 1;\r
777 \r
778     /* Button was pressed or control was controlled. */\r
779     case WM_COMMAND:\r
780       HWND dlg;\r
781       TCHAR buffer[PATH_LENGTH];\r
782       unsigned char enabled;\r
783 \r
784       switch (LOWORD(w)) {\r
785         /* Browse for application. */\r
786         case IDC_BROWSE:\r
787           dlg = GetDlgItem(tab, IDC_PATH);\r
788           GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
789           browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
790           /* Fill in startup directory if it wasn't already specified. */\r
791           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
792           if (! buffer[0]) {\r
793             GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
794             strip_basename(buffer);\r
795             SetDlgItemText(tab, IDC_DIR, buffer);\r
796           }\r
797           break;\r
798 \r
799         /* Browse for startup directory. */\r
800         case IDC_BROWSE_DIR:\r
801           dlg = GetDlgItem(tab, IDC_DIR);\r
802           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
803           browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0);\r
804           break;\r
805 \r
806         /* Log on. */\r
807         case IDC_LOCALSYSTEM:\r
808           set_logon_enabled(0);\r
809           break;\r
810 \r
811         case IDC_ACCOUNT:\r
812           set_logon_enabled(1);\r
813           break;\r
814 \r
815         /* Affinity. */\r
816         case IDC_AFFINITY_ALL:\r
817           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 0;\r
818           else enabled = 1;\r
819           set_affinity_enabled(enabled);\r
820           break;\r
821 \r
822         /* Shutdown methods. */\r
823         case IDC_METHOD_CONSOLE:\r
824           set_timeout_enabled(LOWORD(w), IDC_KILL_CONSOLE);\r
825           break;\r
826 \r
827         case IDC_METHOD_WINDOW:\r
828           set_timeout_enabled(LOWORD(w), IDC_KILL_WINDOW);\r
829           break;\r
830 \r
831         case IDC_METHOD_THREADS:\r
832           set_timeout_enabled(LOWORD(w), IDC_KILL_THREADS);\r
833           break;\r
834 \r
835         /* Browse for stdin. */\r
836         case IDC_BROWSE_STDIN:\r
837           dlg = GetDlgItem(tab, IDC_STDIN);\r
838           GetDlgItemText(tab, IDC_STDIN, buffer, _countof(buffer));\r
839           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
840           break;\r
841 \r
842         /* Browse for stdout. */\r
843         case IDC_BROWSE_STDOUT:\r
844           dlg = GetDlgItem(tab, IDC_STDOUT);\r
845           GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
846           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
847           /* Fill in stderr if it wasn't already specified. */\r
848           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
849           if (! buffer[0]) {\r
850             GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
851             SetDlgItemText(tab, IDC_STDERR, buffer);\r
852           }\r
853           break;\r
854 \r
855         /* Browse for stderr. */\r
856         case IDC_BROWSE_STDERR:\r
857           dlg = GetDlgItem(tab, IDC_STDERR);\r
858           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
859           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
860           break;\r
861 \r
862         /* Rotation. */\r
863         case IDC_ROTATE:\r
864           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
865           else enabled = 0;\r
866           set_rotation_enabled(enabled);\r
867           break;\r
868       }\r
869       return 1;\r
870   }\r
871 \r
872   return 0;\r
873 }\r
874 \r
875 /* Install/remove dialogue callback */\r
876 INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {\r
877   nssm_service_t *service;\r
878 \r
879   switch (message) {\r
880     /* Creating the dialogue */\r
881     case WM_INITDIALOG:\r
882       service = (nssm_service_t *) l;\r
883 \r
884       SetFocus(GetDlgItem(window, IDC_NAME));\r
885 \r
886       HWND tabs;\r
887       HWND combo;\r
888       HWND list;\r
889       int i, n;\r
890       tabs = GetDlgItem(window, IDC_TAB1);\r
891       if (! tabs) return 0;\r
892 \r
893       /* Set up tabs. */\r
894       TCITEM tab;\r
895       ZeroMemory(&tab, sizeof(tab));\r
896       tab.mask = TCIF_TEXT;\r
897 \r
898       selected_tab = 0;\r
899 \r
900       /* Application tab. */\r
901       if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE);\r
902       else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION);\r
903       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
904       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab);\r
905       if (service->native) {\r
906         tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg);\r
907         EnableWindow(tablist[NSSM_TAB_APPLICATION], 0);\r
908         EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0);\r
909       }\r
910       else tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg);\r
911       ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW);\r
912 \r
913       /* Details tab. */\r
914       tab.pszText = message_string(NSSM_GUI_TAB_DETAILS);\r
915       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
916       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DETAILS, (LPARAM) &tab);\r
917       tablist[NSSM_TAB_DETAILS] = dialog(MAKEINTRESOURCE(IDD_DETAILS), window, tab_dlg);\r
918       ShowWindow(tablist[NSSM_TAB_DETAILS], SW_HIDE);\r
919 \r
920       /* Set defaults. */\r
921       combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
922       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_AUTOMATIC, (LPARAM) message_string(NSSM_GUI_STARTUP_AUTOMATIC));\r
923       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DELAYED, (LPARAM) message_string(NSSM_GUI_STARTUP_DELAYED));\r
924       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_MANUAL, (LPARAM) message_string(NSSM_GUI_STARTUP_MANUAL));\r
925       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DISABLED, (LPARAM) message_string(NSSM_GUI_STARTUP_DISABLED));\r
926       SendMessage(combo, CB_SETCURSEL, NSSM_STARTUP_AUTOMATIC, 0);\r
927 \r
928       /* Logon tab. */\r
929       tab.pszText = message_string(NSSM_GUI_TAB_LOGON);\r
930       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
931       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_LOGON, (LPARAM) &tab);\r
932       tablist[NSSM_TAB_LOGON] = dialog(MAKEINTRESOURCE(IDD_LOGON), window, tab_dlg);\r
933       ShowWindow(tablist[NSSM_TAB_LOGON], SW_HIDE);\r
934 \r
935       /* Set defaults. */\r
936       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
937       set_logon_enabled(0);\r
938 \r
939       /* Remaining tabs are only for services we manage. */\r
940       if (service->native) return 1;\r
941 \r
942       /* Process tab. */\r
943       tab.pszText = message_string(NSSM_GUI_TAB_PROCESS);\r
944       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
945       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab);\r
946       tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg);\r
947       ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE);\r
948 \r
949       /* Set defaults. */\r
950       combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
951       SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS));\r
952       SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS));\r
953       SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS));\r
954       SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS));\r
955       SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS));\r
956       SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS));\r
957       SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0);\r
958 \r
959       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
960       n = num_cpus();\r
961       SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0);\r
962       for (i = 0; i < n; i++) {\r
963         TCHAR buffer[3];\r
964         _sntprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("%d"), i);\r
965         SendMessage(list, LB_ADDSTRING, 0, (LPARAM) buffer);\r
966       }\r
967 \r
968       /*\r
969         Size to fit.\r
970         The box is high enough for four rows.  It is wide enough for eight\r
971         columns without scrolling.  With scrollbars it shrinks to two rows.\r
972         Note that the above only holds if we set the column width BEFORE
973         adding the strings.
974       */\r
975       if (n < 32) {\r
976         int columns = (n - 1) / 4;\r
977         RECT rect;\r
978         GetWindowRect(list, &rect);\r
979         int width = rect.right - rect.left;
980         width -= (7 - columns) * 16;\r
981         int height = rect.bottom - rect.top;\r
982         if (n < 4) height -= (int) SendMessage(list, LB_GETITEMHEIGHT, 0, 0) * (4 - n);
983         SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\r
984       }\r
985       SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n));\r
986 \r
987       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_CHECKED, 0);\r
988       set_affinity_enabled(0);\r
989 \r
990       /* Shutdown tab. */\r
991       tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN);\r
992       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
993       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab);\r
994       tablist[NSSM_TAB_SHUTDOWN] = dialog(MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg);\r
995       ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE);\r
996 \r
997       /* Set defaults. */\r
998       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
999       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, NSSM_KILL_CONSOLE_GRACE_PERIOD, 0);\r
1000       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_CHECKED, 0);\r
1001       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, NSSM_KILL_WINDOW_GRACE_PERIOD, 0);\r
1002       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0);\r
1003       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0);\r
1004       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0);\r
1005 \r
1006       /* Restart tab. */\r
1007       tab.pszText = message_string(NSSM_GUI_TAB_EXIT);\r
1008       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1009       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab);\r
1010       tablist[NSSM_TAB_EXIT] = dialog(MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg);\r
1011       ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE);\r
1012 \r
1013       /* Set defaults. */\r
1014       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, NSSM_RESET_THROTTLE_RESTART, 0);\r
1015       combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
1016       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_RESTART, (LPARAM) message_string(NSSM_GUI_EXIT_RESTART));\r
1017       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_IGNORE, (LPARAM) message_string(NSSM_GUI_EXIT_IGNORE));\r
1018       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY));\r
1019       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN));\r
1020       SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0);\r
1021       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0);\r
1022 \r
1023       /* I/O tab. */\r
1024       tab.pszText = message_string(NSSM_GUI_TAB_IO);\r
1025       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1026       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab);\r
1027       tablist[NSSM_TAB_IO] = dialog(MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
1028       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);\r
1029 \r
1030       /* Rotation tab. */\r
1031       tab.pszText = message_string(NSSM_GUI_TAB_ROTATION);\r
1032       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1033       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ROTATION, (LPARAM) &tab);\r
1034       tablist[NSSM_TAB_ROTATION] = dialog(MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg);\r
1035       ShowWindow(tablist[NSSM_TAB_ROTATION], SW_HIDE);\r
1036 \r
1037       /* Set defaults. */\r
1038       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_UNCHECKED, 0);\r
1039       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, 0, 0);\r
1040       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, 0, 0);\r
1041       set_rotation_enabled(0);\r
1042 \r
1043       /* Environment tab. */\r
1044       tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);\r
1045       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1046       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);\r
1047       tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
1048       ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
1049 \r
1050       return 1;\r
1051 \r
1052     /* Tab change. */\r
1053     case WM_NOTIFY:\r
1054       NMHDR *notification;\r
1055 \r
1056       notification = (NMHDR *) l;\r
1057       switch (notification->code) {\r
1058         case TCN_SELCHANGE:\r
1059           HWND tabs;\r
1060           int selection;\r
1061 \r
1062           tabs = GetDlgItem(window, IDC_TAB1);\r
1063           if (! tabs) return 0;\r
1064 \r
1065           selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0);\r
1066           if (selection != selected_tab) {\r
1067             ShowWindow(tablist[selected_tab], SW_HIDE);\r
1068             ShowWindow(tablist[selection], SW_SHOWDEFAULT);\r
1069             SetFocus(GetDlgItem(window, IDOK));\r
1070             selected_tab = selection;\r
1071           }\r
1072           return 1;\r
1073       }\r
1074 \r
1075       return 0;\r
1076 \r
1077     /* Button was pressed or control was controlled */\r
1078     case WM_COMMAND:\r
1079       switch (LOWORD(w)) {\r
1080         /* OK button */\r
1081         case IDOK:\r
1082           if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) {\r
1083             if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0);\r
1084           }\r
1085           else if (! install(window)) PostQuitMessage(0);\r
1086           break;\r
1087 \r
1088         /* Cancel button */\r
1089         case IDCANCEL:\r
1090           DestroyWindow(window);\r
1091           break;\r
1092 \r
1093         /* Remove button */\r
1094         case IDC_REMOVE:\r
1095           if (! remove(window)) PostQuitMessage(0);\r
1096           break;\r
1097       }\r
1098       return 1;\r
1099 \r
1100     /* Window closing */\r
1101     case WM_CLOSE:\r
1102       DestroyWindow(window);\r
1103       return 0;\r
1104     case WM_DESTROY:\r
1105       PostQuitMessage(0);\r
1106   }\r
1107   return 0;\r
1108 }\r