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 #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 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 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 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 568 START_TEST(QueryServiceConfig2) 569 { 570 Test_QueryServiceConfig2W(); 571 Test_QueryServiceConfig2A(); 572 } 573