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