1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: GPLv2+ - See COPYING in the top level directory
4 * PURPOSE: Test for QueryServiceConfig2A/W
5 * PROGRAMMER: Hermès BÉLUSCA - MAÏTO
6 */
7
8 #include "precomp.h"
9
10 #define TESTING_SERVICEW L"Spooler"
11 #define TESTING_SERVICEA "Spooler"
12
13 /*
14 * Taken from base/system/services/config.c and adapted.
15 */
16 static DWORD
RegReadStringW(HKEY hKey,LPWSTR lpValueName,LPWSTR * lpValue)17 RegReadStringW(HKEY hKey,
18 LPWSTR lpValueName,
19 LPWSTR *lpValue)
20 {
21 DWORD dwError;
22 DWORD dwSize;
23 DWORD dwType;
24
25 *lpValue = NULL;
26
27 dwSize = 0;
28 dwError = RegQueryValueExW(hKey,
29 lpValueName,
30 0,
31 &dwType,
32 NULL,
33 &dwSize);
34 if (dwError != ERROR_SUCCESS)
35 return dwError;
36
37 *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
38 if (*lpValue == NULL)
39 return ERROR_NOT_ENOUGH_MEMORY;
40
41 dwError = RegQueryValueExW(hKey,
42 lpValueName,
43 0,
44 &dwType,
45 (LPBYTE)*lpValue,
46 &dwSize);
47 if (dwError != ERROR_SUCCESS)
48 {
49 HeapFree(GetProcessHeap(), 0, *lpValue);
50 *lpValue = NULL;
51 }
52
53 return dwError;
54 }
55
56 static DWORD
RegReadStringA(HKEY hKey,LPSTR lpValueName,LPSTR * lpValue)57 RegReadStringA(HKEY hKey,
58 LPSTR lpValueName,
59 LPSTR *lpValue)
60 {
61 DWORD dwError;
62 DWORD dwSize;
63 DWORD dwType;
64
65 *lpValue = NULL;
66
67 dwSize = 0;
68 dwError = RegQueryValueExA(hKey,
69 lpValueName,
70 0,
71 &dwType,
72 NULL,
73 &dwSize);
74 if (dwError != ERROR_SUCCESS)
75 return dwError;
76
77 *lpValue = HeapAlloc(GetProcessHeap(), 0, dwSize);
78 if (*lpValue == NULL)
79 return ERROR_NOT_ENOUGH_MEMORY;
80
81 dwError = RegQueryValueExA(hKey,
82 lpValueName,
83 0,
84 &dwType,
85 (LPBYTE)*lpValue,
86 &dwSize);
87 if (dwError != ERROR_SUCCESS)
88 {
89 HeapFree(GetProcessHeap(), 0, *lpValue);
90 *lpValue = NULL;
91 }
92
93 return dwError;
94 }
95
96
QueryConfig2W(SC_HANDLE hService,LPCWSTR serviceName,DWORD dwInfoLevel)97 static int QueryConfig2W(SC_HANDLE hService, LPCWSTR serviceName, DWORD dwInfoLevel)
98 {
99 int iRet = 0;
100 LONG lRet = 0;
101 DWORD dwRet = 0;
102 BOOL bError = FALSE;
103 DWORD dwRequiredSize = 0;
104 LPBYTE lpBuffer = NULL;
105
106 WCHAR keyName[256];
107 HKEY hKey = NULL;
108 DWORD dwType = 0;
109
110 /* Get the needed size */
111 SetLastError(0xdeadbeef);
112 bError = QueryServiceConfig2W(hService,
113 dwInfoLevel,
114 NULL,
115 0,
116 &dwRequiredSize);
117 ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
118 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
119 if (dwRequiredSize == 0)
120 {
121 skip("Required size is null; cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel);
122 return 1;
123 }
124
125 /* Allocate memory */
126 lpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
127 if (lpBuffer == NULL)
128 {
129 skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
130 return 2;
131 }
132
133 /* Get the actual value */
134 SetLastError(0xdeadbeef);
135 bError = QueryServiceConfig2W(hService,
136 dwInfoLevel,
137 lpBuffer,
138 dwRequiredSize,
139 &dwRequiredSize);
140 ok(bError, "bError = %u, expected TRUE\n", bError);
141 if (bError == FALSE)
142 {
143 skip("QueryServiceConfig2W returned an error; cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel);
144 HeapFree(GetProcessHeap(), 0, lpBuffer);
145 return 3;
146 }
147
148 /* Now we can compare the retrieved value with what it's actually stored in the registry */
149 StringCbPrintfW(keyName, sizeof(keyName), L"System\\CurrentControlSet\\Services\\%s", serviceName);
150 SetLastError(0xdeadbeef);
151 lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &hKey);
152 ok(lRet == ERROR_SUCCESS, "RegOpenKeyExW failed with 0x%08lx\n", lRet);
153 if (lRet != ERROR_SUCCESS)
154 {
155 skip("No regkey; cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel);
156 HeapFree(GetProcessHeap(), 0, lpBuffer);
157 return 4;
158 }
159
160 switch (dwInfoLevel)
161 {
162 case SERVICE_CONFIG_DESCRIPTION:
163 {
164 LPSERVICE_DESCRIPTIONW lpDescription = (LPSERVICE_DESCRIPTIONW)lpBuffer;
165 LPWSTR lpszDescription = NULL;
166
167 /* Retrieve the description via the registry */
168 dwRet = RegReadStringW(hKey, L"Description", &lpszDescription);
169 ok(dwRet == ERROR_SUCCESS, "RegReadStringW returned 0x%08lx\n", dwRet);
170 ok(lpszDescription != NULL, "lpszDescription is null, expected non-null\n");
171
172 /* Compare it with the description retrieved via QueryServiceConfig2 */
173 if (lpszDescription)
174 iRet = wcscmp(lpDescription->lpDescription, lpszDescription);
175 else
176 iRet = 0;
177
178 ok(iRet == 0, "Retrieved descriptions are different !\n");
179
180
181 /* Memory cleanup */
182 HeapFree(GetProcessHeap(), 0, lpszDescription);
183
184 break;
185 }
186
187 case SERVICE_CONFIG_FAILURE_ACTIONS:
188 {
189 LPSERVICE_FAILURE_ACTIONSW lpFailureActions1 = (LPSERVICE_FAILURE_ACTIONSW)lpBuffer;
190 LPSERVICE_FAILURE_ACTIONSW lpFailureActions2 = NULL;
191 LPWSTR lpRebootMessage = NULL;
192 LPWSTR lpFailureCommand = NULL;
193 DWORD i = 0;
194
195 /* Retrieve the failure actions via the registry */
196 lRet = RegQueryValueExW(hKey,
197 L"FailureActions",
198 NULL,
199 &dwType,
200 NULL,
201 &dwRequiredSize);
202 ok(lRet == ERROR_SUCCESS, "RegQueryValueExW returned 0x%08lx\n", lRet);
203 ok(dwType == REG_BINARY, "dwType = %lu, expected REG_BINARY\n", dwType);
204 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
205
206 lpFailureActions2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
207 if (lpFailureActions2 == NULL)
208 {
209 skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
210 break;
211 }
212
213 lRet = RegQueryValueExW(hKey,
214 L"FailureActions",
215 NULL,
216 NULL,
217 (LPBYTE)lpFailureActions2,
218 &dwRequiredSize);
219 ok(lRet == ERROR_SUCCESS, "RegQueryValueExW returned 0x%08lx\n", lRet);
220 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
221
222 /* Get the strings */
223 RegReadStringW(hKey, L"FailureCommand", &lpFailureCommand);
224 RegReadStringW(hKey, L"RebootMessage" , &lpRebootMessage );
225
226 /* Check the values */
227 ok(lpFailureActions1->dwResetPeriod == lpFailureActions2->dwResetPeriod, "lpFailureActions1->dwResetPeriod != lpFailureActions2->dwResetPeriod\n");
228 #ifndef _M_AMD64 // Fails on Win 2003 x64
229 ok(lpFailureActions1->cActions == lpFailureActions2->cActions, "lpFailureActions1->cActions != lpFailureActions2->cActions\n");
230 #endif
231 /* Compare the actions */
232 if (lpFailureActions1->cActions == lpFailureActions2->cActions)
233 {
234 lpFailureActions2->lpsaActions = (lpFailureActions2->cActions > 0 ? (LPSC_ACTION)(lpFailureActions2 + 1) : NULL);
235
236 if (lpFailureActions1->cActions > 0 &&
237 lpFailureActions1->lpsaActions != NULL)
238 {
239 for (i = 0; i < lpFailureActions1->cActions; ++i)
240 {
241 ok(lpFailureActions1->lpsaActions[i].Type == lpFailureActions2->lpsaActions[i].Type , "lpFailureActions1->lpsaActions[%lu].Type != lpFailureActions2->lpsaActions[%lu].Type\n" , i, i);
242 ok(lpFailureActions1->lpsaActions[i].Delay == lpFailureActions2->lpsaActions[i].Delay, "lpFailureActions1->lpsaActions[%lu].Delay != lpFailureActions2->lpsaActions[%lu].Delay\n", i, i);
243 }
244 }
245 }
246
247 /* TODO: retrieve the strings if they are in MUI format */
248
249 /* Compare RebootMsg */
250 if (lpFailureActions1->lpRebootMsg && lpRebootMessage)
251 iRet = wcscmp(lpFailureActions1->lpRebootMsg, lpRebootMessage);
252 else
253 iRet = 0;
254
255 ok(iRet == 0, "Retrieved reboot messages are different !\n");
256
257 /* Compare Command */
258 if (lpFailureActions1->lpCommand && lpFailureCommand)
259 iRet = wcscmp(lpFailureActions1->lpCommand, lpFailureCommand);
260 else
261 iRet = 0;
262
263 ok(iRet == 0, "Retrieved commands are different !\n");
264
265
266 /* Memory cleanup */
267 if (lpRebootMessage)
268 HeapFree(GetProcessHeap(), 0, lpRebootMessage);
269
270 if (lpFailureCommand)
271 HeapFree(GetProcessHeap(), 0, lpFailureCommand);
272
273 HeapFree(GetProcessHeap(), 0, lpFailureActions2);
274
275 break;
276 }
277
278 default:
279 skip("Unknown dwInfoLevel %lu, cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel, dwInfoLevel);
280 break;
281 }
282
283 RegCloseKey(hKey);
284
285 HeapFree(GetProcessHeap(), 0, lpBuffer);
286
287 return 0;
288 }
289
QueryConfig2A(SC_HANDLE hService,LPCSTR serviceName,DWORD dwInfoLevel)290 static int QueryConfig2A(SC_HANDLE hService, LPCSTR serviceName, DWORD dwInfoLevel)
291 {
292 int iRet = 0;
293 LONG lRet = 0;
294 DWORD dwRet = 0;
295 BOOL bError = FALSE;
296 DWORD dwRequiredSize = 0;
297 LPBYTE lpBuffer = NULL;
298
299 CHAR keyName[256];
300 HKEY hKey = NULL;
301 DWORD dwType = 0;
302
303 /* Get the needed size */
304 SetLastError(0xdeadbeef);
305 bError = QueryServiceConfig2A(hService,
306 dwInfoLevel,
307 NULL,
308 0,
309 &dwRequiredSize);
310 ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER);
311 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
312 if (dwRequiredSize == 0)
313 {
314 skip("Required size is null; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel);
315 return 1;
316 }
317
318 /* Allocate memory */
319 lpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
320 if (lpBuffer == NULL)
321 {
322 skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
323 return 2;
324 }
325
326 /* Get the actual value */
327 SetLastError(0xdeadbeef);
328 bError = QueryServiceConfig2A(hService,
329 dwInfoLevel,
330 lpBuffer,
331 dwRequiredSize,
332 &dwRequiredSize);
333 ok(bError, "bError = %u, expected TRUE\n", bError);
334 if (bError == FALSE)
335 {
336 skip("QueryServiceConfig2A returned an error; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel);
337 HeapFree(GetProcessHeap(), 0, lpBuffer);
338 return 3;
339 }
340
341 /* Now we can compare the retrieved value with what it's actually stored in the registry */
342 StringCbPrintfA(keyName, sizeof(keyName), "System\\CurrentControlSet\\Services\\%s", serviceName);
343 SetLastError(0xdeadbeef);
344 lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &hKey);
345 ok(lRet == ERROR_SUCCESS, "RegOpenKeyExA failed with 0x%08lx\n", lRet);
346 if (lRet != ERROR_SUCCESS)
347 {
348 skip("No regkey; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel);
349 HeapFree(GetProcessHeap(), 0, lpBuffer);
350 return 4;
351 }
352
353 switch (dwInfoLevel)
354 {
355 case SERVICE_CONFIG_DESCRIPTION:
356 {
357 LPSERVICE_DESCRIPTIONA lpDescription = (LPSERVICE_DESCRIPTIONA)lpBuffer;
358 LPSTR lpszDescription = NULL;
359
360 /* Retrieve the description via the registry */
361 dwRet = RegReadStringA(hKey, "Description", &lpszDescription);
362 ok(dwRet == ERROR_SUCCESS, "RegReadStringA returned 0x%08lx\n", dwRet);
363 ok(lpszDescription != NULL, "lpszDescription is null, expected non-null\n");
364
365 /* Compare it with the description retrieved via QueryServiceConfig2 */
366 if (lpszDescription)
367 iRet = strcmp(lpDescription->lpDescription, lpszDescription);
368 else
369 iRet = 0;
370
371 ok(iRet == 0, "Retrieved descriptions are different !\n");
372
373
374 /* Memory cleanup */
375 HeapFree(GetProcessHeap(), 0, lpszDescription);
376
377 break;
378 }
379
380 case SERVICE_CONFIG_FAILURE_ACTIONS:
381 {
382 LPSERVICE_FAILURE_ACTIONSA lpFailureActions1 = (LPSERVICE_FAILURE_ACTIONSA)lpBuffer;
383 LPSERVICE_FAILURE_ACTIONSA lpFailureActions2 = NULL;
384 LPSTR lpRebootMessage = NULL;
385 LPSTR lpFailureCommand = NULL;
386 DWORD i = 0;
387
388 /* Retrieve the failure actions via the registry */
389 lRet = RegQueryValueExA(hKey,
390 "FailureActions",
391 NULL,
392 &dwType,
393 NULL,
394 &dwRequiredSize);
395 ok(lRet == ERROR_SUCCESS, "RegQueryValueExA returned 0x%08lx\n", lRet);
396 ok(dwType == REG_BINARY, "dwType = %lu, expected REG_BINARY\n", dwType);
397 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
398
399 lpFailureActions2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize);
400 if (lpFailureActions2 == NULL)
401 {
402 skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize);
403 break;
404 }
405
406 lRet = RegQueryValueExA(hKey,
407 "FailureActions",
408 NULL,
409 NULL,
410 (LPBYTE)lpFailureActions2,
411 &dwRequiredSize);
412 ok(lRet == ERROR_SUCCESS, "RegQueryValueExA returned 0x%08lx\n", lRet);
413 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n");
414
415 /* Get the strings */
416 RegReadStringA(hKey, "FailureCommand", &lpFailureCommand);
417 RegReadStringA(hKey, "RebootMessage" , &lpRebootMessage );
418
419 /* Check the values */
420 ok(lpFailureActions1->dwResetPeriod == lpFailureActions2->dwResetPeriod, "lpFailureActions1->dwResetPeriod != lpFailureActions2->dwResetPeriod\n");
421 #ifndef _M_AMD64 // Fails on Win 2003 x64
422 ok(lpFailureActions1->cActions == lpFailureActions2->cActions, "lpFailureActions1->cActions != lpFailureActions2->cActions\n");
423 #endif
424
425 /* Compare the actions */
426 if (lpFailureActions1->cActions == lpFailureActions2->cActions)
427 {
428 lpFailureActions2->lpsaActions = (lpFailureActions2->cActions > 0 ? (LPSC_ACTION)(lpFailureActions2 + 1) : NULL);
429
430 if (lpFailureActions1->cActions > 0 &&
431 lpFailureActions1->lpsaActions != NULL)
432 {
433 for (i = 0; i < lpFailureActions1->cActions; ++i)
434 {
435 ok(lpFailureActions1->lpsaActions[i].Type == lpFailureActions2->lpsaActions[i].Type , "lpFailureActions1->lpsaActions[%lu].Type != lpFailureActions2->lpsaActions[%lu].Type\n" , i, i);
436 ok(lpFailureActions1->lpsaActions[i].Delay == lpFailureActions2->lpsaActions[i].Delay, "lpFailureActions1->lpsaActions[%lu].Delay != lpFailureActions2->lpsaActions[%lu].Delay\n", i, i);
437 }
438 }
439 }
440
441 /* TODO: retrieve the strings if they are in MUI format */
442
443 /* Compare RebootMsg */
444 if (lpFailureActions1->lpRebootMsg && lpRebootMessage)
445 iRet = strcmp(lpFailureActions1->lpRebootMsg, lpRebootMessage);
446 else
447 iRet = 0;
448
449 ok(iRet == 0, "Retrieved reboot messages are different !\n");
450
451 /* Compare Command */
452 if (lpFailureActions1->lpCommand && lpFailureCommand)
453 iRet = strcmp(lpFailureActions1->lpCommand, lpFailureCommand);
454 else
455 iRet = 0;
456
457 ok(iRet == 0, "Retrieved commands are different !\n");
458
459
460 /* Memory cleanup */
461 if (lpRebootMessage)
462 HeapFree(GetProcessHeap(), 0, lpRebootMessage);
463
464 if (lpFailureCommand)
465 HeapFree(GetProcessHeap(), 0, lpFailureCommand);
466
467 HeapFree(GetProcessHeap(), 0, lpFailureActions2);
468
469 break;
470 }
471
472 default:
473 skip("Unknown dwInfoLevel %lu, cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel, dwInfoLevel);
474 break;
475 }
476
477 RegCloseKey(hKey);
478
479 HeapFree(GetProcessHeap(), 0, lpBuffer);
480
481 return 0;
482 }
483
484
Test_QueryServiceConfig2W(void)485 static void Test_QueryServiceConfig2W(void)
486 {
487 SC_HANDLE hScm = NULL;
488 SC_HANDLE hService = NULL;
489
490 SetLastError(0xdeadbeef);
491 hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
492 ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
493 if (!hScm)
494 {
495 skip("No service control manager; cannot proceed with QueryServiceConfig2W test\n");
496 goto cleanup;
497 }
498
499 ok_err(ERROR_SUCCESS);
500
501 SetLastError(0xdeadbeef);
502 hService = OpenServiceW(hScm, TESTING_SERVICEW, SERVICE_QUERY_CONFIG);
503 ok(hService != NULL, "Failed to open service handle, error=0x%08lx\n", GetLastError());
504 if (!hService)
505 {
506 skip("Service not found; cannot proceed with QueryServiceConfig2W test\n");
507 goto cleanup;
508 }
509
510 ok_err(ERROR_SUCCESS);
511
512 if (QueryConfig2W(hService, TESTING_SERVICEW, SERVICE_CONFIG_DESCRIPTION) != 0)
513 goto cleanup;
514
515 if (QueryConfig2W(hService, TESTING_SERVICEW, SERVICE_CONFIG_FAILURE_ACTIONS) != 0)
516 goto cleanup;
517
518 cleanup:
519 if (hService)
520 CloseServiceHandle(hService);
521
522 if (hScm)
523 CloseServiceHandle(hScm);
524 }
525
Test_QueryServiceConfig2A(void)526 static void Test_QueryServiceConfig2A(void)
527 {
528 SC_HANDLE hScm = NULL;
529 SC_HANDLE hService = NULL;
530
531 SetLastError(0xdeadbeef);
532 hScm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT);
533 ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError());
534 if (!hScm)
535 {
536 skip("No service control manager; cannot proceed with QueryServiceConfig2A test\n");
537 goto cleanup;
538 }
539
540 ok_err(ERROR_SUCCESS);
541
542 SetLastError(0xdeadbeef);
543 hService = OpenServiceA(hScm, TESTING_SERVICEA, SERVICE_QUERY_CONFIG);
544 ok(hService != NULL, "Failed to open service handle, error=0x%08lx\n", GetLastError());
545 if (!hService)
546 {
547 skip("Service not found; cannot proceed with QueryServiceConfig2A test\n");
548 goto cleanup;
549 }
550
551 ok_err(ERROR_SUCCESS);
552
553 if (QueryConfig2A(hService, TESTING_SERVICEA, SERVICE_CONFIG_DESCRIPTION) != 0)
554 goto cleanup;
555
556 if (QueryConfig2A(hService, TESTING_SERVICEA, SERVICE_CONFIG_FAILURE_ACTIONS) != 0)
557 goto cleanup;
558
559 cleanup:
560 if (hService)
561 CloseServiceHandle(hService);
562
563 if (hScm)
564 CloseServiceHandle(hScm);
565 }
566
567
START_TEST(QueryServiceConfig2)568 START_TEST(QueryServiceConfig2)
569 {
570 Test_QueryServiceConfig2W();
571 Test_QueryServiceConfig2A();
572 }
573