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