Tidy up French GUI.
[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     if (service->timestamp_log) SendDlgItemMessage(tablist[NSSM_TAB_IO], IDC_TIMESTAMP, BM_SETCHECK, BST_CHECKED, 0);\r
184 \r
185     /* Rotation tab. */\r
186     if (service->stdout_disposition == CREATE_ALWAYS) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_SETCHECK, BST_CHECKED, 0);\r
187     if (service->rotate_files) {\r
188       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_SETCHECK, BST_CHECKED, 0);\r
189       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), 1);\r
190       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), 1);\r
191       EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), 1);\r
192     }\r
193     if (service->rotate_stdout_online || service->rotate_stderr_online) SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_CHECKED, 0);\r
194     SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, service->rotate_seconds, 0);\r
195     if (! service->rotate_bytes_high) SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, service->rotate_bytes_low, 0);\r
196 \r
197     /* Hooks tab. */\r
198     if (service->hook_share_output_handles) SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_SETCHECK, BST_CHECKED, 0);\r
199 \r
200     /* Check if advanced settings are in use. */\r
201     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
202     if (service->rotate_bytes_high) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ROTATE_BYTES);\r
203 \r
204     /* Environment tab. */\r
205     TCHAR *env;\r
206     unsigned long envlen;\r
207     if (service->env_extralen) {\r
208       env = service->env_extra;\r
209       envlen = service->env_extralen;\r
210     }\r
211     else {\r
212       env = service->env;\r
213       envlen = service->envlen;\r
214       if (envlen) SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_SETCHECK, BST_CHECKED, 0);\r
215     }\r
216 \r
217     if (envlen) {\r
218       TCHAR *formatted;\r
219       unsigned long newlen;\r
220       if (format_double_null(env, envlen, &formatted, &newlen)) {\r
221         popup_message(dlg, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("nssm_dlg()"));\r
222       }\r
223       else {\r
224         SetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, formatted);\r
225         HeapFree(GetProcessHeap(), 0, formatted);\r
226       }\r
227     }\r
228     if (service->envlen && service->env_extralen) popup_message(dlg, MB_OK | MB_ICONWARNING, NSSM_GUI_WARN_ENVIRONMENT);\r
229   }\r
230 \r
231   /* Go! */\r
232   MSG message;\r
233   while (GetMessage(&message, 0, 0, 0)) {\r
234     if (IsDialogMessage(dlg, &message)) continue;\r
235     TranslateMessage(&message);\r
236     DispatchMessage(&message);\r
237   }\r
238 \r
239   return (int) message.wParam;\r
240 }\r
241 \r
242 void centre_window(HWND window) {\r
243   HWND desktop;\r
244   RECT size, desktop_size;\r
245   unsigned long x, y;\r
246 \r
247   if (! window) return;\r
248 \r
249   /* Find window size */\r
250   if (! GetWindowRect(window, &size)) return;\r
251 \r
252   /* Find desktop window */\r
253   desktop = GetDesktopWindow();\r
254   if (! desktop) return;\r
255 \r
256   /* Find desktop window size */\r
257   if (! GetWindowRect(desktop, &desktop_size)) return;\r
258 \r
259   /* Centre window */\r
260   x = (desktop_size.right - size.right) / 2;\r
261   y = (desktop_size.bottom - size.bottom) / 2;\r
262   MoveWindow(window, x, y, size.right - size.left, size.bottom - size.top, 0);\r
263 }\r
264 \r
265 static inline void check_stop_method(nssm_service_t *service, unsigned long method, unsigned long control) {\r
266   if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) return;\r
267   service->stop_method &= ~method;\r
268 }\r
269 \r
270 static inline void check_number(HWND tab, unsigned long control, unsigned long *timeout) {\r
271   BOOL translated;\r
272   unsigned long configured = GetDlgItemInt(tab, control, &translated, 0);\r
273   if (translated) *timeout = configured;\r
274 }\r
275 \r
276 static inline void set_timeout_enabled(unsigned long control, unsigned long dependent) {\r
277   unsigned char enabled = 0;\r
278   if (SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], control, BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
279   EnableWindow(GetDlgItem(tablist[NSSM_TAB_SHUTDOWN], dependent), enabled);\r
280 }\r
281 \r
282 static inline void set_affinity_enabled(unsigned char enabled) {\r
283   EnableWindow(GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY), enabled);\r
284 }\r
285 \r
286 static inline void set_rotation_enabled(unsigned char enabled) {\r
287   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE), enabled);\r
288   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS), enabled);\r
289   EnableWindow(GetDlgItem(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW), enabled);\r
290 }\r
291 \r
292 static inline int hook_env(const TCHAR *hook_event, const TCHAR *hook_action, TCHAR *buffer, unsigned long buflen) {\r
293   return _sntprintf_s(buffer, buflen, _TRUNCATE, _T("NSSM_HOOK_%s_%s"), hook_event, hook_action);\r
294 }\r
295 \r
296 static inline void set_hook_tab(int event_index, int action_index, bool changed) {\r
297   int first_event = NSSM_GUI_HOOK_EVENT_START;\r
298   HWND combo;\r
299   combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_EVENT);\r
300   SendMessage(combo, CB_SETCURSEL, event_index, 0);\r
301   combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_ACTION);\r
302   SendMessage(combo, CB_RESETCONTENT, 0, 0);\r
303 \r
304   const TCHAR *hook_event = hook_event_strings[event_index];\r
305   TCHAR *hook_action;\r
306   int i;\r
307   switch (event_index + first_event) {\r
308     case NSSM_GUI_HOOK_EVENT_ROTATE:\r
309       i = 0;\r
310       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_ROTATE_PRE));\r
311       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
312       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_ROTATE_POST));\r
313       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
314       break;\r
315 \r
316     case NSSM_GUI_HOOK_EVENT_START:\r
317       i = 0;\r
318       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_START_PRE));\r
319       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
320       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_START_POST));\r
321       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
322       break;\r
323 \r
324     case NSSM_GUI_HOOK_EVENT_STOP:\r
325       i = 0;\r
326       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_STOP_PRE));\r
327       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_PRE;\r
328       break;\r
329 \r
330     case NSSM_GUI_HOOK_EVENT_EXIT:\r
331       i = 0;\r
332       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_EXIT_POST));\r
333       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_POST;\r
334       break;\r
335 \r
336     case NSSM_GUI_HOOK_EVENT_POWER:\r
337       i = 0;\r
338       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_POWER_CHANGE));\r
339       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_CHANGE;\r
340       SendMessage(combo, CB_INSERTSTRING, i, (LPARAM) message_string(NSSM_GUI_HOOK_ACTION_POWER_RESUME));\r
341       if (action_index == i++) hook_action = NSSM_HOOK_ACTION_RESUME;\r
342       break;\r
343   }\r
344 \r
345   SendMessage(combo, CB_SETCURSEL, action_index, 0);\r
346 \r
347   TCHAR hook_name[HOOK_NAME_LENGTH];\r
348   hook_env(hook_event, hook_action, hook_name, _countof(hook_name));\r
349 \r
350   if (! *hook_name) return;\r
351 \r
352   TCHAR cmd[CMD_LENGTH];\r
353   if (changed) {\r
354     GetDlgItemText(tablist[NSSM_TAB_HOOKS], IDC_HOOK, cmd, _countof(cmd));\r
355     SetEnvironmentVariable(hook_name, cmd);\r
356   }\r
357   else {\r
358     if (! GetEnvironmentVariable(hook_name, cmd, _countof(cmd))) cmd[0] = _T('\0');\r
359     SetDlgItemText(tablist[NSSM_TAB_HOOKS], IDC_HOOK, cmd);\r
360   }\r
361 }\r
362 \r
363 static inline int update_hook(TCHAR *service_name, const TCHAR *hook_event, const TCHAR *hook_action) {\r
364   TCHAR hook_name[HOOK_NAME_LENGTH];\r
365   if (hook_env(hook_event, hook_action, hook_name, _countof(hook_name)) < 0) return 1;\r
366   TCHAR cmd[CMD_LENGTH];\r
367   ZeroMemory(cmd, sizeof(cmd));\r
368   GetEnvironmentVariable(hook_name, cmd, _countof(cmd));\r
369   if (set_hook(service_name, hook_event, hook_action, cmd)) return 2;\r
370   return 0;\r
371 }\r
372 \r
373 static inline int update_hooks(TCHAR *service_name) {\r
374   int ret = 0;\r
375   ret += update_hook(service_name, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_PRE);\r
376   ret += update_hook(service_name, NSSM_HOOK_EVENT_START, NSSM_HOOK_ACTION_POST);\r
377   ret += update_hook(service_name, NSSM_HOOK_EVENT_STOP, NSSM_HOOK_ACTION_PRE);\r
378   ret += update_hook(service_name, NSSM_HOOK_EVENT_EXIT, NSSM_HOOK_ACTION_POST);\r
379   ret += update_hook(service_name, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_CHANGE);\r
380   ret += update_hook(service_name, NSSM_HOOK_EVENT_POWER, NSSM_HOOK_ACTION_RESUME);\r
381   ret += update_hook(service_name, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_PRE);\r
382   ret += update_hook(service_name, NSSM_HOOK_EVENT_ROTATE, NSSM_HOOK_ACTION_POST);\r
383   return ret;\r
384 }\r
385 \r
386 static inline void check_io(HWND owner, TCHAR *name, TCHAR *buffer, unsigned long len, unsigned long control) {\r
387   if (! SendMessage(GetDlgItem(tablist[NSSM_TAB_IO], control), WM_GETTEXTLENGTH, 0, 0)) return;\r
388   if (GetDlgItemText(tablist[NSSM_TAB_IO], control, buffer, (int) len)) return;\r
389   popup_message(owner, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, name);\r
390   ZeroMemory(buffer, len * sizeof(TCHAR));\r
391 }\r
392 \r
393 /* Set service parameters. */\r
394 int configure(HWND window, nssm_service_t *service, nssm_service_t *orig_service) {\r
395   if (! service) return 1;\r
396 \r
397   set_nssm_service_defaults(service);\r
398 \r
399   if (orig_service) {\r
400     service->native = orig_service->native;\r
401     service->handle = orig_service->handle;\r
402   }\r
403 \r
404   /* Get service name. */\r
405   if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
406     popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
407     cleanup_nssm_service(service);\r
408     return 2;\r
409   }\r
410 \r
411   /* Get executable name */\r
412   if (! service->native) {\r
413     if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_PATH, service->exe, _countof(service->exe))) {\r
414       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_PATH);\r
415       return 3;\r
416     }\r
417 \r
418     /* Get startup directory. */\r
419     if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_DIR, service->dir, _countof(service->dir))) {\r
420       _sntprintf_s(service->dir, _countof(service->dir), _TRUNCATE, _T("%s"), service->exe);\r
421       strip_basename(service->dir);\r
422     }\r
423 \r
424     /* Get flags. */\r
425     if (SendMessage(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS), WM_GETTEXTLENGTH, 0, 0)) {\r
426       if (! GetDlgItemText(tablist[NSSM_TAB_APPLICATION], IDC_FLAGS, service->flags, _countof(service->flags))) {\r
427         popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_OPTIONS);\r
428         return 4;\r
429       }\r
430     }\r
431   }\r
432 \r
433   /* Get details. */\r
434   if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME), WM_GETTEXTLENGTH, 0, 0)) {\r
435     if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DISPLAYNAME, service->displayname, _countof(service->displayname))) {\r
436       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DISPLAYNAME);\r
437       return 5;\r
438     }\r
439   }\r
440 \r
441   if (SendMessage(GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION), WM_GETTEXTLENGTH, 0, 0)) {\r
442     if (! GetDlgItemText(tablist[NSSM_TAB_DETAILS], IDC_DESCRIPTION, service->description, _countof(service->description))) {\r
443       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_DESCRIPTION);\r
444       return 5;\r
445     }\r
446   }\r
447 \r
448   HWND combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
449   service->startup = (unsigned long) SendMessage(combo, CB_GETCURSEL, 0, 0);\r
450   if (service->startup == CB_ERR) service->startup = 0;\r
451 \r
452   /* Get logon stuff. */\r
453   if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
454     if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_INTERACT, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
455       service->type |= SERVICE_INTERACTIVE_PROCESS;\r
456     }\r
457     if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
458     service->username = 0;\r
459     service->usernamelen = 0;\r
460     if (service->password) {\r
461       SecureZeroMemory(service->password, service->passwordlen * sizeof(TCHAR));\r
462       HeapFree(GetProcessHeap(), 0, service->password);\r
463     }\r
464     service->password = 0;\r
465     service->passwordlen = 0;\r
466   }\r
467   else if (SendDlgItemMessage(tablist[NSSM_TAB_LOGON], IDC_VIRTUAL_SERVICE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
468     if (service->username) HeapFree(GetProcessHeap(), 0, service->username);\r
469     service->username = virtual_account(service->name);\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     service->usernamelen = _tcslen(service->username) + 1;\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   if (SendDlgItemMessage(tablist[NSSM_TAB_IO], IDC_TIMESTAMP, BM_GETCHECK, 0, 0) & BST_CHECKED) service->timestamp_log = true;\r
681   else service->timestamp_log = false;\r
682 \r
683   /* Override stdout and/or stderr. */\r
684   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_TRUNCATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
685     if (service->stdout_path[0]) service->stdout_disposition = CREATE_ALWAYS;\r
686     if (service->stderr_path[0]) service->stderr_disposition = CREATE_ALWAYS;\r
687   }\r
688 \r
689   /* Get rotation stuff. */\r
690   if (SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
691     service->rotate_files = true;\r
692     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
693     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, &service->rotate_seconds);\r
694     check_number(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, &service->rotate_bytes_low);\r
695   }\r
696 \r
697   /* Get hook stuff. */\r
698   if (SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_GETCHECK, 0, 0) & BST_CHECKED) service->hook_share_output_handles = true;\r
699 \r
700   /* Get environment. */\r
701   unsigned long envlen = (unsigned long) SendMessage(GetDlgItem(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT), WM_GETTEXTLENGTH, 0, 0);\r
702   if (envlen) {\r
703     TCHAR *env = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, (envlen + 2) * sizeof(TCHAR));\r
704     if (! env) {\r
705       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
706       cleanup_nssm_service(service);\r
707       return 5;\r
708     }\r
709 \r
710     if (! GetDlgItemText(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT, env, envlen + 1)) {\r
711       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
712       HeapFree(GetProcessHeap(), 0, env);\r
713       cleanup_nssm_service(service);\r
714       return 5;\r
715     }\r
716 \r
717     TCHAR *newenv;\r
718     unsigned long newlen;\r
719     if (unformat_double_null(env, envlen, &newenv, &newlen)) {\r
720       HeapFree(GetProcessHeap(), 0, env);\r
721       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("environment"), _T("install()"));\r
722       cleanup_nssm_service(service);\r
723       return 5;\r
724     }\r
725 \r
726     HeapFree(GetProcessHeap(), 0, env);\r
727     env = newenv;\r
728     envlen = newlen;\r
729 \r
730     /* Test the environment is valid. */\r
731     if (test_environment(env)) {\r
732       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INVALID_ENVIRONMENT);\r
733       HeapFree(GetProcessHeap(), 0, env);\r
734       cleanup_nssm_service(service);\r
735       return 5;\r
736     }\r
737 \r
738     if (SendDlgItemMessage(tablist[NSSM_TAB_ENVIRONMENT], IDC_ENVIRONMENT_REPLACE, BM_GETCHECK, 0, 0) & BST_CHECKED) {\r
739       service->env = env;\r
740       service->envlen = envlen;\r
741     }\r
742     else {\r
743       service->env_extra = env;\r
744       service->env_extralen = envlen;\r
745     }\r
746   }\r
747 \r
748   return 0;\r
749 }\r
750 \r
751 /* Install the service. */\r
752 int install(HWND window) {\r
753   if (! window) return 1;\r
754 \r
755   nssm_service_t *service = alloc_nssm_service();\r
756   if (service) {\r
757     int ret = configure(window, service, 0);\r
758     if (ret) return ret;\r
759   }\r
760 \r
761   /* See if it works. */\r
762   switch (install_service(service)) {\r
763     case 1:\r
764       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("install()"));\r
765       cleanup_nssm_service(service);\r
766       return 1;\r
767 \r
768     case 2:\r
769       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
770       cleanup_nssm_service(service);\r
771       return 2;\r
772 \r
773     case 3:\r
774       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
775       cleanup_nssm_service(service);\r
776       return 3;\r
777 \r
778     case 4:\r
779       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
780       cleanup_nssm_service(service);\r
781       return 4;\r
782 \r
783     case 5:\r
784       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_INSTALL_SERVICE_FAILED);\r
785       cleanup_nssm_service(service);\r
786       return 5;\r
787 \r
788     case 6:\r
789       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_CREATE_PARAMETERS_FAILED);\r
790       cleanup_nssm_service(service);\r
791       return 6;\r
792   }\r
793 \r
794   update_hooks(service->name);\r
795 \r
796   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_INSTALLED, service->name);\r
797   cleanup_nssm_service(service);\r
798   return 0;\r
799 }\r
800 \r
801 /* Remove the service */\r
802 int remove(HWND window) {\r
803   if (! window) return 1;\r
804 \r
805   /* See if it works */\r
806   nssm_service_t *service = alloc_nssm_service();\r
807   if (service) {\r
808     /* Get service name */\r
809     if (! GetDlgItemText(window, IDC_NAME, service->name, _countof(service->name))) {\r
810       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_MISSING_SERVICE_NAME);\r
811       cleanup_nssm_service(service);\r
812       return 2;\r
813     }\r
814 \r
815     /* Confirm */\r
816     if (popup_message(window, MB_YESNO, NSSM_GUI_ASK_REMOVE_SERVICE, service->name) != IDYES) {\r
817       cleanup_nssm_service(service);\r
818       return 0;\r
819     }\r
820   }\r
821 \r
822   switch (remove_service(service)) {\r
823     case 1:\r
824       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("remove()"));\r
825       cleanup_nssm_service(service);\r
826       return 1;\r
827 \r
828     case 2:\r
829       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_OPEN_SERVICE_MANAGER_FAILED);\r
830       cleanup_nssm_service(service);\r
831       return 2;\r
832 \r
833     case 3:\r
834       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_SERVICE_NOT_INSTALLED);\r
835       cleanup_nssm_service(service);\r
836       return 3;\r
837 \r
838     case 4:\r
839       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_REMOVE_SERVICE_FAILED);\r
840       cleanup_nssm_service(service);\r
841       return 4;\r
842   }\r
843 \r
844   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_REMOVED, service->name);\r
845   cleanup_nssm_service(service);\r
846   return 0;\r
847 }\r
848 \r
849 int edit(HWND window, nssm_service_t *orig_service) {\r
850   if (! window) return 1;\r
851 \r
852   nssm_service_t *service = alloc_nssm_service();\r
853   if (service) {\r
854     int ret = configure(window, service, orig_service);\r
855     if (ret) return ret;\r
856   }\r
857 \r
858   switch (edit_service(service, true)) {\r
859     case 1:\r
860       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_EVENT_OUT_OF_MEMORY, _T("service"), _T("edit()"));\r
861       cleanup_nssm_service(service);\r
862       return 1;\r
863 \r
864     case 3:\r
865       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_MESSAGE_PATH_TOO_LONG, NSSM);\r
866       cleanup_nssm_service(service);\r
867       return 3;\r
868 \r
869     case 4:\r
870       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_OUT_OF_MEMORY_FOR_IMAGEPATH);\r
871       cleanup_nssm_service(service);\r
872       return 4;\r
873 \r
874     case 5:\r
875     case 6:\r
876       popup_message(window, MB_OK | MB_ICONEXCLAMATION, NSSM_GUI_EDIT_PARAMETERS_FAILED);\r
877       cleanup_nssm_service(service);\r
878       return 6;\r
879   }\r
880 \r
881   update_hooks(service->name);\r
882 \r
883   popup_message(window, MB_OK, NSSM_MESSAGE_SERVICE_EDITED, service->name);\r
884   cleanup_nssm_service(service);\r
885   return 0;\r
886 }\r
887 \r
888 static TCHAR *browse_filter(int message) {\r
889   switch (message) {\r
890     case NSSM_GUI_BROWSE_FILTER_APPLICATIONS: return _T("*.exe;*.bat;*.cmd");\r
891     case NSSM_GUI_BROWSE_FILTER_DIRECTORIES: return _T(".");\r
892     case NSSM_GUI_BROWSE_FILTER_ALL_FILES: /* Fall through. */\r
893     default: return _T("*.*");\r
894   }\r
895 }\r
896 \r
897 UINT_PTR CALLBACK browse_hook(HWND dlg, UINT message, WPARAM w, LPARAM l) {\r
898   switch (message) {\r
899     case WM_INITDIALOG:\r
900       return 1;\r
901   }\r
902 \r
903   return 0;\r
904 }\r
905 \r
906 /* Browse for application */\r
907 void browse(HWND window, TCHAR *current, unsigned long flags, ...) {\r
908   if (! window) return;\r
909 \r
910   va_list arg;\r
911   size_t bufsize = 256;\r
912   size_t len = bufsize;\r
913   int i;\r
914 \r
915   OPENFILENAME ofn;\r
916   ZeroMemory(&ofn, sizeof(ofn));\r
917   ofn.lStructSize = sizeof(ofn);\r
918   ofn.lpstrFilter = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, bufsize * sizeof(TCHAR));\r
919   /* XXX: Escaping nulls with FormatMessage is tricky */\r
920   if (ofn.lpstrFilter) {\r
921     ZeroMemory((void *) ofn.lpstrFilter, bufsize);\r
922     len = 0;\r
923     /* "Applications" + NULL + "*.exe" + NULL */\r
924     va_start(arg, flags);\r
925     while (i = va_arg(arg, int)) {\r
926       TCHAR *localised = message_string(i);\r
927       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize, _TRUNCATE, localised);\r
928       len += _tcslen(localised) + 1;\r
929       LocalFree(localised);\r
930       TCHAR *filter = browse_filter(i);\r
931       _sntprintf_s((TCHAR *) ofn.lpstrFilter + len, bufsize - len, _TRUNCATE, _T("%s"), filter);\r
932       len += _tcslen(filter) + 1;\r
933     }\r
934     va_end(arg);\r
935     /* Remainder of the buffer is already zeroed */\r
936   }\r
937   ofn.lpstrFile = (TCHAR *) HeapAlloc(GetProcessHeap(), 0, PATH_LENGTH * sizeof(TCHAR));\r
938   if (ofn.lpstrFile) {\r
939     if (flags & OFN_NOVALIDATE) {\r
940       /* Directory hack. */\r
941       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T(":%s:"), message_string(NSSM_GUI_BROWSE_FILTER_DIRECTORIES));\r
942       ofn.nMaxFile = DIR_LENGTH;\r
943     }\r
944     else {\r
945       _sntprintf_s(ofn.lpstrFile, PATH_LENGTH, _TRUNCATE, _T("%s"), current);\r
946       ofn.nMaxFile = PATH_LENGTH;\r
947     }\r
948   }\r
949   ofn.lpstrTitle = message_string(NSSM_GUI_BROWSE_TITLE);\r
950   ofn.Flags = OFN_EXPLORER | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | flags;\r
951 \r
952   if (GetOpenFileName(&ofn)) {\r
953     /* Directory hack. */\r
954     if (flags & OFN_NOVALIDATE) strip_basename(ofn.lpstrFile);\r
955     SendMessage(window, WM_SETTEXT, 0, (LPARAM) ofn.lpstrFile);\r
956   }\r
957   if (ofn.lpstrFilter) HeapFree(GetProcessHeap(), 0, (void *) ofn.lpstrFilter);\r
958   if (ofn.lpstrFile) HeapFree(GetProcessHeap(), 0, ofn.lpstrFile);\r
959 }\r
960 \r
961 INT_PTR CALLBACK tab_dlg(HWND tab, UINT message, WPARAM w, LPARAM l) {\r
962   switch (message) {\r
963     case WM_INITDIALOG:\r
964       return 1;\r
965 \r
966     /* Button was pressed or control was controlled. */\r
967     case WM_COMMAND:\r
968       HWND dlg;\r
969       TCHAR buffer[PATH_LENGTH];\r
970       unsigned char enabled;\r
971 \r
972       switch (LOWORD(w)) {\r
973         /* Browse for application. */\r
974         case IDC_BROWSE:\r
975           dlg = GetDlgItem(tab, IDC_PATH);\r
976           GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
977           browse(dlg, buffer, OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_APPLICATIONS, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
978           /* Fill in startup directory if it wasn't already specified. */\r
979           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
980           if (! buffer[0]) {\r
981             GetDlgItemText(tab, IDC_PATH, buffer, _countof(buffer));\r
982             strip_basename(buffer);\r
983             SetDlgItemText(tab, IDC_DIR, buffer);\r
984           }\r
985           break;\r
986 \r
987         /* Browse for startup directory. */\r
988         case IDC_BROWSE_DIR:\r
989           dlg = GetDlgItem(tab, IDC_DIR);\r
990           GetDlgItemText(tab, IDC_DIR, buffer, _countof(buffer));\r
991           browse(dlg, buffer, OFN_NOVALIDATE, NSSM_GUI_BROWSE_FILTER_DIRECTORIES, 0);\r
992           break;\r
993 \r
994         /* Log on. */\r
995         case IDC_LOCALSYSTEM:\r
996           set_logon_enabled(1, 0);\r
997           break;\r
998 \r
999         case IDC_VIRTUAL_SERVICE:\r
1000           set_logon_enabled(0, 0);\r
1001           break;\r
1002 \r
1003         case IDC_ACCOUNT:\r
1004           set_logon_enabled(0, 1);\r
1005           break;\r
1006 \r
1007         /* Affinity. */\r
1008         case IDC_AFFINITY_ALL:\r
1009           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 0;\r
1010           else enabled = 1;\r
1011           set_affinity_enabled(enabled);\r
1012           break;\r
1013 \r
1014         /* Shutdown methods. */\r
1015         case IDC_METHOD_CONSOLE:\r
1016           set_timeout_enabled(LOWORD(w), IDC_KILL_CONSOLE);\r
1017           break;\r
1018 \r
1019         case IDC_METHOD_WINDOW:\r
1020           set_timeout_enabled(LOWORD(w), IDC_KILL_WINDOW);\r
1021           break;\r
1022 \r
1023         case IDC_METHOD_THREADS:\r
1024           set_timeout_enabled(LOWORD(w), IDC_KILL_THREADS);\r
1025           break;\r
1026 \r
1027         /* Browse for stdin. */\r
1028         case IDC_BROWSE_STDIN:\r
1029           dlg = GetDlgItem(tab, IDC_STDIN);\r
1030           GetDlgItemText(tab, IDC_STDIN, buffer, _countof(buffer));\r
1031           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1032           break;\r
1033 \r
1034         /* Browse for stdout. */\r
1035         case IDC_BROWSE_STDOUT:\r
1036           dlg = GetDlgItem(tab, IDC_STDOUT);\r
1037           GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
1038           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1039           /* Fill in stderr if it wasn't already specified. */\r
1040           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
1041           if (! buffer[0]) {\r
1042             GetDlgItemText(tab, IDC_STDOUT, buffer, _countof(buffer));\r
1043             SetDlgItemText(tab, IDC_STDERR, buffer);\r
1044           }\r
1045           break;\r
1046 \r
1047         /* Browse for stderr. */\r
1048         case IDC_BROWSE_STDERR:\r
1049           dlg = GetDlgItem(tab, IDC_STDERR);\r
1050           GetDlgItemText(tab, IDC_STDERR, buffer, _countof(buffer));\r
1051           browse(dlg, buffer, 0, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1052           break;\r
1053 \r
1054         /* Rotation. */\r
1055         case IDC_ROTATE:\r
1056           if (SendDlgItemMessage(tab, LOWORD(w), BM_GETCHECK, 0, 0) & BST_CHECKED) enabled = 1;\r
1057           else enabled = 0;\r
1058           set_rotation_enabled(enabled);\r
1059           break;\r
1060 \r
1061         /* Hook event. */\r
1062         case IDC_HOOK_EVENT:\r
1063           if (HIWORD(w) == CBN_SELCHANGE) set_hook_tab((int) SendMessage(GetDlgItem(tab, IDC_HOOK_EVENT), CB_GETCURSEL, 0, 0), 0, false);\r
1064           break;\r
1065 \r
1066         /* Hook action. */\r
1067         case IDC_HOOK_ACTION:\r
1068           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
1069           break;\r
1070 \r
1071         /* Browse for hook. */\r
1072         case IDC_BROWSE_HOOK:\r
1073           dlg = GetDlgItem(tab, IDC_HOOK);\r
1074           GetDlgItemText(tab, IDC_HOOK, buffer, _countof(buffer));\r
1075           browse(dlg, _T(""), OFN_FILEMUSTEXIST, NSSM_GUI_BROWSE_FILTER_ALL_FILES, 0);\r
1076           break;\r
1077 \r
1078         /* Hook. */\r
1079         case IDC_HOOK:\r
1080           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
1081           break;\r
1082       }\r
1083       return 1;\r
1084   }\r
1085 \r
1086   return 0;\r
1087 }\r
1088 \r
1089 /* Install/remove dialogue callback */\r
1090 INT_PTR CALLBACK nssm_dlg(HWND window, UINT message, WPARAM w, LPARAM l) {\r
1091   nssm_service_t *service;\r
1092 \r
1093   switch (message) {\r
1094     /* Creating the dialogue */\r
1095     case WM_INITDIALOG:\r
1096       service = (nssm_service_t *) l;\r
1097 \r
1098       SetFocus(GetDlgItem(window, IDC_NAME));\r
1099 \r
1100       HWND tabs;\r
1101       HWND combo;\r
1102       HWND list;\r
1103       int i, n;\r
1104       tabs = GetDlgItem(window, IDC_TAB1);\r
1105       if (! tabs) return 0;\r
1106 \r
1107       /* Set up tabs. */\r
1108       TCITEM tab;\r
1109       ZeroMemory(&tab, sizeof(tab));\r
1110       tab.mask = TCIF_TEXT;\r
1111 \r
1112       selected_tab = 0;\r
1113 \r
1114       /* Application tab. */\r
1115       if (service->native) tab.pszText = message_string(NSSM_GUI_TAB_NATIVE);\r
1116       else tab.pszText = message_string(NSSM_GUI_TAB_APPLICATION);\r
1117       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1118       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_APPLICATION, (LPARAM) &tab);\r
1119       if (service->native) {\r
1120         tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_NATIVE), window, tab_dlg);\r
1121         EnableWindow(tablist[NSSM_TAB_APPLICATION], 0);\r
1122         EnableWindow(GetDlgItem(tablist[NSSM_TAB_APPLICATION], IDC_PATH), 0);\r
1123       }\r
1124       else tablist[NSSM_TAB_APPLICATION] = dialog(MAKEINTRESOURCE(IDD_APPLICATION), window, tab_dlg);\r
1125       ShowWindow(tablist[NSSM_TAB_APPLICATION], SW_SHOW);\r
1126 \r
1127       /* Details tab. */\r
1128       tab.pszText = message_string(NSSM_GUI_TAB_DETAILS);\r
1129       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1130       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DETAILS, (LPARAM) &tab);\r
1131       tablist[NSSM_TAB_DETAILS] = dialog(MAKEINTRESOURCE(IDD_DETAILS), window, tab_dlg);\r
1132       ShowWindow(tablist[NSSM_TAB_DETAILS], SW_HIDE);\r
1133 \r
1134       /* Set defaults. */\r
1135       combo = GetDlgItem(tablist[NSSM_TAB_DETAILS], IDC_STARTUP);\r
1136       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_AUTOMATIC, (LPARAM) message_string(NSSM_GUI_STARTUP_AUTOMATIC));\r
1137       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DELAYED, (LPARAM) message_string(NSSM_GUI_STARTUP_DELAYED));\r
1138       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_MANUAL, (LPARAM) message_string(NSSM_GUI_STARTUP_MANUAL));\r
1139       SendMessage(combo, CB_INSERTSTRING, NSSM_STARTUP_DISABLED, (LPARAM) message_string(NSSM_GUI_STARTUP_DISABLED));\r
1140       SendMessage(combo, CB_SETCURSEL, NSSM_STARTUP_AUTOMATIC, 0);\r
1141 \r
1142       /* Logon tab. */\r
1143       tab.pszText = message_string(NSSM_GUI_TAB_LOGON);\r
1144       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1145       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_LOGON, (LPARAM) &tab);\r
1146       tablist[NSSM_TAB_LOGON] = dialog(MAKEINTRESOURCE(IDD_LOGON), window, tab_dlg);\r
1147       ShowWindow(tablist[NSSM_TAB_LOGON], SW_HIDE);\r
1148 \r
1149       /* Set defaults. */\r
1150       CheckRadioButton(tablist[NSSM_TAB_LOGON], IDC_LOCALSYSTEM, IDC_ACCOUNT, IDC_LOCALSYSTEM);\r
1151       set_logon_enabled(1, 0);\r
1152 \r
1153       /* Dependencies tab. */\r
1154       tab.pszText = message_string(NSSM_GUI_TAB_DEPENDENCIES);\r
1155       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1156       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_DEPENDENCIES, (LPARAM) &tab);\r
1157       tablist[NSSM_TAB_DEPENDENCIES] = dialog(MAKEINTRESOURCE(IDD_DEPENDENCIES), window, tab_dlg);\r
1158       ShowWindow(tablist[NSSM_TAB_DEPENDENCIES], SW_HIDE);\r
1159 \r
1160       /* Remaining tabs are only for services we manage. */\r
1161       if (service->native) return 1;\r
1162 \r
1163       /* Process tab. */\r
1164       tab.pszText = message_string(NSSM_GUI_TAB_PROCESS);\r
1165       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1166       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_PROCESS, (LPARAM) &tab);\r
1167       tablist[NSSM_TAB_PROCESS] = dialog(MAKEINTRESOURCE(IDD_PROCESS), window, tab_dlg);\r
1168       ShowWindow(tablist[NSSM_TAB_PROCESS], SW_HIDE);\r
1169 \r
1170       /* Set defaults. */\r
1171       combo = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_PRIORITY);\r
1172       SendMessage(combo, CB_INSERTSTRING, NSSM_REALTIME_PRIORITY, (LPARAM) message_string(NSSM_GUI_REALTIME_PRIORITY_CLASS));\r
1173       SendMessage(combo, CB_INSERTSTRING, NSSM_HIGH_PRIORITY, (LPARAM) message_string(NSSM_GUI_HIGH_PRIORITY_CLASS));\r
1174       SendMessage(combo, CB_INSERTSTRING, NSSM_ABOVE_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_ABOVE_NORMAL_PRIORITY_CLASS));\r
1175       SendMessage(combo, CB_INSERTSTRING, NSSM_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_NORMAL_PRIORITY_CLASS));\r
1176       SendMessage(combo, CB_INSERTSTRING, NSSM_BELOW_NORMAL_PRIORITY, (LPARAM) message_string(NSSM_GUI_BELOW_NORMAL_PRIORITY_CLASS));\r
1177       SendMessage(combo, CB_INSERTSTRING, NSSM_IDLE_PRIORITY, (LPARAM) message_string(NSSM_GUI_IDLE_PRIORITY_CLASS));\r
1178       SendMessage(combo, CB_SETCURSEL, NSSM_NORMAL_PRIORITY, 0);\r
1179 \r
1180       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
1181 \r
1182       list = GetDlgItem(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY);\r
1183       n = num_cpus();\r
1184       SendMessage(list, LB_SETCOLUMNWIDTH, 16, 0);\r
1185       for (i = 0; i < n; i++) {\r
1186         TCHAR buffer[3];\r
1187         _sntprintf_s(buffer, _countof(buffer), _TRUNCATE, _T("%d"), i);\r
1188         SendMessage(list, LB_ADDSTRING, 0, (LPARAM) buffer);\r
1189       }\r
1190 \r
1191       /*\r
1192         Size to fit.\r
1193         The box is high enough for four rows.  It is wide enough for eight\r
1194         columns without scrolling.  With scrollbars it shrinks to two rows.\r
1195         Note that the above only holds if we set the column width BEFORE\r
1196         adding the strings.\r
1197       */\r
1198       if (n < 32) {\r
1199         int columns = (n - 1) / 4;\r
1200         RECT rect;\r
1201         GetWindowRect(list, &rect);\r
1202         int width = rect.right - rect.left;\r
1203         width -= (7 - columns) * 16;\r
1204         int height = rect.bottom - rect.top;\r
1205         if (n < 4) height -= (int) SendMessage(list, LB_GETITEMHEIGHT, 0, 0) * (4 - n);\r
1206         SetWindowPos(list, 0, 0, 0, width, height, SWP_NOMOVE | SWP_NOOWNERZORDER);\r
1207       }\r
1208       SendMessage(list, LB_SELITEMRANGE, 1, MAKELPARAM(0, n));\r
1209 \r
1210       SendDlgItemMessage(tablist[NSSM_TAB_PROCESS], IDC_AFFINITY_ALL, BM_SETCHECK, BST_CHECKED, 0);\r
1211       set_affinity_enabled(0);\r
1212 \r
1213       /* Shutdown tab. */\r
1214       tab.pszText = message_string(NSSM_GUI_TAB_SHUTDOWN);\r
1215       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1216       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_SHUTDOWN, (LPARAM) &tab);\r
1217       tablist[NSSM_TAB_SHUTDOWN] = dialog(MAKEINTRESOURCE(IDD_SHUTDOWN), window, tab_dlg);\r
1218       ShowWindow(tablist[NSSM_TAB_SHUTDOWN], SW_HIDE);\r
1219 \r
1220       /* Set defaults. */\r
1221       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_CONSOLE, BM_SETCHECK, BST_CHECKED, 0);\r
1222       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_CONSOLE, NSSM_KILL_CONSOLE_GRACE_PERIOD, 0);\r
1223       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_WINDOW, BM_SETCHECK, BST_CHECKED, 0);\r
1224       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_WINDOW, NSSM_KILL_WINDOW_GRACE_PERIOD, 0);\r
1225       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_THREADS, BM_SETCHECK, BST_CHECKED, 0);\r
1226       SetDlgItemInt(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_THREADS, NSSM_KILL_THREADS_GRACE_PERIOD, 0);\r
1227       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_METHOD_TERMINATE, BM_SETCHECK, BST_CHECKED, 0);\r
1228       SendDlgItemMessage(tablist[NSSM_TAB_SHUTDOWN], IDC_KILL_PROCESS_TREE, BM_SETCHECK, BST_CHECKED, 1);\r
1229 \r
1230       /* Restart tab. */\r
1231       tab.pszText = message_string(NSSM_GUI_TAB_EXIT);\r
1232       tab.cchTextMax = (int) _tcslen(tab.pszText);\r
1233       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_EXIT, (LPARAM) &tab);\r
1234       tablist[NSSM_TAB_EXIT] = dialog(MAKEINTRESOURCE(IDD_APPEXIT), window, tab_dlg);\r
1235       ShowWindow(tablist[NSSM_TAB_EXIT], SW_HIDE);\r
1236 \r
1237       /* Set defaults. */\r
1238       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_THROTTLE, NSSM_RESET_THROTTLE_RESTART, 0);\r
1239       combo = GetDlgItem(tablist[NSSM_TAB_EXIT], IDC_APPEXIT);\r
1240       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_RESTART, (LPARAM) message_string(NSSM_GUI_EXIT_RESTART));\r
1241       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_IGNORE, (LPARAM) message_string(NSSM_GUI_EXIT_IGNORE));\r
1242       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_REALLY, (LPARAM) message_string(NSSM_GUI_EXIT_REALLY));\r
1243       SendMessage(combo, CB_INSERTSTRING, NSSM_EXIT_UNCLEAN, (LPARAM) message_string(NSSM_GUI_EXIT_UNCLEAN));\r
1244       SendMessage(combo, CB_SETCURSEL, NSSM_EXIT_RESTART, 0);\r
1245       SetDlgItemInt(tablist[NSSM_TAB_EXIT], IDC_RESTART_DELAY, 0, 0);\r
1246 \r
1247       /* I/O tab. */\r
1248       tab.pszText = message_string(NSSM_GUI_TAB_IO);\r
1249       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1250       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_IO, (LPARAM) &tab);\r
1251       tablist[NSSM_TAB_IO] = dialog(MAKEINTRESOURCE(IDD_IO), window, tab_dlg);\r
1252       ShowWindow(tablist[NSSM_TAB_IO], SW_HIDE);\r
1253 \r
1254       /* Set defaults. */\r
1255       SendDlgItemMessage(tablist[NSSM_TAB_IO], IDC_TIMESTAMP, BM_SETCHECK, BST_UNCHECKED, 0);\r
1256 \r
1257       /* Rotation tab. */\r
1258       tab.pszText = message_string(NSSM_GUI_TAB_ROTATION);\r
1259       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1260       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ROTATION, (LPARAM) &tab);\r
1261       tablist[NSSM_TAB_ROTATION] = dialog(MAKEINTRESOURCE(IDD_ROTATION), window, tab_dlg);\r
1262       ShowWindow(tablist[NSSM_TAB_ROTATION], SW_HIDE);\r
1263 \r
1264       /* Set defaults. */\r
1265       SendDlgItemMessage(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_ONLINE, BM_SETCHECK, BST_UNCHECKED, 0);\r
1266       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_SECONDS, 0, 0);\r
1267       SetDlgItemInt(tablist[NSSM_TAB_ROTATION], IDC_ROTATE_BYTES_LOW, 0, 0);\r
1268       set_rotation_enabled(0);\r
1269 \r
1270       /* Environment tab. */\r
1271       tab.pszText = message_string(NSSM_GUI_TAB_ENVIRONMENT);\r
1272       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1273       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_ENVIRONMENT, (LPARAM) &tab);\r
1274       tablist[NSSM_TAB_ENVIRONMENT] = dialog(MAKEINTRESOURCE(IDD_ENVIRONMENT), window, tab_dlg);\r
1275       ShowWindow(tablist[NSSM_TAB_ENVIRONMENT], SW_HIDE);\r
1276 \r
1277       /* Hooks tab. */\r
1278       tab.pszText = message_string(NSSM_GUI_TAB_HOOKS);\r
1279       tab.cchTextMax = (int) _tcslen(tab.pszText) + 1;\r
1280       SendMessage(tabs, TCM_INSERTITEM, NSSM_TAB_HOOKS, (LPARAM) &tab);\r
1281       tablist[NSSM_TAB_HOOKS] = dialog(MAKEINTRESOURCE(IDD_HOOKS), window, tab_dlg);\r
1282       ShowWindow(tablist[NSSM_TAB_HOOKS], SW_HIDE);\r
1283 \r
1284       /* Set defaults. */\r
1285       combo = GetDlgItem(tablist[NSSM_TAB_HOOKS], IDC_HOOK_EVENT);\r
1286       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_START));\r
1287       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_STOP));\r
1288       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_EXIT));\r
1289       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_POWER));\r
1290       SendMessage(combo, CB_INSERTSTRING, -1, (LPARAM) message_string(NSSM_GUI_HOOK_EVENT_ROTATE));\r
1291       SendDlgItemMessage(tablist[NSSM_TAB_HOOKS], IDC_REDIRECT_HOOK, BM_SETCHECK, BST_UNCHECKED, 0);\r
1292       if (_tcslen(service->name)) {\r
1293         TCHAR hook_name[HOOK_NAME_LENGTH];\r
1294         TCHAR cmd[CMD_LENGTH];\r
1295         for (i = 0; hook_event_strings[i]; i++) {\r
1296           const TCHAR *hook_event = hook_event_strings[i];\r
1297           int j;\r
1298           for (j = 0; hook_action_strings[j]; j++) {\r
1299             const TCHAR *hook_action = hook_action_strings[j];\r
1300             if (! valid_hook_name(hook_event, hook_action, true)) continue;\r
1301             if (get_hook(service->name, hook_event, hook_action, cmd, sizeof(cmd))) continue;\r
1302             if (hook_env(hook_event, hook_action, hook_name, _countof(hook_name)) < 0) continue;\r
1303             SetEnvironmentVariable(hook_name, cmd);\r
1304           }\r
1305         }\r
1306       }\r
1307       set_hook_tab(0, 0, false);\r
1308 \r
1309       return 1;\r
1310 \r
1311     /* Tab change. */\r
1312     case WM_NOTIFY:\r
1313       NMHDR *notification;\r
1314 \r
1315       notification = (NMHDR *) l;\r
1316       switch (notification->code) {\r
1317         case TCN_SELCHANGE:\r
1318           HWND tabs;\r
1319           int selection;\r
1320 \r
1321           tabs = GetDlgItem(window, IDC_TAB1);\r
1322           if (! tabs) return 0;\r
1323 \r
1324           selection = (int) SendMessage(tabs, TCM_GETCURSEL, 0, 0);\r
1325           if (selection != selected_tab) {\r
1326             ShowWindow(tablist[selected_tab], SW_HIDE);\r
1327             ShowWindow(tablist[selection], SW_SHOWDEFAULT);\r
1328             SetFocus(GetDlgItem(window, IDOK));\r
1329             selected_tab = selection;\r
1330           }\r
1331           return 1;\r
1332       }\r
1333 \r
1334       return 0;\r
1335 \r
1336     /* Button was pressed or control was controlled */\r
1337     case WM_COMMAND:\r
1338       switch (LOWORD(w)) {\r
1339         /* OK button */\r
1340         case IDOK:\r
1341           if ((int) GetWindowLongPtr(window, GWLP_USERDATA) == IDD_EDIT) {\r
1342             if (! edit(window, (nssm_service_t *) GetWindowLongPtr(window, DWLP_USER))) PostQuitMessage(0);\r
1343           }\r
1344           else if (! install(window)) PostQuitMessage(0);\r
1345           break;\r
1346 \r
1347         /* Cancel button */\r
1348         case IDCANCEL:\r
1349           DestroyWindow(window);\r
1350           break;\r
1351 \r
1352         /* Remove button */\r
1353         case IDC_REMOVE:\r
1354           if (! remove(window)) PostQuitMessage(0);\r
1355           break;\r
1356       }\r
1357       return 1;\r
1358 \r
1359     /* Window closing */\r
1360     case WM_CLOSE:\r
1361       DestroyWindow(window);\r
1362       return 0;\r
1363     case WM_DESTROY:\r
1364       PostQuitMessage(0);\r
1365   }\r
1366   return 0;\r
1367 }\r