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