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 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 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 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 ok(lpFailureActions1->cActions == lpFailureActions2->cActions, "lpFailureActions1->cActions != lpFailureActions2->cActions\n"); 229 230 /* Compare the actions */ 231 if (lpFailureActions1->cActions == lpFailureActions2->cActions) 232 { 233 lpFailureActions2->lpsaActions = (lpFailureActions2->cActions > 0 ? (LPSC_ACTION)(lpFailureActions2 + 1) : NULL); 234 235 if (lpFailureActions1->cActions > 0 && 236 lpFailureActions1->lpsaActions != NULL) 237 { 238 for (i = 0; i < lpFailureActions1->cActions; ++i) 239 { 240 ok(lpFailureActions1->lpsaActions[i].Type == lpFailureActions2->lpsaActions[i].Type , "lpFailureActions1->lpsaActions[%lu].Type != lpFailureActions2->lpsaActions[%lu].Type\n" , i, i); 241 ok(lpFailureActions1->lpsaActions[i].Delay == lpFailureActions2->lpsaActions[i].Delay, "lpFailureActions1->lpsaActions[%lu].Delay != lpFailureActions2->lpsaActions[%lu].Delay\n", i, i); 242 } 243 } 244 } 245 246 /* TODO: retrieve the strings if they are in MUI format */ 247 248 /* Compare RebootMsg */ 249 if (lpFailureActions1->lpRebootMsg && lpRebootMessage) 250 iRet = wcscmp(lpFailureActions1->lpRebootMsg, lpRebootMessage); 251 else 252 iRet = 0; 253 254 ok(iRet == 0, "Retrieved reboot messages are different !\n"); 255 256 /* Compare Command */ 257 if (lpFailureActions1->lpCommand && lpFailureCommand) 258 iRet = wcscmp(lpFailureActions1->lpCommand, lpFailureCommand); 259 else 260 iRet = 0; 261 262 ok(iRet == 0, "Retrieved commands are different !\n"); 263 264 265 /* Memory cleanup */ 266 if (lpRebootMessage) 267 HeapFree(GetProcessHeap(), 0, lpRebootMessage); 268 269 if (lpFailureCommand) 270 HeapFree(GetProcessHeap(), 0, lpFailureCommand); 271 272 HeapFree(GetProcessHeap(), 0, lpFailureActions2); 273 274 break; 275 } 276 277 default: 278 skip("Unknown dwInfoLevel %lu, cannot proceed with QueryConfig2W --> %lu test\n", dwInfoLevel, dwInfoLevel); 279 break; 280 } 281 282 RegCloseKey(hKey); 283 284 HeapFree(GetProcessHeap(), 0, lpBuffer); 285 286 return 0; 287 } 288 289 static int QueryConfig2A(SC_HANDLE hService, LPCSTR serviceName, DWORD dwInfoLevel) 290 { 291 int iRet = 0; 292 LONG lRet = 0; 293 DWORD dwRet = 0; 294 BOOL bError = FALSE; 295 DWORD dwRequiredSize = 0; 296 LPBYTE lpBuffer = NULL; 297 298 CHAR keyName[256]; 299 HKEY hKey = NULL; 300 DWORD dwType = 0; 301 302 /* Get the needed size */ 303 SetLastError(0xdeadbeef); 304 bError = QueryServiceConfig2A(hService, 305 dwInfoLevel, 306 NULL, 307 0, 308 &dwRequiredSize); 309 ok(bError == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER, "(bError, GetLastError()) = (%u, 0x%08lx), expected (FALSE, 0x%08lx)\n", bError, GetLastError(), (DWORD)ERROR_INSUFFICIENT_BUFFER); 310 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n"); 311 if (dwRequiredSize == 0) 312 { 313 skip("Required size is null; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel); 314 return 1; 315 } 316 317 /* Allocate memory */ 318 lpBuffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize); 319 if (lpBuffer == NULL) 320 { 321 skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize); 322 return 2; 323 } 324 325 /* Get the actual value */ 326 SetLastError(0xdeadbeef); 327 bError = QueryServiceConfig2A(hService, 328 dwInfoLevel, 329 lpBuffer, 330 dwRequiredSize, 331 &dwRequiredSize); 332 ok(bError, "bError = %u, expected TRUE\n", bError); 333 if (bError == FALSE) 334 { 335 skip("QueryServiceConfig2A returned an error; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel); 336 HeapFree(GetProcessHeap(), 0, lpBuffer); 337 return 3; 338 } 339 340 /* Now we can compare the retrieved value with what it's actually stored in the registry */ 341 StringCbPrintfA(keyName, sizeof(keyName), "System\\CurrentControlSet\\Services\\%s", serviceName); 342 SetLastError(0xdeadbeef); 343 lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, keyName, 0, KEY_QUERY_VALUE, &hKey); 344 ok(lRet == ERROR_SUCCESS, "RegOpenKeyExA failed with 0x%08lx\n", lRet); 345 if (lRet != ERROR_SUCCESS) 346 { 347 skip("No regkey; cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel); 348 HeapFree(GetProcessHeap(), 0, lpBuffer); 349 return 4; 350 } 351 352 switch (dwInfoLevel) 353 { 354 case SERVICE_CONFIG_DESCRIPTION: 355 { 356 LPSERVICE_DESCRIPTIONA lpDescription = (LPSERVICE_DESCRIPTIONA)lpBuffer; 357 LPSTR lpszDescription = NULL; 358 359 /* Retrieve the description via the registry */ 360 dwRet = RegReadStringA(hKey, "Description", &lpszDescription); 361 ok(dwRet == ERROR_SUCCESS, "RegReadStringA returned 0x%08lx\n", dwRet); 362 ok(lpszDescription != NULL, "lpszDescription is null, expected non-null\n"); 363 364 /* Compare it with the description retrieved via QueryServiceConfig2 */ 365 if (lpszDescription) 366 iRet = strcmp(lpDescription->lpDescription, lpszDescription); 367 else 368 iRet = 0; 369 370 ok(iRet == 0, "Retrieved descriptions are different !\n"); 371 372 373 /* Memory cleanup */ 374 HeapFree(GetProcessHeap(), 0, lpszDescription); 375 376 break; 377 } 378 379 case SERVICE_CONFIG_FAILURE_ACTIONS: 380 { 381 LPSERVICE_FAILURE_ACTIONSA lpFailureActions1 = (LPSERVICE_FAILURE_ACTIONSA)lpBuffer; 382 LPSERVICE_FAILURE_ACTIONSA lpFailureActions2 = NULL; 383 LPSTR lpRebootMessage = NULL; 384 LPSTR lpFailureCommand = NULL; 385 DWORD i = 0; 386 387 /* Retrieve the failure actions via the registry */ 388 lRet = RegQueryValueExA(hKey, 389 "FailureActions", 390 NULL, 391 &dwType, 392 NULL, 393 &dwRequiredSize); 394 ok(lRet == ERROR_SUCCESS, "RegQueryValueExA returned 0x%08lx\n", lRet); 395 ok(dwType == REG_BINARY, "dwType = %lu, expected REG_BINARY\n", dwType); 396 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n"); 397 398 lpFailureActions2 = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwRequiredSize); 399 if (lpFailureActions2 == NULL) 400 { 401 skip("Cannot allocate %lu bytes of memory\n", dwRequiredSize); 402 break; 403 } 404 405 lRet = RegQueryValueExA(hKey, 406 "FailureActions", 407 NULL, 408 NULL, 409 (LPBYTE)lpFailureActions2, 410 &dwRequiredSize); 411 ok(lRet == ERROR_SUCCESS, "RegQueryValueExA returned 0x%08lx\n", lRet); 412 ok(dwRequiredSize != 0, "dwRequiredSize is zero, expected non-zero\n"); 413 414 /* Get the strings */ 415 RegReadStringA(hKey, "FailureCommand", &lpFailureCommand); 416 RegReadStringA(hKey, "RebootMessage" , &lpRebootMessage ); 417 418 /* Check the values */ 419 ok(lpFailureActions1->dwResetPeriod == lpFailureActions2->dwResetPeriod, "lpFailureActions1->dwResetPeriod != lpFailureActions2->dwResetPeriod\n"); 420 ok(lpFailureActions1->cActions == lpFailureActions2->cActions, "lpFailureActions1->cActions != lpFailureActions2->cActions\n"); 421 422 /* Compare the actions */ 423 if (lpFailureActions1->cActions == lpFailureActions2->cActions) 424 { 425 lpFailureActions2->lpsaActions = (lpFailureActions2->cActions > 0 ? (LPSC_ACTION)(lpFailureActions2 + 1) : NULL); 426 427 if (lpFailureActions1->cActions > 0 && 428 lpFailureActions1->lpsaActions != NULL) 429 { 430 for (i = 0; i < lpFailureActions1->cActions; ++i) 431 { 432 ok(lpFailureActions1->lpsaActions[i].Type == lpFailureActions2->lpsaActions[i].Type , "lpFailureActions1->lpsaActions[%lu].Type != lpFailureActions2->lpsaActions[%lu].Type\n" , i, i); 433 ok(lpFailureActions1->lpsaActions[i].Delay == lpFailureActions2->lpsaActions[i].Delay, "lpFailureActions1->lpsaActions[%lu].Delay != lpFailureActions2->lpsaActions[%lu].Delay\n", i, i); 434 } 435 } 436 } 437 438 /* TODO: retrieve the strings if they are in MUI format */ 439 440 /* Compare RebootMsg */ 441 if (lpFailureActions1->lpRebootMsg && lpRebootMessage) 442 iRet = strcmp(lpFailureActions1->lpRebootMsg, lpRebootMessage); 443 else 444 iRet = 0; 445 446 ok(iRet == 0, "Retrieved reboot messages are different !\n"); 447 448 /* Compare Command */ 449 if (lpFailureActions1->lpCommand && lpFailureCommand) 450 iRet = strcmp(lpFailureActions1->lpCommand, lpFailureCommand); 451 else 452 iRet = 0; 453 454 ok(iRet == 0, "Retrieved commands are different !\n"); 455 456 457 /* Memory cleanup */ 458 if (lpRebootMessage) 459 HeapFree(GetProcessHeap(), 0, lpRebootMessage); 460 461 if (lpFailureCommand) 462 HeapFree(GetProcessHeap(), 0, lpFailureCommand); 463 464 HeapFree(GetProcessHeap(), 0, lpFailureActions2); 465 466 break; 467 } 468 469 default: 470 skip("Unknown dwInfoLevel %lu, cannot proceed with QueryConfig2A --> %lu test\n", dwInfoLevel, dwInfoLevel); 471 break; 472 } 473 474 RegCloseKey(hKey); 475 476 HeapFree(GetProcessHeap(), 0, lpBuffer); 477 478 return 0; 479 } 480 481 482 static void Test_QueryServiceConfig2W(void) 483 { 484 SC_HANDLE hScm = NULL; 485 SC_HANDLE hService = NULL; 486 487 SetLastError(0xdeadbeef); 488 hScm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT); 489 ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError()); 490 if (!hScm) 491 { 492 skip("No service control manager; cannot proceed with QueryServiceConfig2W test\n"); 493 goto cleanup; 494 } 495 496 ok_err(ERROR_SUCCESS); 497 498 SetLastError(0xdeadbeef); 499 hService = OpenServiceW(hScm, TESTING_SERVICEW, SERVICE_QUERY_CONFIG); 500 ok(hService != NULL, "Failed to open service handle, error=0x%08lx\n", GetLastError()); 501 if (!hService) 502 { 503 skip("Service not found; cannot proceed with QueryServiceConfig2W test\n"); 504 goto cleanup; 505 } 506 507 ok_err(ERROR_SUCCESS); 508 509 if (QueryConfig2W(hService, TESTING_SERVICEW, SERVICE_CONFIG_DESCRIPTION) != 0) 510 goto cleanup; 511 512 if (QueryConfig2W(hService, TESTING_SERVICEW, SERVICE_CONFIG_FAILURE_ACTIONS) != 0) 513 goto cleanup; 514 515 cleanup: 516 if (hService) 517 CloseServiceHandle(hService); 518 519 if (hScm) 520 CloseServiceHandle(hScm); 521 } 522 523 static void Test_QueryServiceConfig2A(void) 524 { 525 SC_HANDLE hScm = NULL; 526 SC_HANDLE hService = NULL; 527 528 SetLastError(0xdeadbeef); 529 hScm = OpenSCManagerA(NULL, NULL, SC_MANAGER_CONNECT); 530 ok(hScm != NULL, "Failed to open service manager, error=0x%08lx\n", GetLastError()); 531 if (!hScm) 532 { 533 skip("No service control manager; cannot proceed with QueryServiceConfig2A test\n"); 534 goto cleanup; 535 } 536 537 ok_err(ERROR_SUCCESS); 538 539 SetLastError(0xdeadbeef); 540 hService = OpenServiceA(hScm, TESTING_SERVICEA, SERVICE_QUERY_CONFIG); 541 ok(hService != NULL, "Failed to open service handle, error=0x%08lx\n", GetLastError()); 542 if (!hService) 543 { 544 skip("Service not found; cannot proceed with QueryServiceConfig2A test\n"); 545 goto cleanup; 546 } 547 548 ok_err(ERROR_SUCCESS); 549 550 if (QueryConfig2A(hService, TESTING_SERVICEA, SERVICE_CONFIG_DESCRIPTION) != 0) 551 goto cleanup; 552 553 if (QueryConfig2A(hService, TESTING_SERVICEA, SERVICE_CONFIG_FAILURE_ACTIONS) != 0) 554 goto cleanup; 555 556 cleanup: 557 if (hService) 558 CloseServiceHandle(hService); 559 560 if (hScm) 561 CloseServiceHandle(hScm); 562 } 563 564 565 START_TEST(QueryServiceConfig2) 566 { 567 Test_QueryServiceConfig2W(); 568 Test_QueryServiceConfig2A(); 569 } 570