From daf94f98b015567d0432f045d2313cdd0ddd5a04 Mon Sep 17 00:00:00 2001 From: Colin Finck Date: Mon, 17 Apr 2017 13:40:51 +0000 Subject: [PATCH] [WINSPOOL] Added tests for some more basic and newly discovered corner cases of EnumPrintersW. svn path=/trunk/; revision=74350 --- rostests/apitests/winspool/EnumPrinters.c | 191 ++++++++++++++++++---- 1 file changed, 155 insertions(+), 36 deletions(-) diff --git a/rostests/apitests/winspool/EnumPrinters.c b/rostests/apitests/winspool/EnumPrinters.c index 7223fc46d12..7f2521121b4 100644 --- a/rostests/apitests/winspool/EnumPrinters.c +++ b/rostests/apitests/winspool/EnumPrinters.c @@ -19,10 +19,23 @@ START_TEST(EnumPrinters) BYTE ZeroBuffer[50] = { 0 }; DWORD cbNeeded; DWORD cbTemp; + DWORD cchComputerName; DWORD dwReturned; + PPRINTER_INFO_1W pPrinterInfo1; PVOID pMem; - DWORD i; - DWORD dwValidLevels[] = { 0, 1, 2, 4, 5 }; + DWORD Level; + WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 2 + 1]; + + wszComputerName[0] = L'\\'; + wszComputerName[1] = L'\\'; + cchComputerName = MAX_COMPUTERNAME_LENGTH + 1; + if (!GetComputerNameW(&wszComputerName[2], &cchComputerName)) + { + skip("GetComputerNameW failed with error %lu!\n", GetLastError()); + return; + } + + cchComputerName += 2; // Verify that EnumPrintersW returns success and zeroes all input variables even though no flag has been specified. memset(TempBuffer, 0xDE, sizeof(TempBuffer)); @@ -53,54 +66,160 @@ START_TEST(EnumPrinters) ok(cbNeeded == 0xDEADBEEF, "cbNeeded is %lu!\n", cbNeeded); ok(dwReturned == 0xDEADBEEF, "dwReturned is %lu!\n", dwReturned); - // Try for all valid levels. Level 0 is valid here and returns the PRINTER_INFO_STRESS structure (documented in MS-RPRN). - for (i = 0; i < sizeof(dwValidLevels) / sizeof(DWORD); i++) + // Try for all levels. Level 0 is valid here and returns the PRINTER_INFO_STRESS structure (documented in MS-RPRN). + for (Level = 0; Level <= 5; Level++) { + if (Level == 3) + continue; + // Try with no valid arguments at all. SetLastError(0xDEADBEEF); - ok(!EnumPrintersW(0, NULL, dwValidLevels[i], NULL, 0, NULL, NULL), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]); - ok(GetLastError() == RPC_X_NULL_REF_POINTER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]); + ok(!EnumPrintersW(0, NULL, Level, NULL, 0, NULL, NULL), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == RPC_X_NULL_REF_POINTER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); // It has to succeed if we supply the required pointers and query no information. SetLastError(0xDEADBEEF); - ok(EnumPrintersW(0, NULL, dwValidLevels[i], NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", dwValidLevels[i]); - ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]); - ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, dwValidLevels[i]); - ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]); + ok(EnumPrintersW(0, NULL, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); // This constant is from Windows 9x/ME times and mustn't work anymore. SetLastError(0xDEADBEEF); - ok(EnumPrintersW(PRINTER_ENUM_DEFAULT, NULL, dwValidLevels[i], NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", dwValidLevels[i]); - ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]); - ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, dwValidLevels[i]); - ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]); + ok(EnumPrintersW(PRINTER_ENUM_DEFAULT, NULL, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); // Now things get interesting. Let's query the buffer size for information about the local printers. SetLastError(0xDEADBEEF); - ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]); - ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]); - ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", dwValidLevels[i]); - ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); - // Same error has to occur with no buffer, but a size < 4 (AlignRpcPtr comes into play here). - SetLastError(0xDEADBEEF); - ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], NULL, 1, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]); - ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]); - ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", dwValidLevels[i]); - ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]); + // There need to be installed local printers for the next steps. + if (cbNeeded > 0) + { + // Same error has to occur with no buffer, but a size < 4 (AlignRpcPtr comes into play here). + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, Level, NULL, 1, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); - // Now provide the demanded size, but no buffer. - SetLastError(0xDEADBEEF); - ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], NULL, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", dwValidLevels[i]); - ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]); - ok(cbTemp == 0, "cbTemp is %lu for Level %lu!\n", cbTemp, dwValidLevels[i]); - ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, dwValidLevels[i]); + // Now provide the demanded size, but no buffer. + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, Level, NULL, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbTemp == 0, "cbTemp is %lu for Level %lu!\n", cbTemp, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); - // Finally use the function as intended and aim for success! - pMem = HeapAlloc(GetProcessHeap(), 0, cbNeeded); - SetLastError(0xDEADBEEF); - ok(EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, dwValidLevels[i], pMem, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", dwValidLevels[i]); - ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), dwValidLevels[i]); - HeapFree(GetProcessHeap(), 0, pMem); + // Finally use the function as intended and aim for success! + // After that, cbTemp contains the needed buffer size without the computer name prepended. + pMem = HeapAlloc(GetProcessHeap(), 0, cbNeeded); + SetLastError(0xDEADBEEF); + ok(EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, Level, pMem, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + HeapFree(GetProcessHeap(), 0, pMem); + + if (Level != 4) + { + // Show that the Name parameter is checked when PRINTER_ENUM_NAME is also specified. + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, L"LOREM IPSUM", Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() != ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); + } + + // Show that the structure is returned with its known size again when PRINTER_ENUM_NAME is specified and Name + // is the (case-insensitively compared) name of the Local Print Provider. + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, L"wInDoWs NT Local Print Providor", Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded == cbTemp, "cbNeeded is %lu, reference size is %lu for Level %lu!\n", cbNeeded, cbTemp, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); + + // Now we specify the correct "\\COMPUTERNAME" for Name. + // The returned structure should have some strings prepended with the Computer Name and thus require a larger buffer. + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded > cbTemp, "cbNeeded is %lu, reference size is %lu for Level %lu!\n", cbNeeded, cbTemp, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); + + if (Level != 4) + { + // This won't work when there is a trailing backslash (i.e. "\\COMPUTERNAME\"). + wszComputerName[cchComputerName++] = L'\\'; + wszComputerName[cchComputerName] = 0; + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INVALID_NAME, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); + wszComputerName[--cchComputerName] = 0; + } + + // Now it gets funky. There are also cases where EnumPrintersW takes the Name parameter into account, + // although PRINTER_ENUM_NAME is not given. + // A bogus Name without two backslashes is ignored. + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, L"LOREM IPSUM", Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded == cbTemp, "cbNeeded is %lu, reference size is %lu for Level %lu!\n", cbNeeded, cbTemp, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); + + // Specifying "\\COMPUTERNAME" again prepends it to some strings. + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded > cbTemp, "cbNeeded is %lu, reference size is %lu for Level %lu!\n", cbNeeded, cbTemp, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); + + // Specifying "\\COMPUTERNAME\" also verifies the Computer Name, but doesn't prepend it. + // This logic is crazy, and different to PRINTER_ENUM_NAME... + wszComputerName[cchComputerName++] = L'\\'; + wszComputerName[cchComputerName] = 0; + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded == cbTemp, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); + + // I can even put an additional bogus character after the trailing backslash, doesn't change anything. + wszComputerName[cchComputerName++] = L'a'; + wszComputerName[cchComputerName] = 0; + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level); + ok(cbNeeded == cbTemp, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level); + ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level); + cchComputerName -= 2; + wszComputerName[cchComputerName] = 0; + } + else + { + skip("cbNeeded is 0 on Level %lu, skipping additional tests!\n", Level); + } } + + // Using EnumPrintersW with PRINTER_ENUM_NAME, Level 1 and no Name must return information about the Print Providers. + // First record must always be the Local Print Provider. + SetLastError(0xDEADBEEF); + ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE!\n"); + ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu!\n", GetLastError()); + ok(cbNeeded > 0, "cbNeeded is 0!\n"); + ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned); + + SetLastError(0xDEADBEEF); + pPrinterInfo1 = (PPRINTER_INFO_1W)HeapAlloc(GetProcessHeap(), 0, cbNeeded); + ok(EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, (PBYTE)pPrinterInfo1, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns FALSE!\n"); + ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu!\n", GetLastError()); + ok(cbTemp == cbNeeded, "cbTemp is %lu, cbNeeded is %lu!\n", cbTemp, cbNeeded); + ok(dwReturned > 0, "dwReturned is %lu!\n", dwReturned); + ok(!wcscmp(pPrinterInfo1->pName, L"Windows NT Local Print Providor"), "pPrinterInfo1->pName is %S!\n", pPrinterInfo1->pName); + ok(!wcscmp(pPrinterInfo1->pComment, L"Locally connected Printers"), "pPrinterInfo1->pComment is %S!\n", pPrinterInfo1->pComment); + ok(!wcscmp(pPrinterInfo1->pDescription, L"Windows NT Local Printers"), "pPrinterInfo1->pDescription is %S!\n", pPrinterInfo1->pDescription); + HeapFree(GetProcessHeap(), 0, pPrinterInfo1); } -- 2.17.1