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  #if 0 // FIXME: fails on WHS testbot with ERROR_INVALID_PARAMETER
60      { "DNSMachineName", L"DNSMachineName", REG_SZ, 0xFFFFFFFF, FALSE },
61  #endif
62      { "AllowUserManageForms", L"AllowUserManageForms", REG_DWORD, 4, TRUE },
63      { NULL, NULL, 0, 0, FALSE }
64  };
65  
66  START_TEST(GetPrinterData)
67  {
68      DWORD cbNeeded;
69      DWORD cchDefaultPrinter;
70      DWORD dwReturnCode;
71      DWORD dwType;
72      HANDLE hPrinter;
73      PBYTE pDataA;
74      PBYTE pDataW;
75      PSPLREG_VALUE p;
76      WCHAR wszDefaultPrinter[MAX_PRINTER_NAME + 1];
77  
78      // Don't supply any parameters, this has to fail with ERROR_INVALID_HANDLE!
79      dwReturnCode = GetPrinterDataExW(NULL, NULL, NULL, NULL, NULL, 0, NULL);
80      ok(dwReturnCode == ERROR_INVALID_HANDLE, "GetPrinterDataExW returns error %lu!\n", dwReturnCode);
81  
82      // Open a handle to the local print server.
83      if (!OpenPrinterW(NULL, &hPrinter, NULL))
84      {
85          skip("Could not retrieve a handle to the local print server!\n");
86          return;
87      }
88  
89      // Now try with valid handle, but leave remaining parameters NULL.
90      dwReturnCode = GetPrinterDataExW(hPrinter, NULL, NULL, NULL, NULL, 0, NULL);
91      ok(dwReturnCode == RPC_X_NULL_REF_POINTER, "GetPrinterDataExW returns error %lu!\n", dwReturnCode);
92  
93      // Try all valid Print Server data values.
94      for (p = SplRegValues; p->pszName; p++)
95      {
96          // Try the ANSI version of the function.
97          dwType = 0xDEADBEEF;
98          dwReturnCode = GetPrinterDataExA(hPrinter, NULL, p->pszName, &dwType, NULL, 0, &cbNeeded);
99          ok(dwReturnCode == ERROR_MORE_DATA || dwReturnCode == ERROR_FILE_NOT_FOUND, "GetPrinterDataExA returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
100          if (dwReturnCode != ERROR_MORE_DATA)
101              continue;
102  
103          ok(dwType == p->dwType, "dwType is %lu for \"%s\"!\n", dwType, p->pszName);
104  
105          if (p->cbNeededA < 0xFFFFFFFF)
106              ok(cbNeeded == p->cbNeededA, "cbNeeded is %lu for \"%s\", but expected %lu!\n", cbNeeded, p->pszName, p->cbNeededA);
107          else
108              ok(cbNeeded > 0, "cbNeeded is 0 for \"%s\"!\n", p->pszName);
109  
110          pDataA = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
111          dwReturnCode = GetPrinterDataExA(hPrinter, NULL, p->pszName, NULL, pDataA, cbNeeded, &cbNeeded);
112          ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExA returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
113  
114          // Try the Unicode version of the function too.
115          dwType = 0xDEADBEEF;
116          dwReturnCode = GetPrinterDataExW(hPrinter, NULL, p->pwszName, &dwType, NULL, 0, &cbNeeded);
117          ok(dwReturnCode == ERROR_MORE_DATA, "GetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
118          ok(dwType == p->dwType, "dwType is %lu for \"%s\"!\n", dwType, p->pszName);
119  
120          pDataW = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
121          dwReturnCode = GetPrinterDataExW(hPrinter, NULL, p->pwszName, NULL, pDataW, cbNeeded, &cbNeeded);
122          ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
123  
124          // Verify that OSVERSIONINFO structures are correctly returned.
125          if (strcmp(p->pszName, "OSVersion") == 0)
126          {
127              POSVERSIONINFOA pOSVersionInfoA = (POSVERSIONINFOA)pDataA;
128              POSVERSIONINFOW pOSVersionInfoW = (POSVERSIONINFOW)pDataW;
129              ok(pOSVersionInfoA->dwOSVersionInfoSize == sizeof(OSVERSIONINFOA), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoA->dwOSVersionInfoSize);
130              ok(pOSVersionInfoW->dwOSVersionInfoSize == sizeof(OSVERSIONINFOW), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoW->dwOSVersionInfoSize);
131          }
132          else if (strcmp(p->pszName, "OSVersionEx") == 0)
133          {
134              POSVERSIONINFOEXA pOSVersionInfoA = (POSVERSIONINFOEXA)pDataA;
135              POSVERSIONINFOEXW pOSVersionInfoW = (POSVERSIONINFOEXW)pDataW;
136              ok(pOSVersionInfoA->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXA), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoA->dwOSVersionInfoSize);
137              ok(pOSVersionInfoW->dwOSVersionInfoSize == sizeof(OSVERSIONINFOEXW), "dwOSVersionInfoSize is %lu!\n", pOSVersionInfoW->dwOSVersionInfoSize);
138          }
139  
140          // Shortly test SetPrinterDataExW by setting the same data we just retrieved.
141          if (p->bSettable)
142          {
143              dwReturnCode = SetPrinterDataExW(hPrinter, NULL, p->pwszName, dwType, pDataW, cbNeeded);
144              ok(dwReturnCode == ERROR_SUCCESS, "SetPrinterDataExW returns %lu for \"%s\"!\n", dwReturnCode, p->pszName);
145          }
146  
147          HeapFree(GetProcessHeap(), 0, pDataA);
148          HeapFree(GetProcessHeap(), 0, pDataW);
149      }
150  
151      // Try an invalid one.
152      dwReturnCode = GetPrinterDataExW(hPrinter, NULL, L"Invalid", NULL, NULL, 0, &cbNeeded);
153      ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
154  
155      ClosePrinter(hPrinter);
156  
157      // Open a handle to the default printer.
158      cchDefaultPrinter = _countof(wszDefaultPrinter);
159      ok(GetDefaultPrinterW(wszDefaultPrinter, &cchDefaultPrinter), "GetDefaultPrinterW returns FALSE and requires %lu characters!\n", cchDefaultPrinter);
160      if (!OpenPrinterW(wszDefaultPrinter, &hPrinter, NULL))
161      {
162          skip("Could not retrieve a handle to the default printer!\n");
163          return;
164      }
165  
166      // Using NULL or L"" for pKeyName on a Printer handle yields ERROR_INVALID_PARAMETER.
167      dwReturnCode = GetPrinterDataExW(hPrinter, NULL, L"Name", NULL, NULL, 0, &cbNeeded);
168      ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
169      dwReturnCode = GetPrinterDataExW(hPrinter, L"", L"Name", NULL, NULL, 0, &cbNeeded);
170      ok(dwReturnCode == ERROR_INVALID_PARAMETER, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
171  
172      // Using L"\\" allows us to examine the contents of the main printer key anyway.
173      dwReturnCode = GetPrinterDataExW(hPrinter, L"\\", L"Name", &dwType, NULL, 0, &cbNeeded);
174      ok(dwReturnCode == ERROR_MORE_DATA, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
175      ok(dwType == REG_SZ, "dwType is %lu!\n", dwType);
176      ok(cbNeeded > 0, "cbNeeded is 0!\n");
177  
178      pDataW = HeapAlloc(GetProcessHeap(), 0, cbNeeded);
179      dwReturnCode = GetPrinterDataExW(hPrinter, L"\\", L"Name", NULL, pDataW, cbNeeded, &cbNeeded);
180      ok(dwReturnCode == ERROR_SUCCESS, "GetPrinterDataExW returns %lu!\n", dwReturnCode);
181  
182      // The following test fails if the default printer is a remote printer.
183      ok(wcscmp((PWSTR)pDataW, wszDefaultPrinter) == 0, "pDataW is \"%S\", default printer is \"%S\"!\n", (PWSTR)pDataW, wszDefaultPrinter);
184  
185      // SetPrinterDataExW should return ERROR_ACCESS_DENIED when attempting to set the Name.
186      dwReturnCode = SetPrinterDataExW(hPrinter, L"\\", L"Name", REG_SZ, pDataW, cbNeeded);
187      ok(dwReturnCode == ERROR_ACCESS_DENIED, "SetPrinterDataExW returns %lu!\n", dwReturnCode);
188  
189      HeapFree(GetProcessHeap(), 0, pDataW);
190  }
191