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 GetPrinterData(Ex)A/GetPrinterData(Ex)W/SetPrinterData(Ex)A/SetPrinterData(Ex)W
5  * COPYRIGHT:   Copyright 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 #include <winreg.h>
16 
17 /* From printing/include/spoolss.h */
18 #define MAX_PRINTER_NAME        220
19 
20 typedef struct _SPLREG_VALUE
21 {
22     PSTR pszName;
23     PWSTR pwszName;
24     DWORD dwType;
25     DWORD cbNeededA;
26     BOOL bSettable;
27 }
28 SPLREG_VALUE, *PSPLREG_VALUE;
29 
30 SPLREG_VALUE SplRegValues[] = {
31     { "DefaultSpoolDirectory", L"DefaultSpoolDirectory", REG_SZ, 0xFFFFFFFF, TRUE },
32     { "PortThreadPriorityDefault", L"PortThreadPriorityDefault", REG_NONE, 4, FALSE },
33     { "PortThreadPriority", L"PortThreadPriority", REG_DWORD, 4, TRUE },
34     { "SchedulerThreadPriorityDefault", L"SchedulerThreadPriorityDefault", REG_NONE, 4, FALSE },
35     { "SchedulerThreadPriority", L"SchedulerThreadPriority", REG_DWORD, 4, TRUE },
36     { "BeepEnabled", L"BeepEnabled", REG_DWORD, 4, TRUE },
37 
38     /* These fail in Win8, probably removed since NT6:
39 
40     { "NetPopup", L"NetPopup", REG_DWORD, 4, TRUE },
41     { "RetryPopup", L"RetryPopup", REG_DWORD, 4, TRUE },
42     { "NetPopupToComputer", L"NetPopupToComputer", REG_DWORD, 4, TRUE },
43 
44     */
45 
46     { "EventLog", L"EventLog", REG_DWORD, 4, TRUE },
47     { "MajorVersion", L"MajorVersion", REG_NONE, 4, FALSE },
48     { "MinorVersion", L"MinorVersion", REG_NONE, 4, FALSE },
49     { "Architecture", L"Architecture", REG_NONE, 0xFFFFFFFF, FALSE },
50     { "OSVersion", L"OSVersion", REG_NONE, sizeof(OSVERSIONINFOA), FALSE },
51     { "OSVersionEx", L"OSVersionEx", REG_NONE, sizeof(OSVERSIONINFOEXA), FALSE },
52 #if 0
53     { "DsPresent", L"DsPresent", REG_DWORD, 4, FALSE },
54     { "DsPresentForUser", L"DsPresentForUser", REG_DWORD, 4, FALSE },
55 #endif
56     { "RemoteFax", L"RemoteFax", REG_NONE, 4, FALSE },
57     { "RestartJobOnPoolError", L"RestartJobOnPoolError", REG_DWORD, 4, TRUE },
58     { "RestartJobOnPoolEnabled", L"RestartJobOnPoolEnabled", REG_DWORD, 4, TRUE },
59     { "DNSMachineName", L"DNSMachineName", REG_SZ, 0xFFFFFFFF, FALSE },
60     { "AllowUserManageForms", L"AllowUserManageForms", REG_DWORD, 4, TRUE },
61     { NULL, NULL, 0, 0, FALSE }
62 };
63 
64 START_TEST(GetPrinterData)
65 {
66     DWORD cbNeeded;
67     DWORD cchDefaultPrinter;
68     DWORD dwReturnCode;
69     DWORD dwType;
70     HANDLE hPrinter;
71     PBYTE pDataA;
72     PBYTE pDataW;
73     PSPLREG_VALUE p;
74     WCHAR wszDefaultPrinter[MAX_PRINTER_NAME + 1];
75 
76     // Don't supply any parameters, this has to fail with ERROR_INVALID_HANDLE!
77     dwReturnCode = GetPrinterDataExW(NULL, NULL, NULL, NULL, NULL, 0, NULL);
78     ok(dwReturnCode == ERROR_INVALID_HANDLE, "GetPrinterDataExW returns error %lu!\n", dwReturnCode);
79 
80     // Open a handle to the local print server.
81     if (!OpenPrinterW(NULL, &hPrinter, NULL))
82     {
83         skip("Could not retrieve a handle to the local print server!\n");
84         return;
85     }
86 
87     // Now try with valid handle, but leave remaining parameters NULL.
88     dwReturnCode = GetPrinterDataExW(hPrinter, NULL, NULL, NULL, NULL, 0, NULL);
89     ok(dwReturnCode == RPC_X_NULL_REF_POINTER, "GetPrinterDataExW returns error %lu!\n", dwReturnCode);
90 
91     // Try all valid Print Server data values.
92     for (p = SplRegValues; p->pszName; p++)
93     {
94         // Try the ANSI version of the function.
95         dwType = 0xDEADBEEF;
96         dwReturnCode = GetPrinterDataExA(hPrinter, NULL, p->pszName, &dwType, NULL, 0, &cbNeeded);
97         ok(dwReturnCode == ERROR_MORE_DATA || dwReturnCode == ERROR_FILE_NOT_FOUND, "GetPrinterDataExA returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
98         if (dwReturnCode != ERROR_MORE_DATA)
99             continue;
100 
101         ok(dwType == p->dwType, "dwType is %lu for \"%s\"!\n", dwType, p->pszName);
102 
103         if (p->cbNeededA < 0xFFFFFFFF)
104             ok(cbNeeded == p->cbNeededA, "cbNeeded is %lu for \"%s\", but expected %lu!\n", cbNeeded, p->pszName, p->cbNeededA);
105         else
106             ok(cbNeeded > 0, "cbNeeded is 0 for \"%s\"!\n", p->pszName);
107 
108         pDataA = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
109         dwReturnCode = GetPrinterDataExA(hPrinter, NULL, p->pszName, NULL, pDataA, cbNeeded, &cbNeeded);
110         ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExA returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
111 
112         // Try the Unicode version of the function too.
113         dwType = 0xDEADBEEF;
114         dwReturnCode = GetPrinterDataExW(hPrinter, NULL, p->pwszName, &dwType, NULL, 0, &cbNeeded);
115         ok(dwReturnCode == ERROR_MORE_DATA, "GetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
116         ok(dwType == p->dwType, "dwType is %lu for \"%s\"!\n", dwType, p->pszName);
117 
118         pDataW = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
119         dwReturnCode = GetPrinterDataExW(hPrinter, NULL, p->pwszName, NULL, pDataW, cbNeeded, &cbNeeded);
120         ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
121 
122         // Verify that OSVERSIONINFO structures are correctly returned.
123         if (strcmp(p->pszName, "OSVersion") == 0)
124         {
125             POSVERSIONINFOA pOSVersionInfoA = (POSVERSIONINFOA)pDataA;
126             POSVERSIONINFOW pOSVersionInfoW = (POSVERSIONINFOW)pDataW;
127             ok(pOSVersionInfoA->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoA->dwOSVersionInfoSize);
128             ok(pOSVersionInfoW->dwOSVersionInfoSize == sizeof(OSVERSIONINFOW), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoW->dwOSVersionInfoSize);
129         }
130         else if (strcmp(p->pszName, "OSVersionEx") == 0)
131         {
132             POSVERSIONINFOEXA pOSVersionInfoA = (POSVERSIONINFOEXA)pDataA;
133             POSVERSIONINFOEXW pOSVersionInfoW = (POSVERSIONINFOEXW)pDataW;
134             ok(pOSVersionInfoA->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoA->dwOSVersionInfoSize);
135             ok(pOSVersionInfoW->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXW), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoW->dwOSVersionInfoSize);
136         }
137 
138         // Shortly test SetPrinterDataExW by setting the same data we just retrieved.
139         if (p->bSettable)
140         {
141             dwReturnCode = SetPrinterDataExW(hPrinter, NULL, p->pwszName, dwType, pDataW, cbNeeded);
142             ok(dwReturnCode == ERROR_SUCCESS, "SetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
143         }
144 
145         HeapFree(GetProcessHeap(), 0, pDataA);
146         HeapFree(GetProcessHeap(), 0, pDataW);
147     }
148 
149     // Try an invalid one.
150     dwReturnCode = GetPrinterDataExW(hPrinter, NULL, L"Invalid", NULL, NULL, 0, &cbNeeded);
151     ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
152 
153     ClosePrinter(hPrinter);
154 
155     // Open a handle to the default printer.
156     cchDefaultPrinter = _countof(wszDefaultPrinter);
157     ok(GetDefaultPrinterW(wszDefaultPrinter, &cchDefaultPrinter), "GetDefaultPrinterW returns FALSE and requires %lu characters!\n", cchDefaultPrinter);
158     if (!OpenPrinterW(wszDefaultPrinter, &hPrinter, NULL))
159     {
160         skip("Could not retrieve a handle to the default printer!\n");
161         return;
162     }
163 
164     // Using NULL or L"" for pKeyName on a Printer handle yields ERROR_INVALID_PARAMETER.
165     dwReturnCode = GetPrinterDataExW(hPrinter, NULL, L"Name", NULL, NULL, 0, &cbNeeded);
166     ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
167     dwReturnCode = GetPrinterDataExW(hPrinter, L"", L"Name", NULL, NULL, 0, &cbNeeded);
168     ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
169 
170     // Using L"\\" allows us to examine the contents of the main printer key anyway.
171     dwReturnCode = GetPrinterDataExW(hPrinter, L"\\", L"Name", &dwType, NULL, 0, &cbNeeded);
172     ok(dwReturnCode == ERROR_MORE_DATA, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
173     ok(dwType == REG_SZ, "dwType is %lu!\n", dwType);
174     ok(cbNeeded > 0, "cbNeeded is 0!\n");
175 
176     pDataW = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
177     dwReturnCode = GetPrinterDataExW(hPrinter, L"\\", L"Name", NULL, pDataW, cbNeeded, &cbNeeded);
178     ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
179 
180     // The following test fails if the default printer is a remote printer.
181     ok(wcscmp((PWSTR)pDataW, wszDefaultPrinter) == 0, "pDataW is \"%S\", default printer is \"%S\"!\n", (PWSTR)pDataW, wszDefaultPrinter);
182 
183     // SetPrinterDataExW should return ERROR_ACCESS_DENIED when attempting to set the Name.
184     dwReturnCode = SetPrinterDataExW(hPrinter, L"\\", L"Name", REG_SZ, pDataW, cbNeeded);
185     ok(dwReturnCode == ERROR_ACCESS_DENIED, "SetPrinterDataExW returns %lu!\n", dwReturnCode);
186 
187     HeapFree(GetProcessHeap(), 0, pDataW);
188 }
189