Handle virtual accounts when dumping service config.
[nssm.git] / gui.cpp
1 #include "nssm.h"\r
2 \r
3 extern const TCHAR *hook_event_strings[];\r
4 extern const TCHAR *hook_action_strings[];\r
5 \r
6 static enum { NSSM_TAB_APPLICATION, NSSM_TAB_DETAILS, NSSM_TAB_LOGON, NSSM_TAB_DEPENDENCIES, NSSM_TAB_PROCESS, NSSM_TAB_SHUTDOWN, NSSM_TAB_EXIT, NSSM_TAB_IO, NSSM_TAB_ROTATION, NSSM_TAB_ENVIRONMENT, NSSM_TAB_HOOKS, NSSM_NUM_TABS } nssm_tabs;\r
7 static HWND tablist[NSSM_NUM_TABS];\r
8 static int selected_tab;\r
9 \r
10 static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function, LPARAM l) {\r
11   /* The caller will deal with GetLastError()... */\r
12   HRSRC resource = FindResourceEx(0, RT_DIALOG, templ, GetUserDefaultLangID());\r
13   if (! resource) {\r
14     if (GetLastError() != ERROR_RESOURCE_LANG_NOT_FOUND) return 0;\r
15     resource = FindResourceEx(0, RT_DIALOG, templ, MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL));\r
16     if (! resource) return 0;\r
17   }\r
18 \r
19   HGLOBAL ret = LoadResource(0, resource);\r
20   if (! ret) return 0;\r
21 \r
22   return CreateDialogIndirectParam(0, (DLGTEMPLATE *) ret, parent, function, l);\r
23 }\r
24 \r
25 static HWND dialog(const TCHAR *templ, HWND parent, DLGPROC function) {\r
26   return dialog(templ, parent, function, 0);\r
27 }\r
28 \r
29 static inline void set_logon_enabled(unsigned char interact_enabled, unsigned char credentials_enabled) {\r
30   EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_INTERACT), interact_enabled);\r
31   EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), credentials_enabled);\r
32   EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), credentials_enabled);\r
33   EnableWindow(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), credentials_enabled);\r
34 }\r
35 \r
36 int nssm_gui(int resource, nssm_service_t *service) {\r
37   /* Create window */\r
38   HWND dlg = dialog(MAKEINTRESOURCE(resource), 0, nssm_dlg, (LPARAM) service);\r
39   if (! dlg) {\r
40     popup_message(0, MB_OK, NSSM_GUI_CREATEDIALOG_FAILED, error_string(GetLastError()));\r
41     return 1;\r
42   }\r
43 \r
44   /* Load the icon. */\r
45   HANDLE icon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_NSSM), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);\r
46   if (icon) SendMessage(dlg, WM_SETICON, ICON_SMALL, (LPARAM) icon);\r
47   icon = LoadImage(GetModuleHandle(0), MAKEINTRESOURCE(IDI_NSSM), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0);\r
48   if (icon) SendMessage(dlg, WM_SETICON, ICON_BIG, (LPARAM) icon);\r
49 \r
50   /* Remember what the window is for. */\r
51   SetWindowLongPtr(dlg, GWLP_USERDATA, (LONG_PTR) resource);\r
52 \r
53   /* Display the window */\r
54   centre_window(dlg);\r
55   ShowWindow(dlg, SW_SHOW);\r
56 \r
57   /* Set service name if given */\r
58   if (service->name[0]) {\r
59     SetDlgItemText(dlg, IDC_NAME, service->name);\r
60     /* No point making user click remove if the name is already entered */\r
61     if (resource == IDD_REMOVE) {\r
62       HWND button = GetDlgItem(dlg, IDC_REMOVE);\r
63       if (button) {\r
64         SendMessage(button, WM_LBUTTONDOWN, 0, 0);\r
65         SendMessage(button, WM_LBUTTONUP, 0, 0);\r
66       }\r
67     }\r
68   }\r
69 \r
70   if (resource == IDD_EDIT) {\r
71     /* We'll need the service handle later. */\r
72     SetWindowLongPtr(dlg, DWLP_USER, (LONG_PTR) service);\r
73 \r
74     /* Service name can't be edited. */\r
75     EnableWindow(GetDlgItem(dlg, IDC_NAME), 0);\r
76     SetFocus(GetDlgItem(dlg, IDOK));\r
77 \r
78     /* Set existing details. */\r
79     HWND combo;\r
80     HWND list;\r
81 \r
82     /* Application tab. */\r
83     if (service->native) SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->image);\r
84     else SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe);\r
85     SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir);\r
86     SetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags);\r
87 \r
88     /* Details tab. */\r
89     SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname);\r
90     SetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description);\r
91     combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
92     SendMessage(combo, CB_SETCURSEL, service->startup, 0);\r
93 \r
94     /* Log on tab. */\r
95     if (service->username) {\r
96       if (is_virtual_account(service->name, service->username)) {\r
97         CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_VIRTUAL_SERVICE);\r
98         set_logon_enabled(0, 0);\r
99       }\r
100       else {\r
101         CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_ACCOUNT);\r
102         SetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username);\r
103         set_logon_enabled(0, 1);\r
104       }\r
105     }\r
106     else {\r
107       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_VIRTUAL_SERVICE, IDC_LOCALSYSTEM);\r
108       if (service->type & SERVICE_INTERACTIVE_PROCESS) SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_SETCHECK, BST_CHECKED, 0);\r
109     }\r
110 \r
111     /* Dependencies tab. */\r
112     if (service->dependencieslen) {\r
113       TCHAR *formatted;\r
114       unsigned long newlen;\r
115       if (format_double_null(service->dependencies, service->dependencieslen, &formatted, &newlen)) {\r
116         popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("nssm_dlg()"));\r
117       }\r
118       else {\r
119         SetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, formatted);\r
120         HeapFree(GetProcessHeap(), 0, formatted);\r
121       }\r
122     }\r
123 \r
124     /* Process tab. */\r
125     if (service->priority) {\r
126       int priority = priority_constant_to_index(service->priority);\r
127       combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
128       SendMessage(combo, CB_SETCURSEL, priority, 0);\r
129     }\r
130 \r
131     if (service->affinity) {\r
132       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
133       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_UNCHECKED, 0);\r
134       EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), 1);\r
135 \r
136       DWORD_PTR affinity, system_affinity;\r
137       if (GetProcessAffinityMask(GetCurrentProcess(), &affinity, &system_affinity)) {\r
138         if ((service->affinity & (__int64) system_affinity) != service->affinity) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_AFFINITY);\r
139       }\r
140 \r
141       for (int i = 0; i < num_cpus(); i++) {\r
142         if (! (service->affinity & (1LL << (__int64) i))) SendMessage(list, LB_SETSEL, 0, i);\r
143       }\r
144     }\r
145 \r
146     if (service->no_console) {\r
147       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0);\r
148     }\r
149 \r
150     /* Shutdown tab. */\r
151     if (! (service->stop_method & NSSM_STOP_METHOD_CONSOLE)) {\r
152       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_UNCHECKED, 0);\r
153       EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE), 0);\r
154     }\r
155     SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, service->kill_console_delay, 0);\r
156     if (! (service->stop_method & NSSM_STOP_METHOD_WINDOW)) {\r
157       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_UNCHECKED, 0);\r
158       EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW), 0);\r
159     }\r
160     SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, service->kill_window_delay, 0);\r
161     if (! (service->stop_method & NSSM_STOP_METHOD_THREADS)) {\r
162       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_UNCHECKED, 0);\r
163       EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS), 0);\r
164     }\r
165     SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, service->kill_threads_delay, 0);\r
166     if (! (service->stop_method & NSSM_STOP_METHOD_TERMINATE)) {\r
167       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_UNCHECKED, 0);\r
168     }\r
169     if (! service->kill_process_tree) {\r
170       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_SETCHECK, BST_UNCHECKED, 0);\r
171     }\r
172 \r
173     /* Restart tab. */\r
174     SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, service->throttle_delay, 0);\r
175     combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
176     SendMessage(combo, CB_SETCURSEL, service->default_exit_action, 0);\r
177     SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, service->restart_delay, 0);\r
178 \r
179     /* I/O tab. */\r
180     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDIN, service->stdin_path);\r
181     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDOUT, service->stdout_path);\r
182     SetDlgItemText(tablist[NSSM_TAB_IO], IDC_STDERR, service->stderr_path);\r
183 \r
184     /* Rotation tab. */\r
185     if (service->stdout_disposition == CREATE_ALWAYS) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_SETCHECK, BST_CHECKED, 0);\r
186     if (service->rotate_files) {\r
187       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_SETCHECK, BST_CHECKED, 0);\r
188       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), 1);\r
189       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), 1);\r
190       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), 1);\r
191     }\r
192     if (service->rotate_stdout_online || service->rotate_stderr_online) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_CHECKED, 0);\r
193     SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, service->rotate_seconds, 0);\r
194     if (! service->rotate_bytes_high) SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, service->rotate_bytes_low, 0);\r
195 \r
196     /* Hooks tab. */\r
197     if (service->hook_share_output_handles) SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_SETCHECK, BST_CHECKED, 0);\r
198 \r
199     /* Check if advanced settings are in use. */\r
200     if (service->stdout_disposition != service->stderr_disposition || (service->stdout_disposition && service->stdout_disposition != NSSM_STDOUT_DISPOSITION && service->stdout_disposition != CREATE_ALWAYS) || (service->stderr_disposition && service->stderr_disposition != NSSM_STDERR_DISPOSITION && service->stderr_disposition != CREATE_ALWAYS)) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_STDIO);\r
201     if (service->rotate_bytes_high) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ROTATE_BYTES);\r
202 \r
203     /* Environment tab. */\r
204     TCHAR *env;\r
205     unsigned long envlen;\r
206     if (service->env_extralen) {\r
207       env = service->env_extra;\r
208       envlen = service->env_extralen;\r
209     }\r
210     else {\r
211       env = service->env;\r
212       envlen = service->envlen;\r
213       if (envlen) SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_SETCHECK, BST_CHECKED, 0);\r
214     }\r
215 \r
216     if (envlen) {\r
217       TCHAR *formatted;\r
218       unsigned long newlen;\r
219       if (format_double_null(env, envlen, &formatted, &newlen)) {\r
220         popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("nssm_dlg()"));\r
221       }\r
222       else {\r
223         SetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, formatted);\r
224         HeapFree(GetProcessHeap(), 0, formatted);\r
225       }\r
226     }\r
227     if (service->envlen && service->env_extralen) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ENVIRONMENT);\r
228   }\r
229 \r
230   /* Go! */\r
231   MSG message;\r
232   while (GetMessage(&message, 0, 0, 0)) {\r
233     if (IsDialogMessage(dlg, &message)) continue;\r
234     TranslateMessage(&message);\r
235     DispatchMessage(&message);\r
236   }\r
237 \r
238   return (int) message.wParam;\r
239 }\r
240 \r
241 void centre_window(HWND window) {\r
242   HWND desktop;\r
243   RECT size, desktop_size;\r
244   unsigned long x, y;\r
245 \r
246   if (! window) return;\r
247 \r
248   /* Find window size */\r
249   if (! GetWindowRect(window, &size)) return;\r
250 \r
251   /* Find desktop window */\r
252   desktop = GetDesktopWindow();\r
253   if (! desktop) return;\r
254 \r
255   /* Find desktop window size */\r
256   if (! GetWindowRect(desktop, &desktop_size)) return;\r
257 \r
258   /* Centre window */\r
259   x = (desktop_size.right - size.right) / 2;\r
260   y = (desktop_size.bottom - size.bottom) / 2;\r
261   MoveWindow(window, x, y, size.right - size.left, size.bottom - size.top, 0);\r
262 }\r
263 \r
264 static inline void check_stop_method(nssm_service_t *service, unsigned long method, unsigned long control) {\r
265   if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) return;\r
266   service->stop_method &= ~method;\r
267 }\r
268 \r
269 static inline void check_number(HWND tab, unsigned long control, unsigned long *timeout) {\r
270   BOOL translated;\r
271   unsigned long configured = GetDlgItemInt(tab, control, &translated, 0);\r
272   if (translated) *timeout = configured;\r
273 }\r
274 \r
275 static inline void set_timeout_enabled(unsigned long control, unsigned long dependent) {\r
276   unsigned char enabled = 0;\r
277   if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
278   EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], dependent), enabled);\r
279 }\r
280 \r
281 static inline void set_affinity_enabled(unsigned char enabled) {\r
282   EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), enabled);\r
283 }\r
284 \r
285 static inline void set_rotation_enabled(unsigned char enabled) {\r
286   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), enabled);\r
287   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), enabled);\r
288   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), enabled);\r
289 }\r
290 \r
291 static inline int hook_env(const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {\r
292   return _sntprintf_s(buffer, buflen, _TRUNCATE, _T("NSSM_HOOK_%s_%s"), hook_event, hook_action);\r
293 }\r
294 \r
295 static inline void set_hook_tab(int event_index, int action_index, bool changed) {\r
296   int first_event = NSSM_GUI_HOOK_EVENT_START;\r
297   HWND combo;\r
298   combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_EVENT);\r
299   SendMessage(combo, CB_SETCURSEL, event_index, 0);\r
300   combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_ACTION);\r
301   SendMessage(combo, CB_RESETCONTENT, 0, 0);\r
302 \r
303   const TCHAR *hook_event = hook_event_strings[event_index];\r
304   TCHAR *hook_action;\r
305   int i;\r
306   switch (event_index + first_event) {\r
307     case NSSM_GUI_HOOK_EVENT_ROTATE:\r
308       i = 0;\r
309       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_ROTATE_PRE));\r
310       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
311       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_ROTATE_POST));\r
312       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
313       break;\r
314 \r
315     case NSSM_GUI_HOOK_EVENT_START:\r
316       i = 0;\r
317       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_START_PRE));\r
318       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
319       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_START_POST));\r
320       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
321       break;\r
322 \r
323     case NSSM_GUI_HOOK_EVENT_STOP:\r
324       i = 0;\r
325       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_STOP_PRE));\r
326       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
327       break;\r
328 \r
329     case NSSM_GUI_HOOK_EVENT_EXIT:\r
330       i = 0;\r
331       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_EXIT_POST));\r
332       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
333       break;\r
334 \r
335     case NSSM_GUI_HOOK_EVENT_POWER:\r
336       i = 0;\r
337       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_POWER_CHANGE));\r
338       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_CHANGE;\r
339       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_POWER_RESUME));\r
340       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_RESUME;\r
341       break;\r
342   }\r
343 \r
344   SendMessage(combo, CB_SETCURSEL, action_index, 0);\r
345 \r
346   TCHAR hook_name[HOOK_NAME_LENGTH];\r
347   hook_env(hook_event, hook_action, hook_name, _countof(hook_name));\r
348 \r
349   if (! *hook_name) return;\r
350 \r
351   TCHAR cmd[CMD_LENGTH];\r
352   if (changed) {\r
353     GetDlgItemText(tablist[NSSM_TAB_HOOKS], IDC_HOOK, cmd, _countof(cmd));\r
354     SetEnvironmentVariable(hook_name, cmd);\r
355   }\r
356   else {\r
357     GetEnvironmentVariable(hook_name, cmd, _countof(cmd));\r
358     SetDlgItemText(tablist[NSSM_TAB_HOOKS], IDC_HOOK, cmd);\r
359   }\r
360 }\r
361 \r
362 static inline int update_hook(TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action) {\r
363   TCHAR hook_name[HOOK_NAME_LENGTH];\r
364   if (hook_env(hook_event, hook_action, hook_name, _countof(hook_name)) < 0) return 1;\r
365   TCHAR cmd[CMD_LENGTH];\r
366   ZeroMemory(cmd, sizeof(cmd));\r
367   GetEnvironmentVariable(hook_name, cmd, _countof(cmd));\r
368   if (set_hook(service_name, hook_event, hook_action, cmd)) return 2;\r
369   return 0;\r
370 }\r
371 \r
372 static inline int update_hooks(TCHAR *service_name) {\r
373   int ret = 0;\r
374   ret += update_hook(service_name, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE);\r
375   ret += update_hook(service_name, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST);\r
376   ret += update_hook(service_name, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE);\r
377   ret += update_hook(service_name, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST);\r
378   ret += update_hook(service_name, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE);\r
379   ret += update_hook(service_name, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME);\r
380   ret += update_hook(service_name, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE);\r
381   ret += update_hook(service_name, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST);\r
382   return ret;\r
383 }\r
384 \r
385 static inline void check_io(HWND owner, TCHAR *name, TCHAR *buffer, unsigned long len, unsigned long control) {\r
386   if (! SendMessage(GetDlgItem(tablist[NSSM_TAB_IO], control), WM_GETTEXTLENGTH, 0, 0)) return;\r
387   if (GetDlgItemText(tablist[NSSM_TAB_IO], control, buffer, (int) len)) return;\r
388   popup_message(owner, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name);\r
389   ZeroMemory(buffer, len * sizeof(TCHAR));\r
390 }\r
391 \r
392 /* Set service parameters. */\r
393 int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service) {\r
394   if (! service) return 1;\r
395 \r
396   set_nssm_service_defaults(service);\r
397 \r
398   if (orig_service) {\r
399     service->native = orig_service->native;\r
400     service->handle = orig_service->handle;\r
401   }\r
402 \r
403   /* Get service name. */\r
404   if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
405     popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
406     cleanup_nssm_service(service);\r
407     return 2;\r
408   }\r
409 \r
410   /* Get executable name */\r
411   if (! service->native) {\r
412     if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe, _countof(service->exe))) {\r
413       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
414       return 3;\r
415     }\r
416 \r
417     /* Get startup directory. */\r
418     if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir, _countof(service->dir))) {\r
419       _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
420       strip_basename(service->dir);\r
421     }\r
422 \r
423     /* Get flags. */\r
424     if (SendMessage(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) {\r
425       if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags, _countof(service->flags))) {\r
426         popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
427         return 4;\r
428       }\r
429     }\r
430   }\r
431 \r
432   /* Get details. */\r
433   if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME), WM_GETTEXTLENGTH, 0, 0)) {\r
434     if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname, _countof(service->displayname))) {\r
435       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME);\r
436       return 5;\r
437     }\r
438   }\r
439 \r
440   if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION), WM_GETTEXTLENGTH, 0, 0)) {\r
441     if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description, _countof(service->description))) {\r
442       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION);\r
443       return 5;\r
444     }\r
445   }\r
446 \r
447   HWND combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
448   service->startup = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
449   if (service->startup == CB_ERR) service->startup = 0;\r
450 \r
451   /* Get logon stuff. */\r
452   if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
453     if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
454       service->type |= SERVICE_INTERACTIVE_PROCESS;\r
455     }\r
456     if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
457     service->username = 0;\r
458     service->usernamelen = 0;\r
459     if (service->password) {\r
460       SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
461       HeapFree(GetProcessHeap(), 0, service->password);\r
462     }\r
463     service->password = 0;\r
464     service->passwordlen = 0;\r
465   }\r
466   else if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_VIRTUAL_SERVICE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
467     if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
468     service->username = virtual_account(service->name);\r
469     if (! service->username) {\r
470       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
471       return 6;\r
472     }\r
473     service->usernamelen = _tcslen(service->username) + 1;\r
474     service->password = 0;\r
475     service->passwordlen = 0;\r
476   }\r
477   else {\r
478     /* Username. */\r
479     service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0);\r
480     if (! service->usernamelen) {\r
481       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_USERNAME);\r
482       return 6;\r
483     }\r
484     service->usernamelen++;\r
485 \r
486     service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));\r
487     if (! service->username) {\r
488       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
489       return 6;\r
490     }\r
491     if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username, (int) service->usernamelen)) {\r
492       HeapFree(GetProcessHeap(), 0, service->username);\r
493       service->username = 0;\r
494       service->usernamelen = 0;\r
495       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME);\r
496       return 6;\r
497     }\r
498 \r
499     /*\r
500       Special case for well-known accounts.\r
501       Ignore the password if we're editing and the username hasn't changed.\r
502     */\r
503     const TCHAR *well_known = well_known_username(service->username);\r
504     if (well_known) {\r
505       if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) {\r
506         HeapFree(GetProcessHeap(), 0, service->username);\r
507         service->username = 0;\r
508         service->usernamelen = 0;\r
509       }\r
510       else {\r
511         service->usernamelen = _tcslen(well_known) + 1;\r
512         service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));\r
513         if (! service->username) {\r
514           print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("install()"));\r
515           return 6;\r
516         }\r
517         memmove(service->username, well_known, service->usernamelen * sizeof(TCHAR));\r
518       }\r
519     }\r
520     else {\r
521       /* Password. */\r
522       service->passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), WM_GETTEXTLENGTH, 0, 0);\r
523       size_t passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), WM_GETTEXTLENGTH, 0, 0);\r
524 \r
525       if (! orig_service || ! orig_service->username || ! str_equiv(service->username, orig_service->username) || service->passwordlen || passwordlen) {\r
526         if (! service->passwordlen) {\r
527           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
528           return 6;\r
529         }\r
530         if (passwordlen != service->passwordlen) {\r
531           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
532           return 6;\r
533         }\r
534         service->passwordlen++;\r
535 \r
536         /* Temporary buffer for password validation. */\r
537         TCHAR *password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
538         if (! password) {\r
539           HeapFree(GetProcessHeap(), 0, service->username);\r
540           service->username = 0;\r
541           service->usernamelen = 0;\r
542           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password confirmation"), _T("install()"));\r
543           return 6;\r
544         }\r
545 \r
546         /* Actual password buffer. */\r
547         service->password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
548         if (! service->password) {\r
549           HeapFree(GetProcessHeap(), 0, password);\r
550           HeapFree(GetProcessHeap(), 0, service->username);\r
551           service->username = 0;\r
552           service->usernamelen = 0;\r
553           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password"), _T("install()"));\r
554           return 6;\r
555         }\r
556 \r
557         /* Get first password. */\r
558         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1, service->password, (int) service->passwordlen)) {\r
559           HeapFree(GetProcessHeap(), 0, password);\r
560           SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
561           HeapFree(GetProcessHeap(), 0, service->password);\r
562           service->password = 0;\r
563           service->passwordlen = 0;\r
564           HeapFree(GetProcessHeap(), 0, service->username);\r
565           service->username = 0;\r
566           service->usernamelen = 0;\r
567           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
568           return 6;\r
569         }\r
570 \r
571         /* Get confirmation. */\r
572         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2, password, (int) service->passwordlen)) {\r
573           SecureZeroMemory(password, service->passwordlen * sizeof(TCHAR));\r
574           HeapFree(GetProcessHeap(), 0, password);\r
575           SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
576           HeapFree(GetProcessHeap(), 0, service->password);\r
577           service->password = 0;\r
578           service->passwordlen = 0;\r
579           HeapFree(GetProcessHeap(), 0, service->username);\r
580           service->username = 0;\r
581           service->usernamelen = 0;\r
582           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
583           return 6;\r
584         }\r
585 \r
586         /* Compare. */\r
587         if (_tcsncmp(password, service->password, service->passwordlen)) {\r
588           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
589           SecureZeroMemory(password, service->passwordlen * sizeof(TCHAR));\r
590           HeapFree(GetProcessHeap(), 0, password);\r
591           SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
592           HeapFree(GetProcessHeap(), 0, service->password);\r
593           service->password = 0;\r
594           service->passwordlen = 0;\r
595           HeapFree(GetProcessHeap(), 0, service->username);\r
596           service->username = 0;\r
597           service->usernamelen = 0;\r
598           return 6;\r
599         }\r
600       }\r
601     }\r
602   }\r
603 \r
604   /* Get dependencies. */\r
605   unsigned long dependencieslen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES), WM_GETTEXTLENGTH, 0, 0);\r
606   if (dependencieslen) {\r
607     TCHAR *dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (dependencieslen + 2) * sizeof(TCHAR));\r
608     if (! dependencies) {\r
609       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()"));\r
610       cleanup_nssm_service(service);\r
611       return 6;\r
612     }\r
613 \r
614     if (! GetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, dependencies, dependencieslen + 1)) {\r
615       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DEPENDENCIES);\r
616       HeapFree(GetProcessHeap(), 0, dependencies);\r
617       cleanup_nssm_service(service);\r
618       return 6;\r
619     }\r
620 \r
621     if (unformat_double_null(dependencies, dependencieslen, &service->dependencies, &service->dependencieslen)) {\r
622       HeapFree(GetProcessHeap(), 0, dependencies);\r
623       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()"));\r
624       cleanup_nssm_service(service);\r
625       return 6;\r
626     }\r
627 \r
628     HeapFree(GetProcessHeap(), 0, dependencies);\r
629   }\r
630 \r
631   /* Remaining tabs are only for services we manage. */\r
632   if (service->native) return 0;\r
633 \r
634   /* Get process stuff. */\r
635   combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
636   service->priority = priority_index_to_constant((unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0));\r
637 \r
638   service->affinity = 0LL;\r
639   if (! (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_GETCHECK, 0, 0) & BST_CHECKED)) {\r
640     HWND list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
641     int selected = (int) SendMessage(list, LB_GETSELCOUNT, 0, 0);\r
642     int count = (int) SendMessage(list, LB_GETCOUNT, 0, 0);\r
643     if (! selected) {\r
644       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_WARN_AFFINITY_NONE);\r
645       return 5;\r
646     }\r
647     else if (selected < count) {\r
648       for (int i = 0; i < count; i++) {\r
649         if (SendMessage(list, LB_GETSEL, i, 0)) service->affinity |= (1LL << (__int64) i);\r
650       }\r
651     }\r
652   }\r
653 \r
654   if (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->no_console = 0;\r
655   else service->no_console = 1;\r
656 \r
657   /* Get stop method stuff. */\r
658   check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE);\r
659   check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW);\r
660   check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS);\r
661   check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE);\r
662   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay);\r
663   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay);\r
664   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay);\r
665   if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->kill_process_tree = 1;\r
666   else service->kill_process_tree = 0;\r
667 \r
668   /* Get exit action stuff. */\r
669   check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay);\r
670   combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
671   service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
672   if (service->default_exit_action == CB_ERR) service->default_exit_action = 0;\r
673   check_number(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, &service->restart_delay);\r
674 \r
675   /* Get I/O stuff. */\r
676   check_io(window, _T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN);\r
677   check_io(window, _T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT);\r
678   check_io(window, _T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR);\r
679 \r
680   /* Override stdout and/or stderr. */\r
681   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
682     if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS;\r
683     if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS;\r
684   }\r
685 \r
686   /* Get rotation stuff. */\r
687   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
688     service->rotate_files = true;\r
689     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;\r
690     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds);\r
691     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low);\r
692   }\r
693 \r
694   /* Get hook stuff. */\r
695   if (SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_GETCHECK, 0, 0) & BST_CHECKED) service->hook_share_output_handles = true;\r
696 \r
697   /* Get environment. */\r
698   unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);\r
699   if (envlen) {\r
700     TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR));\r
701     if (! env) {\r
702       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
703       cleanup_nssm_service(service);\r
704       return 5;\r
705     }\r
706 \r
707     if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {\r
708       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
709       HeapFree(GetProcessHeap(), 0, env);\r
710       cleanup_nssm_service(service);\r
711       return 5;\r
712     }\r
713 \r
714     TCHAR *newenv;\r
715     unsigned long newlen;\r
716     if (unformat_double_null(env, envlen, &newenv, &newlen)) {\r
717       HeapFree(GetProcessHeap(), 0, env);\r
718       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
719       cleanup_nssm_service(service);\r
720       return 5;\r
721     }\r
722 \r
723     HeapFree(GetProcessHeap(), 0, env);\r
724     env = newenv;\r
725     envlen = newlen;\r
726 \r
727     /* Test the environment is valid. */\r
728     if (test_environment(env)) {\r
729       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
730       HeapFree(GetProcessHeap(), 0, env);\r
731       cleanup_nssm_service(service);\r
732       return 5;\r
733     }\r
734 \r
735     if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
736       service->env = env;\r
737       service->envlen = envlen;\r
738     }\r
739     else {\r
740       service->env_extra = env;\r
741       service->env_extralen = envlen;\r
742     }\r
743   }\r
744 \r
745   return 0;\r
746 }\r
747 \r
748 /* Install the service. */\r
749 int install(HWND window) {\r
750   if (! window) return 1;\r
751 \r
752   nssm_service_t *service = alloc_nssm_service();\r
753   if (service) {\r
754     int ret = configure(window, service, 0);\r
755     if (ret) return ret;\r
756   }\r
757 \r
758   /* See if it works. */\r
759   switch (install_service(service)) {\r
760     case 1:\r
761       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("install()"));\r
762       cleanup_nssm_service(service);\r
763       return 1;\r
764 \r
765     case 2:\r
766       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
767       cleanup_nssm_service(service);\r
768       return 2;\r
769 \r
770     case 3:\r
771       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
772       cleanup_nssm_service(service);\r
773       return 3;\r
774 \r
775     case 4:\r
776       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
777       cleanup_nssm_service(service);\r
778       return 4;\r
779 \r
780     case 5:\r
781       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);\r
782       cleanup_nssm_service(service);\r
783       return 5;\r
784 \r
785     case 6:\r
786       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);\r
787       cleanup_nssm_service(service);\r
788       return 6;\r
789   }\r
790 \r
791   update_hooks(service->name);\r
792 \r
793   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
794   cleanup_nssm_service(service);\r
795   return 0;\r
796 }\r
797 \r
798 /* Remove the service */\r
799 int remove(HWND window) {\r
800   if (! window) return 1;\r
801 \r
802   /* See if it works */\r
803   nssm_service_t *service = alloc_nssm_service();\r
804   if (service) {\r
805     /* Get service name */\r
806     if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
807       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
808       cleanup_nssm_service(service);\r
809       return 2;\r
810     }\r
811 \r
812     /* Confirm */\r
813     if (popup_message(window, MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {\r
814       cleanup_nssm_service(service);\r
815       return 0;\r
816     }\r
817   }\r
818 \r
819   switch (remove_service(service)) {\r
820     case 1:\r
821       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("remove()"));\r
822       cleanup_nssm_service(service);\r
823       return 1;\r
824 \r
825     case 2:\r
826       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
827       cleanup_nssm_service(service);\r
828       return 2;\r
829 \r
830     case 3:\r
831       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
832       cleanup_nssm_service(service);\r
833       return 3;\r
834 \r
835     case 4:\r
836       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
837       cleanup_nssm_service(service);\r
838       return 4;\r
839   }\r
840 \r
841   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
842   cleanup_nssm_service(service);\r
843   return 0;\r
844 }\r
845 \r
846 int edit(HWND window, nssm_service_t *orig_service) {\r
847   if (! window) return 1;\r
848 \r
849   nssm_service_t *service = alloc_nssm_service();\r
850   if (service) {\r
851     int ret = configure(window, service, orig_service);\r
852     if (ret) return ret;\r
853   }\r
854 \r
855   switch (edit_service(service, true)) {\r
856     case 1:\r
857       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()"));\r
858       cleanup_nssm_service(service);\r
859       return 1;\r
860 \r
861     case 3:\r
862       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
863       cleanup_nssm_service(service);\r
864       return 3;\r
865 \r
866     case 4:\r
867       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
868       cleanup_nssm_service(service);\r
869       return 4;\r
870 \r
871     case 5:\r
872     case 6:\r
873       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED);\r
874       cleanup_nssm_service(service);\r
875       return 6;\r
876   }\r
877 \r
878   update_hooks(service->name);\r
879 \r
880   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name);\r
881   cleanup_nssm_service(service);\r
882   return 0;\r
883 }\r
884 \r
885 static TCHAR *browse_filter(int message) {\r
886   switch (message) {\r
887     case NSSM_GUI_BROWSE_FILTER_APPLICATIONS: return _T("*.exe;*.bat;*.cmd");\r
888     case NSSM_GUI_BROWSE_FILTER_DIRECTORIES: return _T(".");\r
889     case NSSM_GUI_BROWSE_FILTER_ALL_FILES: /* Fall through. */\r
890     default: return _T("*.*");\r
891   }\r
892 }\r
893 \r
894 UINT_PTR CALLBACK browse_hook(HWND dlg, UINT message, WPARAM w, LPARAM l) {\r
895   switch (message) {\r
896     case WM_INITDIALOG:\r
897       return 1;\r
898   }\r
899 \r
900   return 0;\r
901 }\r
902 \r
903 /* Browse for application */\r
904 void browse(HWND window, TCHAR *current, unsigned long flags, ...) {\r
905   if (! window) return;\r
906 \r
907   va_list arg;\r
908   size_t bufsize = 256;\r
909   size_t len = bufsize;\r
910   int i;\r
911 \r
912   OPENFILENAME ofn;\r
913   ZeroMemory(&ofn, sizeof(ofn));\r
914   ofn.lStructSize = sizeof(ofn);\r
915   ofn.lpstrFilter = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, bufsize * sizeof(TCHAR));\r
916   /* XXX: Escaping nulls with FormatMessage is tricky */\r
917   if (ofn.lpstrFilter) {\r
918     ZeroMemory((void *) ofn.lpstrFilter, bufsize);\r
919     len = 0;\r
920     /* "Applications" + NULL + "*.exe" + NULL */\r
921     va_start(arg, flags);\r
922     while (i = va_arg(arg, int)) {\r
923       TCHAR *localised = message_string(i);\r
924       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize, _TRUNCATE, localised);\r
925       len += _tcslen(localised) + 1;\r
926       LocalFree(localised);\r
927       TCHAR *filter = browse_filter(i);\r
928       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, _T("%s"), filter);\r
929       len += _tcslen(filter) + 1;\r
930     }\r
931     va_end(arg);\r
932     /* Remainder of the buffer is already zeroed */\r
933   }\r
934   ofn.lpstrFile = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH * sizeof(TCHAR));\r
935   if (ofn.lpstrFile) {\r
936     if (flags & OFN_NOVALIDATE) {\r
937       /* Directory hack. */\r
938       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
939       ofn.nMaxFile = DIR_LENGTH;\r
940     }\r
941     else {\r
942       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T("%s"), current);\r
943       ofn.nMaxFile = PATH_LENGTH;\r
944     }\r
945   }\r
946   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
947   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
948 \r
949   if (GetOpenFileName(&ofn)) {\r
950     /* Directory hack. */\r
951     if (flags & OFN_NOVALIDATE) strip_basename(ofn.lpstrFile);\r
952     SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile);\r
953   }\r
954   if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter);\r
955   if (ofn.lpstrFile) HeapFree(GetProcessHeap(), 0, ofn.lpstrFile);\r
956 }\r
957 \r
958 INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {\r
959   switch (message) {\r
960     case WM_INITDIALOG:\r
961       return 1;\r
962 \r
963     /* Button was pressed or control was controlled. */\r
964     case WM_COMMAND:\r
965       HWND dlg;\r
966       TCHAR buffer[PATH_LENGTH];\r
967       unsigned char enabled;\r
968 \r
969       switch (LOWORD(w)) {\r
970         /* Browse for application. */\r
971         case IDC_BROWSE:\r
972           dlg = GetDlgItem(tab, IDC_PATH);\r
973           GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
974           browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
975           /* Fill in startup directory if it wasn't already specified. */\r
976           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
977           if (! buffer[0]) {\r
978             GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
979             strip_basename(buffer);\r
980             SetDlgItemText(tab, IDC_DIR, buffer);\r
981           }\r
982           break;\r
983 \r
984         /* Browse for startup directory. */\r
985         case IDC_BROWSE_DIR:\r
986           dlg = GetDlgItem(tab, IDC_DIR);\r
987           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
988           browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0);\r
989           break;\r
990 \r
991         /* Log on. */\r
992         case IDC_LOCALSYSTEM:\r
993           set_logon_enabled(1, 0);\r
994           break;\r
995 \r
996         case IDC_VIRTUAL_SERVICE:\r
997           set_logon_enabled(0, 0);\r
998           break;\r
999 \r
1000         case IDC_ACCOUNT:\r
1001           set_logon_enabled(0, 1);\r
1002           break;\r
1003 \r
1004         /* Affinity. */\r
1005         case IDC_AFFINITY_ALL:\r
1006           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 0;\r
1007           else enabled = 1;\r
1008           set_affinity_enabled(enabled);\r
1009           break;\r
1010 \r
1011         /* Shutdown methods. */\r
1012         case IDC_METHOD_CONSOLE:\r
1013           set_timeout_enabled(LOWORD(w), IDC_KILL_CONSOLE);\r
1014           break;\r
1015 \r
1016         case IDC_METHOD_WINDOW:\r
1017           set_timeout_enabled(LOWORD(w), IDC_KILL_WINDOW);\r
1018           break;\r
1019 \r
1020         case IDC_METHOD_THREADS:\r
1021           set_timeout_enabled(LOWORD(w), IDC_KILL_THREADS);\r
1022           break;\r
1023 \r
1024         /* Browse for stdin. */\r
1025         case IDC_BROWSE_STDIN:\r
1026           dlg = GetDlgItem(tab, IDC_STDIN);\r
1027           GetDlgItemText(tab, IDC_STDIN, buffer, _countof(buffer));\r
1028           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1029           break;\r
1030 \r
1031         /* Browse for stdout. */\r
1032         case IDC_BROWSE_STDOUT:\r
1033           dlg = GetDlgItem(tab, IDC_STDOUT);\r
1034           GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
1035           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1036           /* Fill in stderr if it wasn't already specified. */\r
1037           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
1038           if (! buffer[0]) {\r
1039             GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
1040             SetDlgItemText(tab, IDC_STDERR, buffer);\r
1041           }\r
1042           break;\r
1043 \r
1044         /* Browse for stderr. */\r
1045         case IDC_BROWSE_STDERR:\r
1046           dlg = GetDlgItem(tab, IDC_STDERR);\r
1047           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
1048           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1049           break;\r
1050 \r
1051         /* Rotation. */\r
1052         case IDC_ROTATE:\r
1053           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
1054           else enabled = 0;\r
1055           set_rotation_enabled(enabled);\r
1056           break;\r
1057 \r
1058         /* Hook event. */\r
1059         case IDC_HOOK_EVENT:\r
1060           if (HIWORD(w) == CBN_SELCHANGE) set_hook_tab((int) SendMessage(GetDlgItem(tab, IDC_HOOK_EVENT), CB_GETCURSEL, 0, 0), 0, false);\r
1061           break;\r
1062 \r
1063         /* Hook action. */\r
1064         case IDC_HOOK_ACTION:\r
1065           if (HIWORD(w) == CBN_SELCHANGE) set_hook_tab((int) SendMessage(GetDlgItem(tab, IDC_HOOK_EVENT), CB_GETCURSEL, 0, 0), (int) SendMessage(GetDlgItem(tab, IDC_HOOK_ACTION), CB_GETCURSEL, 0, 0), false);\r
1066           break;\r
1067 \r
1068         /* Browse for hook. */\r
1069         case IDC_BROWSE_HOOK:\r
1070           dlg = GetDlgItem(tab, IDC_HOOK);\r
1071           GetDlgItemText(tab, IDC_HOOK, buffer, _countof(buffer));\r
1072           browse(dlg, _T(""), OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1073           break;\r
1074 \r
1075         /* Hook. */\r
1076         case IDC_HOOK:\r
1077           set_hook_tab((int) SendMessage(GetDlgItem(tab, IDC_HOOK_EVENT), CB_GETCURSEL, 0, 0), (int) SendMessage(GetDlgItem(tab, IDC_HOOK_ACTION), CB_GETCURSEL, 0, 0), true);\r
1078           break;\r
1079       }\r
1080       return 1;\r
1081   }\r
1082 \r
1083   return 0;\r
1084 }\r
1085 \r
1086 /* Install/remove dialogue callback */\r
1087 INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {\r
1088   nssm_service_t *service;\r
1089 \r
1090   switch (message) {\r
1091     /* Creating the dialogue */\r
1092     case WM_INITDIALOG:\r
1093       service = (nssm_service_t *) l;\r
1094 \r
1095       SetFocus(GetDlgItem(window, IDC_NAME));\r
1096 \r
1097       HWND tabs;\r
1098       HWND combo;\r
1099       HWND list;\r
1100       int i, n;\r
1101       tabs = GetDlgItem(window, IDC_TAB1);\r
1102       if (! tabs) return 0;\r
1103 \r
1104       /* Set up tabs. */\r
1105       TCITEM tab;\r
1106       ZeroMemory(&tab, sizeof(tab));\r
1107       tab.mask = TCIF_TEXT;\r
1108 \r
1109       selected_tab = 0;\r
1110 \r
1111       /* Application tab. */\r
1112       if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE);\r
1113       else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION);\r
1114       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1115       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab);\r
1116       if (service->native) {\r
1117         tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg);\r
1118         EnableWindow(tablist[NSSM_TAB_APPLICATION], 0);\r
1119         EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0);\r
1120       }\r
1121       else tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg);\r
1122       ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW);\r
1123 \r
1124       /* Details tab. */\r
1125       tab.pszText = message_string(NSSM_GUI_TAB_DETAILS);\r
1126       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1127       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DETAILS, (LPARAM) &tab);\r
1128       tablist[NSSM_TAB_DETAILS] = dialog(MAKEINTRESOURCE(IDD_DETAILS), window, tab_dlg);\r
1129       ShowWindow(tablist[NSSM_TAB_DETAILS], SW_HIDE);\r
1130 \r
1131       /* Set defaults. */\r
1132       combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
1133       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_AUTOMATIC, (LPARAM) message_string(NSSM_GUI_STARTUP_AUTOMATIC));\r
1134       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DELAYED, (LPARAM) message_string(NSSM_GUI_STARTUP_DELAYED));\r
1135       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_MANUAL, (LPARAM) message_string(NSSM_GUI_STARTUP_MANUAL));\r
1136       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DISABLED, (LPARAM) message_string(NSSM_GUI_STARTUP_DISABLED));\r
1137       SendMessage(combo, CB_SETCURSEL, NSSM_STARTUP_AUTOMATIC, 0);\r
1138 \r
1139       /* Logon tab. */\r
1140       tab.pszText = message_string(NSSM_GUI_TAB_LOGON);\r
1141       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1142       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_LOGON, (LPARAM) &tab);\r
1143       tablist[NSSM_TAB_LOGON] = dialog(MAKEINTRESOURCE(IDD_LOGON), window, tab_dlg);\r
1144       ShowWindow(tablist[NSSM_TAB_LOGON], SW_HIDE);\r
1145 \r
1146       /* Set defaults. */\r
1147       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
1148       set_logon_enabled(1, 0);\r
1149 \r
1150       /* Dependencies tab. */\r
1151       tab.pszText = message_string(NSSM_GUI_TAB_DEPENDENCIES);\r
1152       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1153       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DEPENDENCIES, (LPARAM) &tab);\r
1154       tablist[NSSM_TAB_DEPENDENCIES] = dialog(MAKEINTRESOURCE(IDD_DEPENDENCIES), window, tab_dlg);\r
1155       ShowWindow(tablist[NSSM_TAB_DEPENDENCIES], SW_HIDE);\r
1156 \r
1157       /* Remaining tabs are only for services we manage. */\r
1158       if (service->native) return 1;\r
1159 \r
1160       /* Process tab. */\r
1161       tab.pszText = message_string(NSSM_GUI_TAB_PROCESS);\r
1162       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1163       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab);\r
1164       tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg);\r
1165       ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE);\r
1166 \r
1167       /* Set defaults. */\r
1168       combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
1169       SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS));\r
1170       SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS));\r
1171       SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS));\r
1172       SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS));\r
1173       SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS));\r
1174       SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS));\r
1175       SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0);\r
1176 \r
1177       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
1178 \r
1179       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
1180       n = num_cpus();\r
1181       SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0);\r
1182       for (i = 0; i < n; i++) {\r
1183         TCHAR buffer[3];\r
1184         _sntprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("%d"), i);\r
1185         SendMessage(list, LB_ADDSTRING, 0, (LPARAM) buffer);\r
1186       }\r
1187 \r
1188       /*\r
1189         Size to fit.\r
1190         The box is high enough for four rows.  It is wide enough for eight\r
1191         columns without scrolling.  With scrollbars it shrinks to two rows.\r
1192         Note that the above only holds if we set the column width BEFORE\r
1193         adding the strings.\r
1194       */\r
1195       if (n < 32) {\r
1196         int columns = (n - 1) / 4;\r
1197         RECT rect;\r
1198         GetWindowRect(list, &rect);\r
1199         int width = rect.right - rect.left;\r
1200         width -= (7 - columns) * 16;\r
1201         int height = rect.bottom - rect.top;\r
1202         if (n < 4) height -= (int) SendMessage(list, LB_GETITEMHEIGHT, 0, 0) * (4 - n);\r
1203         SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\r
1204       }\r
1205       SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n));\r
1206 \r
1207       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_CHECKED, 0);\r
1208       set_affinity_enabled(0);\r
1209 \r
1210       /* Shutdown tab. */\r
1211       tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN);\r
1212       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1213       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab);\r
1214       tablist[NSSM_TAB_SHUTDOWN] = dialog(MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg);\r
1215       ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE);\r
1216 \r
1217       /* Set defaults. */\r
1218       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
1219       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, NSSM_KILL_CONSOLE_GRACE_PERIOD, 0);\r
1220       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_CHECKED, 0);\r
1221       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, NSSM_KILL_WINDOW_GRACE_PERIOD, 0);\r
1222       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0);\r
1223       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0);\r
1224       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0);\r
1225       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_SETCHECK, BST_CHECKED, 1);\r
1226 \r
1227       /* Restart tab. */\r
1228       tab.pszText = message_string(NSSM_GUI_TAB_EXIT);\r
1229       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1230       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab);\r
1231       tablist[NSSM_TAB_EXIT] = dialog(MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg);\r
1232       ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE);\r
1233 \r
1234       /* Set defaults. */\r
1235       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, NSSM_RESET_THROTTLE_RESTART, 0);\r
1236       combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
1237       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_RESTART, (LPARAM) message_string(NSSM_GUI_EXIT_RESTART));\r
1238       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_IGNORE, (LPARAM) message_string(NSSM_GUI_EXIT_IGNORE));\r
1239       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY));\r
1240       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN));\r
1241       SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0);\r
1242       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0);\r
1243 \r
1244       /* I/O tab. */\r
1245       tab.pszText = message_string(NSSM_GUI_TAB_IO);\r
1246       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1247       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab);\r
1248       tablist[NSSM_TAB_IO] = dialog(MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
1249       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);\r
1250 \r
1251       /* Rotation tab. */\r
1252       tab.pszText = message_string(NSSM_GUI_TAB_ROTATION);\r
1253       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1254       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ROTATION, (LPARAM) &tab);\r
1255       tablist[NSSM_TAB_ROTATION] = dialog(MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg);\r
1256       ShowWindow(tablist[NSSM_TAB_ROTATION], SW_HIDE);\r
1257 \r
1258       /* Set defaults. */\r
1259       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_UNCHECKED, 0);\r
1260       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, 0, 0);\r
1261       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, 0, 0);\r
1262       set_rotation_enabled(0);\r
1263 \r
1264       /* Environment tab. */\r
1265       tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);\r
1266       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1267       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);\r
1268       tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
1269       ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
1270 \r
1271       /* Hooks tab. */\r
1272       tab.pszText = message_string(NSSM_GUI_TAB_HOOKS);\r
1273       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1274       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_HOOKS, (LPARAM) &tab);\r
1275       tablist[NSSM_TAB_HOOKS] = dialog(MAKEINTRESOURCE(IDD_HOOKS), window, tab_dlg);\r
1276       ShowWindow(tablist[NSSM_TAB_HOOKS], SW_HIDE);\r
1277 \r
1278       /* Set defaults. */\r
1279       combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_EVENT);\r
1280       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_START));\r
1281       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_STOP));\r
1282       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_EXIT));\r
1283       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_POWER));\r
1284       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_ROTATE));\r
1285       SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_SETCHECK, BST_UNCHECKED, 0);\r
1286       if (_tcslen(service->name)) {\r
1287         TCHAR hook_name[HOOK_NAME_LENGTH];\r
1288         TCHAR cmd[CMD_LENGTH];\r
1289         for (i = 0; hook_event_strings[i]; i++) {\r
1290           const TCHAR *hook_event = hook_event_strings[i];\r
1291           int j;\r
1292           for (j = 0; hook_action_strings[j]; j++) {\r
1293             const TCHAR *hook_action = hook_action_strings[j];\r
1294             if (! valid_hook_name(hook_event, hook_action, true)) continue;\r
1295             if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) continue;\r
1296             if (hook_env(hook_event, hook_action, hook_name, _countof(hook_name)) < 0) continue;\r
1297             SetEnvironmentVariable(hook_name, cmd);\r
1298           }\r
1299         }\r
1300       }\r
1301       set_hook_tab(0, 0, false);\r
1302 \r
1303       return 1;\r
1304 \r
1305     /* Tab change. */\r
1306     case WM_NOTIFY:\r
1307       NMHDR *notification;\r
1308 \r
1309       notification = (NMHDR *) l;\r
1310       switch (notification->code) {\r
1311         case TCN_SELCHANGE:\r
1312           HWND tabs;\r
1313           int selection;\r
1314 \r
1315           tabs = GetDlgItem(window, IDC_TAB1);\r
1316           if (! tabs) return 0;\r
1317 \r
1318           selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0);\r
1319           if (selection != selected_tab) {\r
1320             ShowWindow(tablist[selected_tab], SW_HIDE);\r
1321             ShowWindow(tablist[selection], SW_SHOWDEFAULT);\r
1322             SetFocus(GetDlgItem(window, IDOK));\r
1323             selected_tab = selection;\r
1324           }\r
1325           return 1;\r
1326       }\r
1327 \r
1328       return 0;\r
1329 \r
1330     /* Button was pressed or control was controlled */\r
1331     case WM_COMMAND:\r
1332       switch (LOWORD(w)) {\r
1333         /* OK button */\r
1334         case IDOK:\r
1335           if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) {\r
1336             if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0);\r
1337           }\r
1338           else if (! install(window)) PostQuitMessage(0);\r
1339           break;\r
1340 \r
1341         /* Cancel button */\r
1342         case IDCANCEL:\r
1343           DestroyWindow(window);\r
1344           break;\r
1345 \r
1346         /* Remove button */\r
1347         case IDC_REMOVE:\r
1348           if (! remove(window)) PostQuitMessage(0);\r
1349           break;\r
1350       }\r
1351       return 1;\r
1352 \r
1353     /* Window closing */\r
1354     case WM_CLOSE:\r
1355       DestroyWindow(window);\r
1356       return 0;\r
1357     case WM_DESTROY:\r
1358       PostQuitMessage(0);\r
1359   }\r
1360   return 0;\r
1361 }\r