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