1 /*
2  * PROJECT:     ReactOS Local Spooler API Tests Injected DLL
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Tests for fpEnumPrinters
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 <winreg.h>
15 #include <winspool.h>
16 #include <winsplp.h>
17 
18 #include <pseh/pseh2.h>
19 
20 #include "../localspl_apitest.h"
21 #include <spoolss.h>
22 
23 extern BOOL GetLocalsplFuncs(LPPRINTPROVIDOR pp);
24 
25 START_TEST(fpEnumPrinters)
26 {
27     BYTE TempBuffer[50];
28     BYTE ZeroBuffer[50];
29     DWORD cbNeeded;
30     DWORD cbTemp;
31     DWORD dwReturned;
32     DWORD i;
33     PRINTPROVIDOR pp;
34     PPRINTER_INFO_1W pPrinterInfo1;
35     PVOID pMem;
36 
37     if (!GetLocalsplFuncs(&pp))
38         return;
39 
40     // Verify that fpEnumPrinters returns success and zeros cbNeeded and dwReturned (but not TempBuffer!) if no flag has been specified.
41     memset(TempBuffer, 0xDE, sizeof(TempBuffer));
42     memset(ZeroBuffer, 0, sizeof(ZeroBuffer));
43     cbNeeded = 0xDEADBEEF;
44     dwReturned = 0xDEADBEEF;
45     SetLastError(0xDEADBEEF);
46     ok(pp.fpEnumPrinters(0, NULL, 1, TempBuffer, sizeof(TempBuffer), &cbNeeded, &dwReturned), "fpEnumPrinters returns FALSE\n");
47     ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu!\n", GetLastError());
48     ok(memcmp(TempBuffer, ZeroBuffer, sizeof(TempBuffer)) != 0, "TempBuffer has been zeroed!\n");
49     ok(cbNeeded == 0, "cbNeeded is %lu!\n", cbNeeded);
50     ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
51 
52     // Verify that localspl only returns information about a single print provider (namely itself).
53     cbNeeded = 0xDEADBEEF;
54     dwReturned = 0xDEADBEEF;
55     SetLastError(0xDEADBEEF);
56     ok(!pp.fpEnumPrinters(PRINTER_ENUM_NAME, NULL, 1, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns TRUE\n");
57     ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "fpEnumPrinters returns error %lu!\n", GetLastError());
58     ok(cbNeeded > 0, "cbNeeded is 0!\n");
59     ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
60 
61     SetLastError(0xDEADBEEF);
62     pPrinterInfo1 = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
63     ok(pp.fpEnumPrinters(PRINTER_ENUM_NAME, NULL, 1, (PBYTE)pPrinterInfo1, cbNeeded, &cbNeeded, &dwReturned), "fpEnumPrinters returns FALSE\n");
64     ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu!\n", GetLastError());
65     ok(cbNeeded > 0, "cbNeeded is 0!\n");
66     ok(dwReturned == 1, "dwReturned is %lu!\n", dwReturned);
67 
68     // Verify the actual strings returned.
69     ok(wcscmp(pPrinterInfo1->pName, L"Windows NT Local Print Providor") == 0, "pPrinterInfo1->pName is \"%S\"!\n", pPrinterInfo1->pName);
70     ok(wcscmp(pPrinterInfo1->pDescription, L"Windows NT Local Printers") == 0, "pPrinterInfo1->pDescription is \"%S\"!\n", pPrinterInfo1->pDescription);
71     ok(wcscmp(pPrinterInfo1->pComment, L"Locally connected Printers") == 0, "pPrinterInfo1->pComment is \"%S\"!\n", pPrinterInfo1->pComment);
72 
73     // Level 7 is the highest supported for localspl under Windows Server 2003.
74     // Higher levels need to fail, but they don't set an error code, just cbNeeded to 0.
75     cbNeeded = 0xDEADBEEF;
76     dwReturned = 0xDEADBEEF;
77     SetLastError(0xDEADBEEF);
78     ok(!pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, 8, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns TRUE!\n");
79     ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu!\n", GetLastError());
80     ok(cbNeeded == 0, "cbNeeded is %lu!\n", cbNeeded);
81     ok(dwReturned == 0, "dwReturned is %lu!\n", dwReturned);
82 
83     // Verify that all valid levels work.
84     // In contrast to EnumPrintersW, which only accepts levels 0, 1, 2, 4 and 5, localspl returns information for level 0 to 7.
85     for (i = 0; i <= 7; i++)
86     {
87         // Try with no valid arguments at all.
88         // This scenario is usually caugt by RPC, so it just raises an exception here.
89         _SEH2_TRY
90         {
91             dwReturned = 0;
92             pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, i, NULL, 0, NULL, NULL);
93         }
94         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
95         {
96             dwReturned = _SEH2_GetExceptionCode();
97         }
98         _SEH2_END;
99 
100         ok(dwReturned == EXCEPTION_ACCESS_VIOLATION, "dwReturned is %lu for Level %lu!\n", dwReturned, i);
101 
102         // Now get the required buffer size.
103         cbNeeded = 0xDEADBEEF;
104         dwReturned = 0xDEADBEEF;
105         SetLastError(0xDEADBEEF);
106         ok(!pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, i, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns TRUE for Level %lu!\n", i);
107         ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "fpEnumPrinters returns error %lu for Level %lu!\n", GetLastError(), i);
108         ok(cbNeeded > 0, "cbNeeded is 0 for Level %lu!\n", i);
109         ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, i);
110 
111         // This test corrupts something inside spoolsv so that it's only runnable once without restarting spoolsv. Therefore it's disabled.
112 #if 0
113         // Now provide the demanded size, but no buffer. This also mustn't touch cbNeeded.
114         // This scenario is also caught by RPC and we just have an exception here.
115         _SEH2_TRY
116         {
117             dwReturned = 0;
118             pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, i, NULL, cbNeeded, &cbTemp, &dwReturned);
119         }
120             _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
121         {
122             dwReturned = _SEH2_GetExceptionCode();
123         }
124         _SEH2_END;
125 
126         ok(dwReturned == EXCEPTION_ACCESS_VIOLATION, "dwReturned is %lu for Level %lu!\n", dwReturned, i);
127         ok(cbNeeded == cbTemp, "cbNeeded is %lu, cbTemp is %lu for Level %lu!\n", cbNeeded, cbTemp, i);
128 #endif
129 
130         // Finally use the function as intended and aim for success!
131         pMem = DllAllocSplMem(cbNeeded);
132         SetLastError(0xDEADBEEF);
133         ok(pp.fpEnumPrinters(PRINTER_ENUM_LOCAL, NULL, i, pMem, cbNeeded, &cbTemp, &dwReturned), "fpEnumPrinters returns FALSE for Level %lu!\n", i);
134 
135         // This is crazy. For level 3, fpEnumPrinters always returns ERROR_INSUFFICIENT_BUFFER even if we supply a buffer large enough.
136         if (i == 3)
137             ok(GetLastError() == ERROR_INSUFFICIENT_BUFFER, "fpEnumPrinters returns error %lu for Level %lu!\n", GetLastError(), i);
138         else
139             ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu for Level %lu!\n", GetLastError(), i);
140 
141         DllFreeSplMem(pMem);
142     }
143 
144     // fpEnumPrinters has to succeed independent of the level (valid or not) if we query no information.
145     for (i = 0; i < 10; i++)
146     {
147         SetLastError(0xDEADBEEF);
148         ok(pp.fpEnumPrinters(0, NULL, i, NULL, 0, &cbNeeded, &dwReturned), "fpEnumPrinters returns FALSE for Level %lu!\n", i);
149         ok(GetLastError() == ERROR_SUCCESS, "fpEnumPrinters returns error %lu for Level %lu!\n", GetLastError(), i);
150         ok(cbNeeded == 0, "cbNeeded is %lu for Level %lu!\n", cbNeeded, i);
151         ok(dwReturned == 0, "dwReturned is %lu for Level %lu!\n", dwReturned, i);
152     }
153 }
154