Support virtual service accounts.
[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->usernamelen = _tcslen(NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN) + _tcslen(service->name) + 2;\r
469     service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));\r
470     if (! service->username) {\r
471       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
472       return 6;\r
473     }\r
474     _sntprintf_s(service->username, service->usernamelen, _TRUNCATE, _T("%s\\%s"), NSSM_VIRTUAL_SERVICE_ACCOUNT_DOMAIN, service->name);\r
475     service->password = 0;\r
476     service->passwordlen = 0;\r
477   }\r
478   else {\r
479     /* Username. */\r
480     service->usernamelen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_USERNAME), WM_GETTEXTLENGTH, 0, 0);\r
481     if (! service->usernamelen) {\r
482       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_USERNAME);\r
483       return 6;\r
484     }\r
485     service->usernamelen++;\r
486 \r
487     service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));\r
488     if (! service->username) {\r
489       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("account name"), _T("install()"));\r
490       return 6;\r
491     }\r
492     if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_USERNAME, service->username, (int) service->usernamelen)) {\r
493       HeapFree(GetProcessHeap(), 0, service->username);\r
494       service->username = 0;\r
495       service->usernamelen = 0;\r
496       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_USERNAME);\r
497       return 6;\r
498     }\r
499 \r
500     /*\r
501       Special case for well-known accounts.\r
502       Ignore the password if we're editing and the username hasn't changed.\r
503     */\r
504     const TCHAR *well_known = well_known_username(service->username);\r
505     if (well_known) {\r
506       if (str_equiv(well_known, NSSM_LOCALSYSTEM_ACCOUNT)) {\r
507         HeapFree(GetProcessHeap(), 0, service->username);\r
508         service->username = 0;\r
509         service->usernamelen = 0;\r
510       }\r
511       else {\r
512         service->usernamelen = _tcslen(well_known) + 1;\r
513         service->username = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->usernamelen * sizeof(TCHAR));\r
514         if (! service->username) {\r
515           print_message(stderr, NSSM_MESSAGE_OUT_OF_MEMORY, _T("canon"), _T("install()"));\r
516           return 6;\r
517         }\r
518         memmove(service->username, well_known, service->usernamelen * sizeof(TCHAR));\r
519       }\r
520     }\r
521     else {\r
522       /* Password. */\r
523       service->passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1), WM_GETTEXTLENGTH, 0, 0);\r
524       size_t passwordlen = SendMessage(GetDlgItem(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2), WM_GETTEXTLENGTH, 0, 0);\r
525 \r
526       if (! orig_service || ! orig_service->username || ! str_equiv(service->username, orig_service->username) || service->passwordlen || passwordlen) {\r
527         if (! service->passwordlen) {\r
528           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
529           return 6;\r
530         }\r
531         if (passwordlen != service->passwordlen) {\r
532           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
533           return 6;\r
534         }\r
535         service->passwordlen++;\r
536 \r
537         /* Temporary buffer for password validation. */\r
538         TCHAR *password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
539         if (! password) {\r
540           HeapFree(GetProcessHeap(), 0, service->username);\r
541           service->username = 0;\r
542           service->usernamelen = 0;\r
543           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password confirmation"), _T("install()"));\r
544           return 6;\r
545         }\r
546 \r
547         /* Actual password buffer. */\r
548         service->password = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, service->passwordlen * sizeof(TCHAR));\r
549         if (! service->password) {\r
550           HeapFree(GetProcessHeap(), 0, password);\r
551           HeapFree(GetProcessHeap(), 0, service->username);\r
552           service->username = 0;\r
553           service->usernamelen = 0;\r
554           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("password"), _T("install()"));\r
555           return 6;\r
556         }\r
557 \r
558         /* Get first password. */\r
559         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD1, service->password, (int) service->passwordlen)) {\r
560           HeapFree(GetProcessHeap(), 0, password);\r
561           SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
562           HeapFree(GetProcessHeap(), 0, service->password);\r
563           service->password = 0;\r
564           service->passwordlen = 0;\r
565           HeapFree(GetProcessHeap(), 0, service->username);\r
566           service->username = 0;\r
567           service->usernamelen = 0;\r
568           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
569           return 6;\r
570         }\r
571 \r
572         /* Get confirmation. */\r
573         if (! GetDlgItemText(tablist[NSSM_TAB_LOGON], IDC_PASSWORD2, password, (int) service->passwordlen)) {\r
574           SecureZeroMemory(password, service->passwordlen * sizeof(TCHAR));\r
575           HeapFree(GetProcessHeap(), 0, password);\r
576           SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
577           HeapFree(GetProcessHeap(), 0, service->password);\r
578           service->password = 0;\r
579           service->passwordlen = 0;\r
580           HeapFree(GetProcessHeap(), 0, service->username);\r
581           service->username = 0;\r
582           service->usernamelen = 0;\r
583           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_PASSWORD);\r
584           return 6;\r
585         }\r
586 \r
587         /* Compare. */\r
588         if (_tcsncmp(password, service->password, service->passwordlen)) {\r
589           popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PASSWORD);\r
590           SecureZeroMemory(password, service->passwordlen * sizeof(TCHAR));\r
591           HeapFree(GetProcessHeap(), 0, password);\r
592           SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
593           HeapFree(GetProcessHeap(), 0, service->password);\r
594           service->password = 0;\r
595           service->passwordlen = 0;\r
596           HeapFree(GetProcessHeap(), 0, service->username);\r
597           service->username = 0;\r
598           service->usernamelen = 0;\r
599           return 6;\r
600         }\r
601       }\r
602     }\r
603   }\r
604 \r
605   /* Get dependencies. */\r
606   unsigned long dependencieslen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES), WM_GETTEXTLENGTH, 0, 0);\r
607   if (dependencieslen) {\r
608     TCHAR *dependencies = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (dependencieslen + 2) * sizeof(TCHAR));\r
609     if (! dependencies) {\r
610       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()"));\r
611       cleanup_nssm_service(service);\r
612       return 6;\r
613     }\r
614 \r
615     if (! GetDlgItemText(tablist[NSSM_TAB_DEPENDENCIES], IDC_DEPENDENCIES, dependencies, dependencieslen + 1)) {\r
616       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DEPENDENCIES);\r
617       HeapFree(GetProcessHeap(), 0, dependencies);\r
618       cleanup_nssm_service(service);\r
619       return 6;\r
620     }\r
621 \r
622     if (unformat_double_null(dependencies, dependencieslen, &service->dependencies, &service->dependencieslen)) {\r
623       HeapFree(GetProcessHeap(), 0, dependencies);\r
624       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("dependencies"), _T("install()"));\r
625       cleanup_nssm_service(service);\r
626       return 6;\r
627     }\r
628 \r
629     HeapFree(GetProcessHeap(), 0, dependencies);\r
630   }\r
631 \r
632   /* Remaining tabs are only for services we manage. */\r
633   if (service->native) return 0;\r
634 \r
635   /* Get process stuff. */\r
636   combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
637   service->priority = priority_index_to_constant((unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0));\r
638 \r
639   service->affinity = 0LL;\r
640   if (! (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_GETCHECK, 0, 0) & BST_CHECKED)) {\r
641     HWND list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
642     int selected = (int) SendMessage(list, LB_GETSELCOUNT, 0, 0);\r
643     int count = (int) SendMessage(list, LB_GETCOUNT, 0, 0);\r
644     if (! selected) {\r
645       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_WARN_AFFINITY_NONE);\r
646       return 5;\r
647     }\r
648     else if (selected < count) {\r
649       for (int i = 0; i < count; i++) {\r
650         if (SendMessage(list, LB_GETSEL, i, 0)) service->affinity |= (1LL << (__int64) i);\r
651       }\r
652     }\r
653   }\r
654 \r
655   if (SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->no_console = 0;\r
656   else service->no_console = 1;\r
657 \r
658   /* Get stop method stuff. */\r
659   check_stop_method(service, NSSM_STOP_METHOD_CONSOLE, IDC_METHOD_CONSOLE);\r
660   check_stop_method(service, NSSM_STOP_METHOD_WINDOW, IDC_METHOD_WINDOW);\r
661   check_stop_method(service, NSSM_STOP_METHOD_THREADS, IDC_METHOD_THREADS);\r
662   check_stop_method(service, NSSM_STOP_METHOD_TERMINATE, IDC_METHOD_TERMINATE);\r
663   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, &service->kill_console_delay);\r
664   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, &service->kill_window_delay);\r
665   check_number(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, &service->kill_threads_delay);\r
666   if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_GETCHECK, 0, 0) & BST_CHECKED) service->kill_process_tree = 1;\r
667   else service->kill_process_tree = 0;\r
668 \r
669   /* Get exit action stuff. */\r
670   check_number(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, &service->throttle_delay);\r
671   combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
672   service->default_exit_action = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
673   if (service->default_exit_action == CB_ERR) service->default_exit_action = 0;\r
674   check_number(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, &service->restart_delay);\r
675 \r
676   /* Get I/O stuff. */\r
677   check_io(window, _T("stdin"), service->stdin_path, _countof(service->stdin_path), IDC_STDIN);\r
678   check_io(window, _T("stdout"), service->stdout_path, _countof(service->stdout_path), IDC_STDOUT);\r
679   check_io(window, _T("stderr"), service->stderr_path, _countof(service->stderr_path), IDC_STDERR);\r
680 \r
681   /* Override stdout and/or stderr. */\r
682   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
683     if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS;\r
684     if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS;\r
685   }\r
686 \r
687   /* Get rotation stuff. */\r
688   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
689     service->rotate_files = true;\r
690     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
691     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds);\r
692     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low);\r
693   }\r
694 \r
695   /* Get hook stuff. */\r
696   if (SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_GETCHECK, 0, 0) & BST_CHECKED) service->hook_share_output_handles = true;\r
697 \r
698   /* Get environment. */\r
699   unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);\r
700   if (envlen) {\r
701     TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR));\r
702     if (! env) {\r
703       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
704       cleanup_nssm_service(service);\r
705       return 5;\r
706     }\r
707 \r
708     if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {\r
709       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
710       HeapFree(GetProcessHeap(), 0, env);\r
711       cleanup_nssm_service(service);\r
712       return 5;\r
713     }\r
714 \r
715     TCHAR *newenv;\r
716     unsigned long newlen;\r
717     if (unformat_double_null(env, envlen, &newenv, &newlen)) {\r
718       HeapFree(GetProcessHeap(), 0, env);\r
719       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
720       cleanup_nssm_service(service);\r
721       return 5;\r
722     }\r
723 \r
724     HeapFree(GetProcessHeap(), 0, env);\r
725     env = newenv;\r
726     envlen = newlen;\r
727 \r
728     /* Test the environment is valid. */\r
729     if (test_environment(env)) {\r
730       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
731       HeapFree(GetProcessHeap(), 0, env);\r
732       cleanup_nssm_service(service);\r
733       return 5;\r
734     }\r
735 \r
736     if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
737       service->env = env;\r
738       service->envlen = envlen;\r
739     }\r
740     else {\r
741       service->env_extra = env;\r
742       service->env_extralen = envlen;\r
743     }\r
744   }\r
745 \r
746   return 0;\r
747 }\r
748 \r
749 /* Install the service. */\r
750 int install(HWND window) {\r
751   if (! window) return 1;\r
752 \r
753   nssm_service_t *service = alloc_nssm_service();\r
754   if (service) {\r
755     int ret = configure(window, service, 0);\r
756     if (ret) return ret;\r
757   }\r
758 \r
759   /* See if it works. */\r
760   switch (install_service(service)) {\r
761     case 1:\r
762       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("install()"));\r
763       cleanup_nssm_service(service);\r
764       return 1;\r
765 \r
766     case 2:\r
767       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
768       cleanup_nssm_service(service);\r
769       return 2;\r
770 \r
771     case 3:\r
772       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
773       cleanup_nssm_service(service);\r
774       return 3;\r
775 \r
776     case 4:\r
777       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
778       cleanup_nssm_service(service);\r
779       return 4;\r
780 \r
781     case 5:\r
782       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);\r
783       cleanup_nssm_service(service);\r
784       return 5;\r
785 \r
786     case 6:\r
787       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);\r
788       cleanup_nssm_service(service);\r
789       return 6;\r
790   }\r
791 \r
792   update_hooks(service->name);\r
793 \r
794   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
795   cleanup_nssm_service(service);\r
796   return 0;\r
797 }\r
798 \r
799 /* Remove the service */\r
800 int remove(HWND window) {\r
801   if (! window) return 1;\r
802 \r
803   /* See if it works */\r
804   nssm_service_t *service = alloc_nssm_service();\r
805   if (service) {\r
806     /* Get service name */\r
807     if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
808       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
809       cleanup_nssm_service(service);\r
810       return 2;\r
811     }\r
812 \r
813     /* Confirm */\r
814     if (popup_message(window, MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {\r
815       cleanup_nssm_service(service);\r
816       return 0;\r
817     }\r
818   }\r
819 \r
820   switch (remove_service(service)) {\r
821     case 1:\r
822       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("remove()"));\r
823       cleanup_nssm_service(service);\r
824       return 1;\r
825 \r
826     case 2:\r
827       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
828       cleanup_nssm_service(service);\r
829       return 2;\r
830 \r
831     case 3:\r
832       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
833       cleanup_nssm_service(service);\r
834       return 3;\r
835 \r
836     case 4:\r
837       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
838       cleanup_nssm_service(service);\r
839       return 4;\r
840   }\r
841 \r
842   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
843   cleanup_nssm_service(service);\r
844   return 0;\r
845 }\r
846 \r
847 int edit(HWND window, nssm_service_t *orig_service) {\r
848   if (! window) return 1;\r
849 \r
850   nssm_service_t *service = alloc_nssm_service();\r
851   if (service) {\r
852     int ret = configure(window, service, orig_service);\r
853     if (ret) return ret;\r
854   }\r
855 \r
856   switch (edit_service(service, true)) {\r
857     case 1:\r
858       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()"));\r
859       cleanup_nssm_service(service);\r
860       return 1;\r
861 \r
862     case 3:\r
863       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
864       cleanup_nssm_service(service);\r
865       return 3;\r
866 \r
867     case 4:\r
868       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
869       cleanup_nssm_service(service);\r
870       return 4;\r
871 \r
872     case 5:\r
873     case 6:\r
874       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED);\r
875       cleanup_nssm_service(service);\r
876       return 6;\r
877   }\r
878 \r
879   update_hooks(service->name);\r
880 \r
881   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name);\r
882   cleanup_nssm_service(service);\r
883   return 0;\r
884 }\r
885 \r
886 static TCHAR *browse_filter(int message) {\r
887   switch (message) {\r
888     case NSSM_GUI_BROWSE_FILTER_APPLICATIONS: return _T("*.exe;*.bat;*.cmd");\r
889     case NSSM_GUI_BROWSE_FILTER_DIRECTORIES: return _T(".");\r
890     case NSSM_GUI_BROWSE_FILTER_ALL_FILES: /* Fall through. */\r
891     default: return _T("*.*");\r
892   }\r
893 }\r
894 \r
895 UINT_PTR CALLBACK browse_hook(HWND dlg, UINT message, WPARAM w, LPARAM l) {\r
896   switch (message) {\r
897     case WM_INITDIALOG:\r
898       return 1;\r
899   }\r
900 \r
901   return 0;\r
902 }\r
903 \r
904 /* Browse for application */\r
905 void browse(HWND window, TCHAR *current, unsigned long flags, ...) {\r
906   if (! window) return;\r
907 \r
908   va_list arg;\r
909   size_t bufsize = 256;\r
910   size_t len = bufsize;\r
911   int i;\r
912 \r
913   OPENFILENAME ofn;\r
914   ZeroMemory(&ofn, sizeof(ofn));\r
915   ofn.lStructSize = sizeof(ofn);\r
916   ofn.lpstrFilter = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, bufsize * sizeof(TCHAR));\r
917   /* XXX: Escaping nulls with FormatMessage is tricky */\r
918   if (ofn.lpstrFilter) {\r
919     ZeroMemory((void *) ofn.lpstrFilter, bufsize);\r
920     len = 0;\r
921     /* "Applications" + NULL + "*.exe" + NULL */\r
922     va_start(arg, flags);\r
923     while (i = va_arg(arg, int)) {\r
924       TCHAR *localised = message_string(i);\r
925       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize, _TRUNCATE, localised);\r
926       len += _tcslen(localised) + 1;\r
927       LocalFree(localised);\r
928       TCHAR *filter = browse_filter(i);\r
929       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, _T("%s"), filter);\r
930       len += _tcslen(filter) + 1;\r
931     }\r
932     va_end(arg);\r
933     /* Remainder of the buffer is already zeroed */\r
934   }\r
935   ofn.lpstrFile = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH * sizeof(TCHAR));\r
936   if (ofn.lpstrFile) {\r
937     if (flags & OFN_NOVALIDATE) {\r
938       /* Directory hack. */\r
939       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
940       ofn.nMaxFile = DIR_LENGTH;\r
941     }\r
942     else {\r
943       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T("%s"), current);\r
944       ofn.nMaxFile = PATH_LENGTH;\r
945     }\r
946   }\r
947   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
948   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
949 \r
950   if (GetOpenFileName(&ofn)) {\r
951     /* Directory hack. */\r
952     if (flags & OFN_NOVALIDATE) strip_basename(ofn.lpstrFile);\r
953     SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile);\r
954   }\r
955   if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter);\r
956   if (ofn.lpstrFile) HeapFree(GetProcessHeap(), 0, ofn.lpstrFile);\r
957 }\r
958 \r
959 INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {\r
960   switch (message) {\r
961     case WM_INITDIALOG:\r
962       return 1;\r
963 \r
964     /* Button was pressed or control was controlled. */\r
965     case WM_COMMAND:\r
966       HWND dlg;\r
967       TCHAR buffer[PATH_LENGTH];\r
968       unsigned char enabled;\r
969 \r
970       switch (LOWORD(w)) {\r
971         /* Browse for application. */\r
972         case IDC_BROWSE:\r
973           dlg = GetDlgItem(tab, IDC_PATH);\r
974           GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
975           browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
976           /* Fill in startup directory if it wasn't already specified. */\r
977           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
978           if (! buffer[0]) {\r
979             GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
980             strip_basename(buffer);\r
981             SetDlgItemText(tab, IDC_DIR, buffer);\r
982           }\r
983           break;\r
984 \r
985         /* Browse for startup directory. */\r
986         case IDC_BROWSE_DIR:\r
987           dlg = GetDlgItem(tab, IDC_DIR);\r
988           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
989           browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0);\r
990           break;\r
991 \r
992         /* Log on. */\r
993         case IDC_LOCALSYSTEM:\r
994           set_logon_enabled(1, 0);\r
995           break;\r
996 \r
997         case IDC_VIRTUAL_SERVICE:\r
998           set_logon_enabled(0, 0);\r
999           break;\r
1000 \r
1001         case IDC_ACCOUNT:\r
1002           set_logon_enabled(0, 1);\r
1003           break;\r
1004 \r
1005         /* Affinity. */\r
1006         case IDC_AFFINITY_ALL:\r
1007           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 0;\r
1008           else enabled = 1;\r
1009           set_affinity_enabled(enabled);\r
1010           break;\r
1011 \r
1012         /* Shutdown methods. */\r
1013         case IDC_METHOD_CONSOLE:\r
1014           set_timeout_enabled(LOWORD(w), IDC_KILL_CONSOLE);\r
1015           break;\r
1016 \r
1017         case IDC_METHOD_WINDOW:\r
1018           set_timeout_enabled(LOWORD(w), IDC_KILL_WINDOW);\r
1019           break;\r
1020 \r
1021         case IDC_METHOD_THREADS:\r
1022           set_timeout_enabled(LOWORD(w), IDC_KILL_THREADS);\r
1023           break;\r
1024 \r
1025         /* Browse for stdin. */\r
1026         case IDC_BROWSE_STDIN:\r
1027           dlg = GetDlgItem(tab, IDC_STDIN);\r
1028           GetDlgItemText(tab, IDC_STDIN, buffer, _countof(buffer));\r
1029           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1030           break;\r
1031 \r
1032         /* Browse for stdout. */\r
1033         case IDC_BROWSE_STDOUT:\r
1034           dlg = GetDlgItem(tab, IDC_STDOUT);\r
1035           GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
1036           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1037           /* Fill in stderr if it wasn't already specified. */\r
1038           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
1039           if (! buffer[0]) {\r
1040             GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
1041             SetDlgItemText(tab, IDC_STDERR, buffer);\r
1042           }\r
1043           break;\r
1044 \r
1045         /* Browse for stderr. */\r
1046         case IDC_BROWSE_STDERR:\r
1047           dlg = GetDlgItem(tab, IDC_STDERR);\r
1048           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
1049           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1050           break;\r
1051 \r
1052         /* Rotation. */\r
1053         case IDC_ROTATE:\r
1054           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
1055           else enabled = 0;\r
1056           set_rotation_enabled(enabled);\r
1057           break;\r
1058 \r
1059         /* Hook event. */\r
1060         case IDC_HOOK_EVENT:\r
1061           if (HIWORD(w) == CBN_SELCHANGE) set_hook_tab((int) SendMessage(GetDlgItem(tab, IDC_HOOK_EVENT), CB_GETCURSEL, 0, 0), 0, false);\r
1062           break;\r
1063 \r
1064         /* Hook action. */\r
1065         case IDC_HOOK_ACTION:\r
1066           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
1067           break;\r
1068 \r
1069         /* Browse for hook. */\r
1070         case IDC_BROWSE_HOOK:\r
1071           dlg = GetDlgItem(tab, IDC_HOOK);\r
1072           GetDlgItemText(tab, IDC_HOOK, buffer, _countof(buffer));\r
1073           browse(dlg, _T(""), OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1074           break;\r
1075 \r
1076         /* Hook. */\r
1077         case IDC_HOOK:\r
1078           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
1079           break;\r
1080       }\r
1081       return 1;\r
1082   }\r
1083 \r
1084   return 0;\r
1085 }\r
1086 \r
1087 /* Install/remove dialogue callback */\r
1088 INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {\r
1089   nssm_service_t *service;\r
1090 \r
1091   switch (message) {\r
1092     /* Creating the dialogue */\r
1093     case WM_INITDIALOG:\r
1094       service = (nssm_service_t *) l;\r
1095 \r
1096       SetFocus(GetDlgItem(window, IDC_NAME));\r
1097 \r
1098       HWND tabs;\r
1099       HWND combo;\r
1100       HWND list;\r
1101       int i, n;\r
1102       tabs = GetDlgItem(window, IDC_TAB1);\r
1103       if (! tabs) return 0;\r
1104 \r
1105       /* Set up tabs. */\r
1106       TCITEM tab;\r
1107       ZeroMemory(&tab, sizeof(tab));\r
1108       tab.mask = TCIF_TEXT;\r
1109 \r
1110       selected_tab = 0;\r
1111 \r
1112       /* Application tab. */\r
1113       if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE);\r
1114       else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION);\r
1115       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1116       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab);\r
1117       if (service->native) {\r
1118         tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg);\r
1119         EnableWindow(tablist[NSSM_TAB_APPLICATION], 0);\r
1120         EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0);\r
1121       }\r
1122       else tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg);\r
1123       ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW);\r
1124 \r
1125       /* Details tab. */\r
1126       tab.pszText = message_string(NSSM_GUI_TAB_DETAILS);\r
1127       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1128       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DETAILS, (LPARAM) &tab);\r
1129       tablist[NSSM_TAB_DETAILS] = dialog(MAKEINTRESOURCE(IDD_DETAILS), window, tab_dlg);\r
1130       ShowWindow(tablist[NSSM_TAB_DETAILS], SW_HIDE);\r
1131 \r
1132       /* Set defaults. */\r
1133       combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
1134       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_AUTOMATIC, (LPARAM) message_string(NSSM_GUI_STARTUP_AUTOMATIC));\r
1135       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DELAYED, (LPARAM) message_string(NSSM_GUI_STARTUP_DELAYED));\r
1136       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_MANUAL, (LPARAM) message_string(NSSM_GUI_STARTUP_MANUAL));\r
1137       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DISABLED, (LPARAM) message_string(NSSM_GUI_STARTUP_DISABLED));\r
1138       SendMessage(combo, CB_SETCURSEL, NSSM_STARTUP_AUTOMATIC, 0);\r
1139 \r
1140       /* Logon tab. */\r
1141       tab.pszText = message_string(NSSM_GUI_TAB_LOGON);\r
1142       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1143       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_LOGON, (LPARAM) &tab);\r
1144       tablist[NSSM_TAB_LOGON] = dialog(MAKEINTRESOURCE(IDD_LOGON), window, tab_dlg);\r
1145       ShowWindow(tablist[NSSM_TAB_LOGON], SW_HIDE);\r
1146 \r
1147       /* Set defaults. */\r
1148       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
1149       set_logon_enabled(1, 0);\r
1150 \r
1151       /* Dependencies tab. */\r
1152       tab.pszText = message_string(NSSM_GUI_TAB_DEPENDENCIES);\r
1153       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1154       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DEPENDENCIES, (LPARAM) &tab);\r
1155       tablist[NSSM_TAB_DEPENDENCIES] = dialog(MAKEINTRESOURCE(IDD_DEPENDENCIES), window, tab_dlg);\r
1156       ShowWindow(tablist[NSSM_TAB_DEPENDENCIES], SW_HIDE);\r
1157 \r
1158       /* Remaining tabs are only for services we manage. */\r
1159       if (service->native) return 1;\r
1160 \r
1161       /* Process tab. */\r
1162       tab.pszText = message_string(NSSM_GUI_TAB_PROCESS);\r
1163       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1164       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab);\r
1165       tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg);\r
1166       ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE);\r
1167 \r
1168       /* Set defaults. */\r
1169       combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
1170       SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS));\r
1171       SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS));\r
1172       SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS));\r
1173       SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS));\r
1174       SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS));\r
1175       SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS));\r
1176       SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0);\r
1177 \r
1178       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
1179 \r
1180       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
1181       n = num_cpus();\r
1182       SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0);\r
1183       for (i = 0; i < n; i++) {\r
1184         TCHAR buffer[3];\r
1185         _sntprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("%d"), i);\r
1186         SendMessage(list, LB_ADDSTRING, 0, (LPARAM) buffer);\r
1187       }\r
1188 \r
1189       /*\r
1190         Size to fit.\r
1191         The box is high enough for four rows.  It is wide enough for eight\r
1192         columns without scrolling.  With scrollbars it shrinks to two rows.\r
1193         Note that the above only holds if we set the column width BEFORE\r
1194         adding the strings.\r
1195       */\r
1196       if (n < 32) {\r
1197         int columns = (n - 1) / 4;\r
1198         RECT rect;\r
1199         GetWindowRect(list, &rect);\r
1200         int width = rect.right - rect.left;\r
1201         width -= (7 - columns) * 16;\r
1202         int height = rect.bottom - rect.top;\r
1203         if (n < 4) height -= (int) SendMessage(list, LB_GETITEMHEIGHT, 0, 0) * (4 - n);\r
1204         SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\r
1205       }\r
1206       SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n));\r
1207 \r
1208       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_CHECKED, 0);\r
1209       set_affinity_enabled(0);\r
1210 \r
1211       /* Shutdown tab. */\r
1212       tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN);\r
1213       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1214       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab);\r
1215       tablist[NSSM_TAB_SHUTDOWN] = dialog(MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg);\r
1216       ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE);\r
1217 \r
1218       /* Set defaults. */\r
1219       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
1220       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, NSSM_KILL_CONSOLE_GRACE_PERIOD, 0);\r
1221       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_CHECKED, 0);\r
1222       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, NSSM_KILL_WINDOW_GRACE_PERIOD, 0);\r
1223       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0);\r
1224       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0);\r
1225       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0);\r
1226       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_SETCHECK, BST_CHECKED, 1);\r
1227 \r
1228       /* Restart tab. */\r
1229       tab.pszText = message_string(NSSM_GUI_TAB_EXIT);\r
1230       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1231       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab);\r
1232       tablist[NSSM_TAB_EXIT] = dialog(MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg);\r
1233       ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE);\r
1234 \r
1235       /* Set defaults. */\r
1236       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, NSSM_RESET_THROTTLE_RESTART, 0);\r
1237       combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
1238       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_RESTART, (LPARAM) message_string(NSSM_GUI_EXIT_RESTART));\r
1239       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_IGNORE, (LPARAM) message_string(NSSM_GUI_EXIT_IGNORE));\r
1240       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY));\r
1241       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN));\r
1242       SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0);\r
1243       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0);\r
1244 \r
1245       /* I/O tab. */\r
1246       tab.pszText = message_string(NSSM_GUI_TAB_IO);\r
1247       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1248       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab);\r
1249       tablist[NSSM_TAB_IO] = dialog(MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
1250       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);\r
1251 \r
1252       /* Rotation tab. */\r
1253       tab.pszText = message_string(NSSM_GUI_TAB_ROTATION);\r
1254       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1255       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ROTATION, (LPARAM) &tab);\r
1256       tablist[NSSM_TAB_ROTATION] = dialog(MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg);\r
1257       ShowWindow(tablist[NSSM_TAB_ROTATION], SW_HIDE);\r
1258 \r
1259       /* Set defaults. */\r
1260       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_UNCHECKED, 0);\r
1261       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, 0, 0);\r
1262       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, 0, 0);\r
1263       set_rotation_enabled(0);\r
1264 \r
1265       /* Environment tab. */\r
1266       tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);\r
1267       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1268       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);\r
1269       tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
1270       ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
1271 \r
1272       /* Hooks tab. */\r
1273       tab.pszText = message_string(NSSM_GUI_TAB_HOOKS);\r
1274       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1275       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_HOOKS, (LPARAM) &tab);\r
1276       tablist[NSSM_TAB_HOOKS] = dialog(MAKEINTRESOURCE(IDD_HOOKS), window, tab_dlg);\r
1277       ShowWindow(tablist[NSSM_TAB_HOOKS], SW_HIDE);\r
1278 \r
1279       /* Set defaults. */\r
1280       combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_EVENT);\r
1281       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_START));\r
1282       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_STOP));\r
1283       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_EXIT));\r
1284       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_POWER));\r
1285       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_ROTATE));\r
1286       SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_SETCHECK, BST_UNCHECKED, 0);\r
1287       if (_tcslen(service->name)) {\r
1288         TCHAR hook_name[HOOK_NAME_LENGTH];\r
1289         TCHAR cmd[CMD_LENGTH];\r
1290         for (i = 0; hook_event_strings[i]; i++) {\r
1291           const TCHAR *hook_event = hook_event_strings[i];\r
1292           int j;\r
1293           for (j = 0; hook_action_strings[j]; j++) {\r
1294             const TCHAR *hook_action = hook_action_strings[j];\r
1295             if (! valid_hook_name(hook_event, hook_action, true)) continue;\r
1296             if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) continue;\r
1297             if (hook_env(hook_event, hook_action, hook_name, _countof(hook_name)) < 0) continue;\r
1298             SetEnvironmentVariable(hook_name, cmd);\r
1299           }\r
1300         }\r
1301       }\r
1302       set_hook_tab(0, 0, false);\r
1303 \r
1304       return 1;\r
1305 \r
1306     /* Tab change. */\r
1307     case WM_NOTIFY:\r
1308       NMHDR *notification;\r
1309 \r
1310       notification = (NMHDR *) l;\r
1311       switch (notification->code) {\r
1312         case TCN_SELCHANGE:\r
1313           HWND tabs;\r
1314           int selection;\r
1315 \r
1316           tabs = GetDlgItem(window, IDC_TAB1);\r
1317           if (! tabs) return 0;\r
1318 \r
1319           selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0);\r
1320           if (selection != selected_tab) {\r
1321             ShowWindow(tablist[selected_tab], SW_HIDE);\r
1322             ShowWindow(tablist[selection], SW_SHOWDEFAULT);\r
1323             SetFocus(GetDlgItem(window, IDOK));\r
1324             selected_tab = selection;\r
1325           }\r
1326           return 1;\r
1327       }\r
1328 \r
1329       return 0;\r
1330 \r
1331     /* Button was pressed or control was controlled */\r
1332     case WM_COMMAND:\r
1333       switch (LOWORD(w)) {\r
1334         /* OK button */\r
1335         case IDOK:\r
1336           if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) {\r
1337             if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0);\r
1338           }\r
1339           else if (! install(window)) PostQuitMessage(0);\r
1340           break;\r
1341 \r
1342         /* Cancel button */\r
1343         case IDCANCEL:\r
1344           DestroyWindow(window);\r
1345           break;\r
1346 \r
1347         /* Remove button */\r
1348         case IDC_REMOVE:\r
1349           if (! remove(window)) PostQuitMessage(0);\r
1350           break;\r
1351       }\r
1352       return 1;\r
1353 \r
1354     /* Window closing */\r
1355     case WM_CLOSE:\r
1356       DestroyWindow(window);\r
1357       return 0;\r
1358     case WM_DESTROY:\r
1359       PostQuitMessage(0);\r
1360   }\r
1361   return 0;\r
1362 }\r