1 /*
2  * PROJECT:     ReactOS Print Spooler DLL API Tests
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Tests for EnumPrintersA/EnumPrintersW
5  * COPYRIGHT:   Copyright 2015-2017 Colin Finck (colin@reactos.org)
6  */
7 
8 #include <apitest.h>
9 
10 #define WIN32_NO_STATUS
11 #include <windef.h>
12 #include <winbase.h>
13 #include <wingdi.h>
14 #include <winspool.h>
15 
START_TEST(EnumPrinters)16 START_TEST(EnumPrinters)
17 {
18     BYTE TempBuffer[50];
19     BYTE ZeroBuffer[50] = { 0 };
20     DWORD cbNeeded;
21     DWORD cbTemp;
22     DWORD cchComputerName;
23     DWORD dwReturned;
24     PPRINTER_INFO_1W pPrinterInfo1;
25     PVOID pMem;
26     DWORD Level;
27     WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 2 + 1];
28 
29     wszComputerName[0] = L'\\';
30     wszComputerName[1] = L'\\';
31     cchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
32     if (!GetComputerNameW(&wszComputerName[2], &cchComputerName))
33     {
34         skip("GetComputerNameW failed with error %lu!\n", GetLastError());
35         return;
36     }
37 
38     cchComputerName += 2;
39 
40     // Verify that EnumPrintersW returns success and zeroes all input variables even though no flag has been specified.
41     memset(TempBuffer, 0xDE, sizeof(TempBuffer));
42     cbNeeded = 0xDEADBEEF;
43     dwReturned = 0xDEADBEEF;
44     SetLastError(0xDEADBEEF);
45     ok(EnumPrintersW(0, NULL, 1, TempBuffer, sizeof(TempBuffer), &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE\n");
46     ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu!\n", GetLastError());
47     ok(memcmp(TempBuffer, ZeroBuffer, sizeof(TempBuffer)) == 0, "TempBuffer has not been zeroed!\n");
48     ok(cbNeeded == 0, "cbNeeded is %lu!\n", cbNeeded);
49     ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
50 
51     // Level 5 is the highest supported under Windows Server 2003. Higher levels need to fail and leave the variables untouched!
52     cbNeeded = 0xDEADBEEF;
53     dwReturned = 0xDEADBEEF;
54     SetLastError(0xDEADBEEF);
55     ok(!EnumPrintersW(0, NULL, 6, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE!\n");
56     ok(GetLastError() == ERROR_INVALID_LEVEL, "EnumPrintersW returns error %lu!\n", GetLastError());
57     ok(cbNeeded == 0xDEADBEEF, "cbNeeded is %lu!\n", cbNeeded);
58     ok(dwReturned == 0xDEADBEEF, "dwReturned is %lu!\n", dwReturned);
59 
60     // Same goes for level 3.
61     cbNeeded = 0xDEADBEEF;
62     dwReturned = 0xDEADBEEF;
63     SetLastError(0xDEADBEEF);
64     ok(!EnumPrintersW(0, NULL, 3, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE!\n");
65     ok(GetLastError() == ERROR_INVALID_LEVEL, "EnumPrintersW returns error %lu!\n", GetLastError());
66     ok(cbNeeded == 0xDEADBEEF, "cbNeeded is %lu!\n", cbNeeded);
67     ok(dwReturned == 0xDEADBEEF, "dwReturned is %lu!\n", dwReturned);
68 
69     // Try for all levels. Level 0 is valid here and returns the PRINTER_INFO_STRESS structure (documented in MS-RPRN).
70     for (Level = 0; Level <= 5; Level++)
71     {
72         if (Level == 3)
73             continue;
74 
75         // Try with no valid arguments at all.
76         SetLastError(0xDEADBEEF);
77         ok(!EnumPrintersW(0, NULL, Level, NULL, 0, NULL, NULL), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
78         ok(GetLastError() == RPC_X_NULL_REF_POINTER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
79 
80         // It has to succeed if we supply the required pointers and query no information.
81         SetLastError(0xDEADBEEF);
82         ok(EnumPrintersW(0, NULL, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", Level);
83         ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
84         ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level);
85         ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
86 
87         // This constant is from Windows 9x/ME times and mustn't work anymore.
88         SetLastError(0xDEADBEEF);
89         ok(EnumPrintersW(PRINTER_ENUM_DEFAULT, NULL, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", Level);
90         ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
91         ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level);
92         ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
93 
94         // Now things get interesting. Let's query the buffer size for information about the local printers.
95         SetLastError(0xDEADBEEF);
96         ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
97         ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
98         ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
99 
100         // There need to be installed local printers for the next steps.
101         if (cbNeeded > 0)
102         {
103             // Same error has to occur with no buffer, but a size < 4 (AlignRpcPtr comes into play here).
104             SetLastError(0xDEADBEEF);
105             ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, Level, NULL, 1, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
106             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
107             ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", Level);
108             ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
109 
110             // Now provide the demanded size, but no buffer.
111             SetLastError(0xDEADBEEF);
112             ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, Level, NULL, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
113             ok(GetLastError() == ERROR_INVALID_USER_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
114             ok(cbTemp == 0, "cbTemp is %lu for Level %lu!\n", cbTemp, Level);
115             ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
116 
117             // Finally use the function as intended and aim for success!
118             // After that, cbTemp contains the needed buffer size without the computer name prepended.
119             pMem = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
120             SetLastError(0xDEADBEEF);
121             ok(EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, Level, pMem, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns FALSE for Level %lu!\n", Level);
122             ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
123             HeapFree(GetProcessHeap(), 0, pMem);
124 
125             if (Level != 4)
126             {
127                 // Show that the Name parameter is checked when PRINTER_ENUM_NAME is also specified.
128                 SetLastError(0xDEADBEEF);
129                 ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, L"LOREM IPSUM", Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
130                 ok(GetLastError() != ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
131                 ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level);
132                 ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
133             }
134 
135             // Show that the structure is returned with its known size again when PRINTER_ENUM_NAME is specified and Name
136             // is the (case-insensitively compared) name of the Local Print Provider.
137             SetLastError(0xDEADBEEF);
138             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);
139             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
140             ok(cbNeeded == cbTemp, "cbNeeded is %lu, reference size is %lu for Level %lu!\n", cbNeeded, cbTemp, Level);
141             ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
142 
143             // Now we specify the correct "\\COMPUTERNAME" for Name.
144             // The returned structure should have some strings prepended with the Computer Name and thus require a larger buffer.
145             SetLastError(0xDEADBEEF);
146             ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
147             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
148             ok(cbNeeded > cbTemp, "cbNeeded is %lu, reference size is %lu for Level %lu!\n", cbNeeded, cbTemp, Level);
149             ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
150 
151             if (Level != 4)
152             {
153                 // This won't work when there is a trailing backslash (i.e. "\\COMPUTERNAME\").
154                 wszComputerName[cchComputerName++] = L'\\';
155                 wszComputerName[cchComputerName] = 0;
156                 SetLastError(0xDEADBEEF);
157                 ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
158                 ok(GetLastError() == ERROR_INVALID_NAME, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
159                 ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level);
160                 ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
161                 wszComputerName[--cchComputerName] = 0;
162             }
163 
164             // Now it gets funky. There are also cases where EnumPrintersW takes the Name parameter into account,
165             // although PRINTER_ENUM_NAME is not given.
166             // A bogus Name without two backslashes is ignored.
167             SetLastError(0xDEADBEEF);
168             ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, L"LOREM IPSUM", Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
169             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
170             ok(cbNeeded == cbTemp, "cbNeeded is %lu, reference size is %lu for Level %lu!\n", cbNeeded, cbTemp, Level);
171             ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
172 
173             // Specifying "\\COMPUTERNAME" again prepends it to some strings.
174             SetLastError(0xDEADBEEF);
175             ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
176             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
177             ok(cbNeeded > cbTemp, "cbNeeded is %lu, reference size is %lu for Level %lu!\n", cbNeeded, cbTemp, Level);
178             ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
179 
180             // Specifying "\\COMPUTERNAME\" also verifies the Computer Name, but doesn't prepend it.
181             // This logic is crazy, and different to PRINTER_ENUM_NAME...
182             wszComputerName[cchComputerName++] = L'\\';
183             wszComputerName[cchComputerName] = 0;
184             SetLastError(0xDEADBEEF);
185             ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
186             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
187             ok(cbNeeded == cbTemp, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level);
188             ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
189 
190             // I can even put an additional bogus character after the trailing backslash, doesn't change anything.
191             wszComputerName[cchComputerName++] = L'a';
192             wszComputerName[cchComputerName] = 0;
193             SetLastError(0xDEADBEEF);
194             ok(!EnumPrintersW(PRINTER_ENUM_LOCAL, wszComputerName, Level, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE for Level %lu!\n", Level);
195             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu for Level %lu!\n", GetLastError(), Level);
196             ok(cbNeeded == cbTemp, "cbNeeded is %lu for Level %lu!\n", cbNeeded, Level);
197             ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, Level);
198             cchComputerName -= 2;
199             wszComputerName[cchComputerName] = 0;
200         }
201         else
202         {
203             skip("cbNeeded is 0 on Level %lu, skipping additional tests!\n", Level);
204         }
205     }
206 
207     // Using EnumPrintersW with PRINTER_ENUM_NAME, Level 1 and no Name must return information about the Print Providers.
208     // First record must always be the Local Print Provider.
209     SetLastError(0xDEADBEEF);
210     ok(!EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "EnumPrintersW returns TRUE!\n");
211     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "EnumPrintersW returns error %lu!\n", GetLastError());
212     ok(cbNeeded > 0, "cbNeeded is 0!\n");
213     ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
214 
215     SetLastError(0xDEADBEEF);
216     pPrinterInfo1 = (PPRINTER_INFO_1W)HeapAlloc(GetProcessHeap(), 0, cbNeeded);
217     ok(EnumPrintersW(PRINTER_ENUM_LOCAL | PRINTER_ENUM_NAME, NULL, 1, (PBYTE)pPrinterInfo1, cbNeeded, &cbTemp, &dwReturned), "EnumPrintersW returns FALSE!\n");
218     ok(GetLastError() == ERROR_SUCCESS, "EnumPrintersW returns error %lu!\n", GetLastError());
219     ok(cbTemp == cbNeeded, "cbTemp is %lu, cbNeeded is %lu!\n", cbTemp, cbNeeded);
220     ok(dwReturned > 0, "dwReturned is %lu!\n", dwReturned);
221     ok(!wcscmp(pPrinterInfo1->pName, L"Windows NT Local Print Providor"), "pPrinterInfo1->pName is %S!\n", pPrinterInfo1->pName);
222     ok(!wcscmp(pPrinterInfo1->pComment, L"Locally connected Printers"), "pPrinterInfo1->pComment is %S!\n", pPrinterInfo1->pComment);
223     ok(!wcscmp(pPrinterInfo1->pDescription, L"Windows NT Local Printers"), "pPrinterInfo1->pDescription is %S!\n", pPrinterInfo1->pDescription);
224     HeapFree(GetProcessHeap(), 0, pPrinterInfo1);
225 }
226