2 * PROJECT: ReactOS Local Spooler
3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4 * PURPOSE: Functions related to Printer Configuration Data
5 * COPYRIGHT: Copyright 2017 Colin Finck (colin@reactos.org)
11 LocalGetPrinterData(HANDLE hPrinter
, PWSTR pValueName
, PDWORD pType
, PBYTE pData
, DWORD nSize
, PDWORD pcbNeeded
)
13 TRACE("LocalGetPrinterData(%p, %S, %p, %p, %lu, %p)\n", hPrinter
, pValueName
, pType
, pData
, nSize
, pcbNeeded
);
15 // The ReactOS Printing Stack forwards all GetPrinterData calls to GetPrinterDataEx as soon as possible.
16 // This function may only be called if localspl.dll is used together with Windows Printing Stack components.
17 WARN("This function should never be called!\n");
18 return LocalGetPrinterDataEx(hPrinter
, L
"PrinterDriverData", pValueName
, pType
, pData
, nSize
, pcbNeeded
);
22 _MakePrinterSubKey(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PCWSTR pKeyName
, PWSTR
* ppwszSubKey
)
24 const WCHAR wszBackslash
[] = L
"\\";
30 if (!pKeyName
|| !*pKeyName
)
31 return ERROR_INVALID_PARAMETER
;
33 // Allocate a buffer for the subkey "PrinterName\KeyName".
34 cbSubKey
= (wcslen(pPrinterHandle
->pPrinter
->pwszPrinterName
) + 1 + wcslen(pKeyName
) + 1) * sizeof(WCHAR
);
35 *ppwszSubKey
= DllAllocSplMem(cbSubKey
);
37 return ERROR_NOT_ENOUGH_MEMORY
;
39 // Concatenate the subkey.
41 StringCbCopyExW(p
, cbSubKey
, pPrinterHandle
->pPrinter
->pwszPrinterName
, &p
, &cbSubKey
, 0);
42 StringCbCopyExW(p
, cbSubKey
, wszBackslash
, &p
, &cbSubKey
, 0);
43 StringCbCopyExW(p
, cbSubKey
, pKeyName
, &p
, &cbSubKey
, 0);
49 _LocalGetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PCWSTR pKeyName
, PCWSTR pValueName
, PDWORD pType
, PBYTE pData
, DWORD nSize
, PDWORD pcbNeeded
)
53 PWSTR pwszSubKey
= NULL
;
55 dwErrorCode
= _MakePrinterSubKey(pPrinterHandle
, pKeyName
, &pwszSubKey
);
56 if (dwErrorCode
!= ERROR_SUCCESS
)
60 dwErrorCode
= (DWORD
)RegOpenKeyExW(hPrintersKey
, pwszSubKey
, 0, KEY_READ
, &hKey
);
61 if (dwErrorCode
!= ERROR_SUCCESS
)
63 ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey
, dwErrorCode
);
67 // Query the desired value.
69 dwErrorCode
= (DWORD
)RegQueryValueExW(hKey
, pValueName
, NULL
, pType
, pData
, pcbNeeded
);
76 DllFreeSplMem(pwszSubKey
);
82 _LocalGetPrintServerHandleData(PCWSTR pValueName
, PDWORD pType
, PBYTE pData
, DWORD nSize
, PDWORD pcbNeeded
)
86 if (wcsicmp(pValueName
, SPLREG_DEFAULT_SPOOL_DIRECTORY
) == 0 ||
87 wcsicmp(pValueName
, SPLREG_PORT_THREAD_PRIORITY
) == 0 ||
88 wcsicmp(pValueName
, SPLREG_SCHEDULER_THREAD_PRIORITY
) == 0 ||
89 wcsicmp(pValueName
, SPLREG_BEEP_ENABLED
) == 0 ||
90 wcsicmp(pValueName
, SPLREG_ALLOW_USER_MANAGEFORMS
) == 0)
93 return (DWORD
)RegQueryValueExW(hPrintersKey
, pValueName
, NULL
, pType
, pData
, pcbNeeded
);
95 else if (wcsicmp(pValueName
, SPLREG_PORT_THREAD_PRIORITY_DEFAULT
) == 0 ||
96 wcsicmp(pValueName
, SPLREG_SCHEDULER_THREAD_PRIORITY_DEFAULT
) == 0)
98 // Store a DWORD value as REG_NONE.
100 *pcbNeeded
= sizeof(DWORD
);
101 if (nSize
< *pcbNeeded
)
102 return ERROR_MORE_DATA
;
104 // Apparently, these values don't serve a purpose anymore.
105 *((PDWORD
)pData
) = 0;
106 return ERROR_SUCCESS
;
108 else if (wcsicmp(pValueName
, SPLREG_NET_POPUP
) == 0 ||
109 wcsicmp(pValueName
, SPLREG_RETRY_POPUP
) == 0 ||
110 wcsicmp(pValueName
, SPLREG_NET_POPUP_TO_COMPUTER
) == 0 ||
111 wcsicmp(pValueName
, SPLREG_EVENT_LOG
) == 0 ||
112 wcsicmp(pValueName
, SPLREG_RESTART_JOB_ON_POOL_ERROR
) == 0 ||
113 wcsicmp(pValueName
, SPLREG_RESTART_JOB_ON_POOL_ENABLED
) == 0)
117 dwErrorCode
= (DWORD
)RegOpenKeyExW(hPrintKey
, L
"Providers", 0, KEY_READ
, &hKey
);
118 if (dwErrorCode
!= ERROR_SUCCESS
)
120 ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode
);
125 dwErrorCode
= (DWORD
)RegQueryValueExW(hKey
, pValueName
, NULL
, pType
, pData
, pcbNeeded
);
129 else if (wcsicmp(pValueName
, SPLREG_MAJOR_VERSION
) == 0)
131 // Store a DWORD value as REG_NONE.
133 *pcbNeeded
= sizeof(DWORD
);
134 if (nSize
< *pcbNeeded
)
135 return ERROR_MORE_DATA
;
137 // Apparently, these values don't serve a purpose anymore.
138 *((PDWORD
)pData
) = dwSpoolerMajorVersion
;
139 return ERROR_SUCCESS
;
141 else if (wcsicmp(pValueName
, SPLREG_MINOR_VERSION
) == 0)
143 // Store a DWORD value as REG_NONE.
145 *pcbNeeded
= sizeof(DWORD
);
146 if (nSize
< *pcbNeeded
)
147 return ERROR_MORE_DATA
;
149 // Apparently, these values don't serve a purpose anymore.
150 *((PDWORD
)pData
) = dwSpoolerMinorVersion
;
151 return ERROR_SUCCESS
;
153 else if (wcsicmp(pValueName
, SPLREG_ARCHITECTURE
) == 0)
155 // Store a string as REG_NONE with the length of the environment name string.
157 *pcbNeeded
= cbCurrentEnvironment
;
158 if (nSize
< *pcbNeeded
)
159 return ERROR_MORE_DATA
;
161 // Copy the environment name as the output value for SPLREG_ARCHITECTURE.
162 CopyMemory(pData
, wszCurrentEnvironment
, cbCurrentEnvironment
);
163 return ERROR_SUCCESS
;
165 else if (wcsicmp(pValueName
, SPLREG_OS_VERSION
) == 0)
167 POSVERSIONINFOW pInfo
= (POSVERSIONINFOW
)pData
;
169 // Store the OSVERSIONINFOW structure as REG_NONE.
171 *pcbNeeded
= sizeof(OSVERSIONINFOW
);
172 if (nSize
< *pcbNeeded
)
173 return ERROR_MORE_DATA
;
175 // Return OS version information.
176 pInfo
->dwOSVersionInfoSize
= sizeof(OSVERSIONINFOW
);
177 GetVersionExW(pInfo
);
178 return ERROR_SUCCESS
;
180 else if (wcsicmp(pValueName
, SPLREG_OS_VERSIONEX
) == 0)
182 POSVERSIONINFOEXW pInfo
= (POSVERSIONINFOEXW
)pData
;
184 // Store the OSVERSIONINFOEXW structure as REG_NONE.
186 *pcbNeeded
= sizeof(OSVERSIONINFOEXW
);
187 if (nSize
< *pcbNeeded
)
188 return ERROR_MORE_DATA
;
190 // Return extended OS version information.
191 pInfo
->dwOSVersionInfoSize
= sizeof(OSVERSIONINFOEXW
);
192 GetVersionExW((POSVERSIONINFOW
)pInfo
);
193 return ERROR_SUCCESS
;
195 else if (wcsicmp(pValueName
, SPLREG_DS_PRESENT
) == 0)
197 PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pInfo
;
199 // We want to store a REG_DWORD value.
201 *pcbNeeded
= sizeof(DWORD
);
202 if (nSize
< *pcbNeeded
)
203 return ERROR_MORE_DATA
;
205 // Get information about the domain membership of this computer.
206 dwErrorCode
= DsRoleGetPrimaryDomainInformation(NULL
, DsRolePrimaryDomainInfoBasic
, (PBYTE
*)&pInfo
);
207 if (dwErrorCode
!= ERROR_SUCCESS
)
209 ERR("DsRoleGetPrimaryDomainInformation failed with error %lu!\n", dwErrorCode
);
213 // Return whether this computer is a workstation or server inside a domain.
214 *((PDWORD
)pData
) = (pInfo
->MachineRole
== DsRole_RoleMemberWorkstation
|| pInfo
->MachineRole
== DsRole_RoleMemberServer
);
215 DsRoleFreeMemory(pInfo
);
216 return ERROR_SUCCESS
;
218 else if (wcsicmp(pValueName
, SPLREG_DS_PRESENT_FOR_USER
) == 0)
222 WCHAR wszComputerName
[MAX_COMPUTERNAME_LENGTH
+ 1];
223 WCHAR wszUserSam
[UNLEN
+ 1];
225 // We want to store a REG_DWORD value.
227 *pcbNeeded
= sizeof(DWORD
);
228 if (nSize
< *pcbNeeded
)
229 return ERROR_MORE_DATA
;
231 // Get the local Computer Name.
232 cch
= MAX_COMPUTERNAME_LENGTH
+ 1;
233 if (!GetComputerNameW(wszComputerName
, &cch
))
235 dwErrorCode
= GetLastError();
236 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode
);
240 // Get the User Name in the SAM format.
241 // This could either be:
245 if (!GetUserNameExW(NameSamCompatible
, wszUserSam
, &cch
))
247 dwErrorCode
= GetLastError();
248 ERR("GetUserNameExW failed with error %lu!\n", dwErrorCode
);
252 // Terminate the SAM-formatted User Name at the backslash.
253 p
= wcschr(wszUserSam
, L
'\\');
256 // Compare it with the Computer Name.
257 // If they differ, this User is part of a domain.
258 *((PDWORD
)pData
) = (wcscmp(wszUserSam
, wszComputerName
) != 0);
259 return ERROR_SUCCESS
;
261 else if (wcsicmp(pValueName
, SPLREG_REMOTE_FAX
) == 0)
263 // Store a DWORD value as REG_NONE.
265 *pcbNeeded
= sizeof(DWORD
);
266 if (nSize
< *pcbNeeded
)
267 return ERROR_MORE_DATA
;
269 // TODO: We don't support any fax service yet, but let's return the same value as Windows Server 2003 here.
270 *((PDWORD
)pData
) = 1;
271 return ERROR_SUCCESS
;
273 else if (wcsicmp(pValueName
, SPLREG_DNS_MACHINE_NAME
) == 0)
275 DWORD cchDnsName
= 0;
277 // Get the length of the fully-qualified computer DNS name.
278 GetComputerNameExW(ComputerNameDnsFullyQualified
, NULL
, &cchDnsName
);
279 dwErrorCode
= GetLastError();
280 if (dwErrorCode
!= ERROR_MORE_DATA
)
282 ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode
);
286 // Check if our supplied buffer is large enough.
288 *pcbNeeded
= cchDnsName
* sizeof(WCHAR
);
289 if (nSize
< *pcbNeeded
)
290 return ERROR_MORE_DATA
;
292 // Get the actual DNS name.
293 if (!GetComputerNameExW(ComputerNameDnsFullyQualified
, (PWSTR
)pData
, &cchDnsName
))
295 dwErrorCode
= GetLastError();
296 ERR("GetComputerNameExW failed with error %lu!\n", dwErrorCode
);
300 // Lowercase the output just like Windows does.
301 _wcslwr((PWSTR
)pData
);
302 return ERROR_SUCCESS
;
306 // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
307 // That also includes SPLREG_WEBSHAREMGMT, which is supported in Windows Server 2003 according to the documentation,
308 // but is actually not!
309 return ERROR_INVALID_PARAMETER
;
314 LocalGetPrinterDataEx(HANDLE hPrinter
, PCWSTR pKeyName
, PCWSTR pValueName
, PDWORD pType
, PBYTE pData
, DWORD nSize
, PDWORD pcbNeeded
)
319 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
321 TRACE("LocalGetPrinterDataEx(%p, %S, %S, %p, %p, %lu, %p)\n", hPrinter
, pKeyName
, pValueName
, pType
, pData
, nSize
, pcbNeeded
);
323 // Even if GetPrinterDataExW in winspool ensures that the RPC function is never called without a valid pointer for pType,
324 // it's officially optional. Windows' fpGetPrinterDataEx also works with NULL for pType!
325 // Ensure here that it is always set to simplify the code later.
329 // pData is later fed to RegQueryValueExW in many cases. When calling it with zero buffer size, RegQueryValueExW returns a
330 // different error code based on whether pData is NULL or something else.
331 // Ensure here that ERROR_MORE_DATA is always returned.
337 dwErrorCode
= ERROR_INVALID_HANDLE
;
341 dwErrorCode
= ERROR_INVALID_PARAMETER
;
343 else if (pHandle
->HandleType
== HandleType_Printer
)
345 dwErrorCode
= _LocalGetPrinterHandleData(pHandle
->pSpecificHandle
, pKeyName
, pValueName
, pType
, pData
, nSize
, pcbNeeded
);
347 else if (pHandle
->HandleType
== HandleType_PrintServer
)
349 dwErrorCode
= _LocalGetPrintServerHandleData(pValueName
, pType
, pData
, nSize
, pcbNeeded
);
353 dwErrorCode
= ERROR_INVALID_HANDLE
;
356 SetLastError(dwErrorCode
);
361 LocalSetPrinterData(HANDLE hPrinter
, PWSTR pValueName
, DWORD Type
, PBYTE pData
, DWORD cbData
)
363 TRACE("LocalSetPrinterData(%p, %S, %lu, %p, %lu)\n", hPrinter
, pValueName
, Type
, pData
, cbData
);
365 // The ReactOS Printing Stack forwards all SetPrinterData calls to SetPrinterDataEx as soon as possible.
366 // This function may only be called if localspl.dll is used together with Windows Printing Stack components.
367 WARN("This function should never be called!\n");
368 return LocalSetPrinterDataEx(hPrinter
, L
"PrinterDriverData", pValueName
, Type
, pData
, cbData
);
372 _LocalSetPrinterHandleData(PLOCAL_PRINTER_HANDLE pPrinterHandle
, PCWSTR pKeyName
, PCWSTR pValueName
, DWORD Type
, PBYTE pData
, DWORD cbData
)
376 PWSTR pwszSubKey
= NULL
;
378 dwErrorCode
= _MakePrinterSubKey(pPrinterHandle
, pKeyName
, &pwszSubKey
);
379 if (dwErrorCode
!= ERROR_SUCCESS
)
383 dwErrorCode
= (DWORD
)RegOpenKeyExW(hPrintersKey
, pwszSubKey
, 0, KEY_SET_VALUE
, &hKey
);
384 if (dwErrorCode
!= ERROR_SUCCESS
)
386 ERR("RegOpenKeyExW failed for \"%S\" with error %lu!\n", pwszSubKey
, dwErrorCode
);
391 dwErrorCode
= (DWORD
)RegSetValueExW(hKey
, pValueName
, 0, Type
, pData
, cbData
);
398 DllFreeSplMem(pwszSubKey
);
404 _LocalSetPrintServerHandleData(PCWSTR pValueName
, DWORD Type
, PBYTE pData
, DWORD cbData
)
408 if (wcsicmp(pValueName
, SPLREG_DEFAULT_SPOOL_DIRECTORY
) == 0 ||
409 wcsicmp(pValueName
, SPLREG_PORT_THREAD_PRIORITY
) == 0 ||
410 wcsicmp(pValueName
, SPLREG_SCHEDULER_THREAD_PRIORITY
) == 0 ||
411 wcsicmp(pValueName
, SPLREG_BEEP_ENABLED
) == 0 ||
412 wcsicmp(pValueName
, SPLREG_ALLOW_USER_MANAGEFORMS
) == 0)
414 return (DWORD
)RegSetValueExW(hPrintersKey
, pValueName
, 0, Type
, pData
, cbData
);
416 else if (wcsicmp(pValueName
, SPLREG_NET_POPUP
) == 0 ||
417 wcsicmp(pValueName
, SPLREG_RETRY_POPUP
) == 0 ||
418 wcsicmp(pValueName
, SPLREG_NET_POPUP_TO_COMPUTER
) == 0 ||
419 wcsicmp(pValueName
, SPLREG_EVENT_LOG
) == 0 ||
420 wcsicmp(pValueName
, SPLREG_RESTART_JOB_ON_POOL_ERROR
) == 0 ||
421 wcsicmp(pValueName
, SPLREG_RESTART_JOB_ON_POOL_ENABLED
) == 0 ||
422 wcsicmp(pValueName
, L
"NoRemotePrinterDrivers") == 0)
426 dwErrorCode
= (DWORD
)RegOpenKeyExW(hPrintKey
, L
"Providers", 0, KEY_SET_VALUE
, &hKey
);
427 if (dwErrorCode
!= ERROR_SUCCESS
)
429 ERR("RegOpenKeyExW failed for \"Providers\" with error %lu!\n", dwErrorCode
);
433 dwErrorCode
= (DWORD
)RegSetValueExW(hKey
, pValueName
, 0, Type
, pData
, cbData
);
437 else if (wcsicmp(pValueName
, SPLREG_WEBSHAREMGMT
) == 0)
439 WARN("Attempting to set WebShareMgmt, which is based on IIS and therefore not supported. Returning fake success!\n");
440 return ERROR_SUCCESS
;
444 // For all other, unknown settings, we just return ERROR_INVALID_PARAMETER.
445 return ERROR_INVALID_PARAMETER
;
450 LocalSetPrinterDataEx(HANDLE hPrinter
, PCWSTR pKeyName
, PCWSTR pValueName
, DWORD Type
, PBYTE pData
, DWORD cbData
)
453 PLOCAL_HANDLE pHandle
= (PLOCAL_HANDLE
)hPrinter
;
455 TRACE("LocalSetPrinterDataEx(%p, %S, %S, %lu, %p, %lu)\n", hPrinter
, pKeyName
, pValueName
, Type
, pData
, cbData
);
459 dwErrorCode
= ERROR_INVALID_HANDLE
;
461 else if (pHandle
->HandleType
== HandleType_Printer
)
463 dwErrorCode
= _LocalSetPrinterHandleData(pHandle
->pSpecificHandle
, pKeyName
, pValueName
, Type
, pData
, cbData
);
465 else if (pHandle
->HandleType
== HandleType_PrintServer
)
467 dwErrorCode
= _LocalSetPrintServerHandleData(pValueName
, Type
, pData
, cbData
);
471 dwErrorCode
= ERROR_INVALID_HANDLE
;
474 SetLastError(dwErrorCode
);