2 * Implementation of svchost.exe
4 * Copyright 2007 Google (Roy Shea)
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 * Starting a service group:
24 * svchost /k service_group_name
33 #include "wine/debug.h"
35 WINE_DEFAULT_DEBUG_CHANNEL(svchost
);
37 /* Static strings used throughout svchost */
38 static const WCHAR kd
[] = {'-','k',0};
40 static const WCHAR ks
[] = {'/','k',0};
42 static const WCHAR reg_separator
[] = {'\\',0};
44 static const WCHAR service_reg_path
[] = {
45 'S','y','s','t','e','m',
46 '\\','C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t',
47 '\\','S','e','r','v','i','c','e','s',0};
49 static const WCHAR parameters
[] = {
50 'P','a','r','a','m','e','t','e','r','s',0};
52 static const WCHAR service_dll
[] = {
53 'S','e','r','v','i','c','e','D','l','l',0};
55 static const WCHAR svchost_path
[] = {
56 'S','o','f','t','w','a','r','e',
57 '\\','M','i','c','r','o','s','o','f','t',
58 '\\','W','i','n','d','o','w','s',' ','N','T',
59 '\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n',
60 '\\','S','v','c','h','o','s','t',0};
62 static const CHAR service_main
[] = "ServiceMain";
64 /* Allocate and initialize a WSTR containing the queried value */
65 static LPWSTR
GetRegValue(HKEY service_key
, const WCHAR
*value_name
)
75 ret
= RegQueryValueExW(service_key
, value_name
, NULL
, &type
, NULL
, ®_size
);
76 if (ret
!= ERROR_SUCCESS
)
81 /* Add space for potentially missing NULL terminators in initial alloc.
82 * The worst case REG_MULTI_SZ requires two NULL terminators. */
83 size
= reg_size
+ (2 * sizeof(WCHAR
));
84 value
= HeapAlloc(GetProcessHeap(), 0, size
);
86 ret
= RegQueryValueExW(service_key
, value_name
, NULL
, &type
,
87 (LPBYTE
)value
, ®_size
);
88 if (ret
!= ERROR_SUCCESS
)
90 HeapFree(GetProcessHeap(), 0, value
);
94 /* Explicitly NULL terminate the result */
95 value
[size
/ sizeof(WCHAR
) - 1] = '\0';
96 value
[size
/ sizeof(WCHAR
) - 2] = '\0';
101 /* Allocate and initialize a WSTR containing the expanded string */
102 static LPWSTR
ExpandEnv(LPWSTR string
)
105 LPWSTR expanded_string
;
110 size
= ExpandEnvironmentStringsW(string
, NULL
, size
);
113 WINE_ERR("cannot expand env vars in %s: %u\n",
114 wine_dbgstr_w(string
), GetLastError());
117 expanded_string
= HeapAlloc(GetProcessHeap(), 0,
118 (size
+ 1) * sizeof(WCHAR
));
119 if (ExpandEnvironmentStringsW(string
, expanded_string
, size
) == 0)
121 WINE_ERR("cannot expand env vars in %s: %u\n",
122 wine_dbgstr_w(string
), GetLastError());
123 HeapFree(GetProcessHeap(), 0, expanded_string
);
126 return expanded_string
;
129 /* Fill in service table entry for a specified service */
130 static BOOL
AddServiceElem(LPWSTR service_name
,
131 SERVICE_TABLE_ENTRYW
*service_table_entry
)
134 HKEY service_hkey
= NULL
;
135 LPWSTR service_param_key
= NULL
;
136 LPWSTR dll_name_short
= NULL
;
137 LPWSTR dll_name_long
= NULL
;
138 LPSTR dll_service_main
= NULL
;
139 HMODULE library
= NULL
;
140 LPSERVICE_MAIN_FUNCTIONW service_main_func
= NULL
;
141 BOOL success
= FALSE
;
145 WINE_TRACE("Adding element for %s\n", wine_dbgstr_w(service_name
));
147 /* Construct registry path to the service's parameters key */
148 size
= (lstrlenW(service_reg_path
) + lstrlenW(reg_separator
) +
149 lstrlenW(service_name
) + lstrlenW(reg_separator
) +
150 lstrlenW(parameters
) + 1);
151 service_param_key
= HeapAlloc(GetProcessHeap(), 0, size
* sizeof(WCHAR
));
152 lstrcpyW(service_param_key
, service_reg_path
);
153 lstrcatW(service_param_key
, reg_separator
);
154 lstrcatW(service_param_key
, service_name
);
155 lstrcatW(service_param_key
, reg_separator
);
156 lstrcatW(service_param_key
, parameters
);
157 service_param_key
[size
- 1] = '\0';
158 ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, service_param_key
, 0,
159 KEY_READ
, &service_hkey
);
160 if (ret
!= ERROR_SUCCESS
)
162 WINE_ERR("cannot open key %s, err=%d\n",
163 wine_dbgstr_w(service_param_key
), ret
);
167 /* Find DLL associate with service from key */
168 dll_name_short
= GetRegValue(service_hkey
, service_dll
);
171 WINE_ERR("cannot find registry value %s for service %s\n",
172 wine_dbgstr_w(service_dll
), wine_dbgstr_w(service_name
));
173 RegCloseKey(service_hkey
);
177 /* Expand environment variables in ServiceDll name*/
178 dll_name_long
= ExpandEnv(dll_name_short
);
181 WINE_ERR("failed to expand string %s\n",
182 wine_dbgstr_w(dll_name_short
));
183 RegCloseKey(service_hkey
);
187 /* Look for alternate to default ServiceMain entry point */
188 ret
= RegQueryValueExA(service_hkey
, service_main
, NULL
, NULL
, NULL
, ®_size
);
189 if (ret
== ERROR_SUCCESS
)
191 /* Add space for potentially missing NULL terminator, allocate, and
192 * fill with the registry value */
194 dll_service_main
= HeapAlloc(GetProcessHeap(), 0, size
);
195 ret
= RegQueryValueExA(service_hkey
, service_main
, NULL
, NULL
,
196 (LPBYTE
)dll_service_main
, ®_size
);
197 if (ret
!= ERROR_SUCCESS
)
199 RegCloseKey(service_hkey
);
202 dll_service_main
[size
- 1] = '\0';
204 RegCloseKey(service_hkey
);
206 /* Load the DLL and obtain a pointer to ServiceMain entry point */
207 library
= LoadLibraryExW(dll_name_long
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
);
210 WINE_ERR("failed to load library %s, err=%u\n",
211 wine_dbgstr_w(dll_name_long
), GetLastError());
214 if (dll_service_main
)
217 (LPSERVICE_MAIN_FUNCTIONW
) GetProcAddress(library
, dll_service_main
);
222 (LPSERVICE_MAIN_FUNCTIONW
) GetProcAddress(library
, service_main
);
224 if (!service_main_func
)
226 WINE_ERR("cannot locate ServiceMain procedure in DLL for %s\n",
227 wine_dbgstr_w(service_name
));
228 FreeLibrary(library
);
232 if (GetProcAddress(library
, "SvchostPushServiceGlobals"))
234 WINE_FIXME("library %s expects undocumented SvchostPushServiceGlobals function to be called\n",
235 wine_dbgstr_w(dll_name_long
));
238 /* Fill in the service table entry */
239 service_table_entry
->lpServiceName
= service_name
;
240 service_table_entry
->lpServiceProc
= service_main_func
;
244 HeapFree(GetProcessHeap(), 0, service_param_key
);
245 HeapFree(GetProcessHeap(), 0, dll_name_short
);
246 HeapFree(GetProcessHeap(), 0, dll_name_long
);
247 HeapFree(GetProcessHeap(), 0, dll_service_main
);
251 /* Initialize the service table for a list (REG_MULTI_SZ) of services */
252 static BOOL
StartGroupServices(LPWSTR services
)
254 LPWSTR service_name
= NULL
;
255 SERVICE_TABLE_ENTRYW
*service_table
= NULL
;
259 /* Count the services to load */
261 service_name
= services
;
262 while (*service_name
!= '\0')
265 service_name
= service_name
+ lstrlenW(service_name
);
268 WINE_TRACE("Service group contains %d services\n", service_count
);
270 /* Populate the service table */
271 service_table
= HeapAlloc(GetProcessHeap(), 0,
272 (service_count
+ 1) * sizeof(SERVICE_TABLE_ENTRYW
));
274 service_name
= services
;
275 while (*service_name
!= '\0')
277 if (!AddServiceElem(service_name
, &service_table
[service_count
]))
279 HeapFree(GetProcessHeap(), 0, service_table
);
283 service_name
= service_name
+ lstrlenW(service_name
);
286 service_table
[service_count
].lpServiceName
= NULL
;
287 service_table
[service_count
].lpServiceProc
= NULL
;
289 /* Start the services */
290 if (!(ret
= StartServiceCtrlDispatcherW(service_table
)))
291 WINE_ERR("StartServiceCtrlDispatcherW failed to start %s: %u\n",
292 wine_dbgstr_w(services
), GetLastError());
294 HeapFree(GetProcessHeap(), 0, service_table
);
298 /* Find the list of services associated with a group name and start those
300 static BOOL
LoadGroup(PWCHAR group_name
)
302 HKEY group_hkey
= NULL
;
303 LPWSTR services
= NULL
;
306 WINE_TRACE("Loading service group for %s\n", wine_dbgstr_w(group_name
));
308 /* Lookup group_name value of svchost registry entry */
309 ret
= RegOpenKeyExW(HKEY_LOCAL_MACHINE
, svchost_path
, 0,
310 KEY_READ
, &group_hkey
);
311 if (ret
!= ERROR_SUCCESS
)
313 WINE_ERR("cannot open key %s, err=%d\n",
314 wine_dbgstr_w(svchost_path
), ret
);
317 services
= GetRegValue(group_hkey
, group_name
);
318 RegCloseKey(group_hkey
);
321 WINE_ERR("cannot find registry value %s in %s\n",
322 wine_dbgstr_w(group_name
), wine_dbgstr_w(svchost_path
));
327 if (!(ret
= StartGroupServices(services
)))
328 WINE_TRACE("Failed to start service group\n");
330 HeapFree(GetProcessHeap(), 0, services
);
334 /* Load svchost group specified on the command line via the /k option */
335 int wmain(int argc
, WCHAR
*argv
[])
341 for (option_index
= 1; option_index
< argc
; option_index
++)
343 if (lstrcmpiW(argv
[option_index
], ks
) == 0 ||
344 lstrcmpiW(argv
[option_index
], kd
) == 0)
347 if (option_index
>= argc
)
349 WINE_ERR("Must specify group to initialize\n");
352 if (!LoadGroup(argv
[option_index
]))
354 WINE_ERR("Failed to load requested group: %s\n",
355 wine_dbgstr_w(argv
[option_index
]));
361 WINE_FIXME("Unrecognized option: %s\n",
362 wine_dbgstr_w(argv
[option_index
]));