1 /* 2 * PROJECT: ReactOS Local Spooler 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Main functions 5 * COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org) 6 */ 7 8 #include "precomp.h" 9 10 // Global Variables 11 HKEY hPrintKey = NULL; 12 HKEY hPrintersKey = NULL; 13 WCHAR wszJobDirectory[MAX_PATH]; 14 DWORD cchJobDirectory; 15 WCHAR wszSpoolDirectory[MAX_PATH]; 16 DWORD cchSpoolDirectory; 17 18 // Global Constants 19 #include <prtprocenv.h> 20 21 /** This is what the Spooler of Windows Server 2003 returns (for example using GetPrinterDataExW, SPLREG_MAJOR_VERSION/SPLREG_MINOR_VERSION) */ 22 const DWORD dwSpoolerMajorVersion = 3; 23 const DWORD dwSpoolerMinorVersion = 0; 24 25 const WCHAR wszDefaultDocumentName[] = L"Local Downlevel Document"; 26 27 PCWSTR wszPrintProviderInfo[3] = { 28 L"Windows NT Local Print Providor", // Name 29 L"Locally connected Printers", // Comment 30 L"Windows NT Local Printers" // Description 31 }; 32 33 // Local Constants 34 static const PRINTPROVIDOR _PrintProviderFunctions = { 35 LocalOpenPrinter, // fpOpenPrinter 36 LocalSetJob, // fpSetJob 37 LocalGetJob, // fpGetJob 38 LocalEnumJobs, // fpEnumJobs 39 NULL, // fpAddPrinter 40 NULL, // fpDeletePrinter 41 NULL, // fpSetPrinter 42 LocalGetPrinter, // fpGetPrinter 43 LocalEnumPrinters, // fpEnumPrinters 44 NULL, // fpAddPrinterDriver 45 NULL, // fpEnumPrinterDrivers 46 LocalGetPrinterDriver, // fpGetPrinterDriver 47 NULL, // fpGetPrinterDriverDirectory 48 NULL, // fpDeletePrinterDriver 49 NULL, // fpAddPrintProcessor 50 LocalEnumPrintProcessors, // fpEnumPrintProcessors 51 LocalGetPrintProcessorDirectory, // fpGetPrintProcessorDirectory 52 NULL, // fpDeletePrintProcessor 53 LocalEnumPrintProcessorDatatypes, // fpEnumPrintProcessorDatatypes 54 LocalStartDocPrinter, // fpStartDocPrinter 55 LocalStartPagePrinter, // fpStartPagePrinter 56 LocalWritePrinter, // fpWritePrinter 57 LocalEndPagePrinter, // fpEndPagePrinter 58 NULL, // fpAbortPrinter 59 LocalReadPrinter, // fpReadPrinter 60 LocalEndDocPrinter, // fpEndDocPrinter 61 LocalAddJob, // fpAddJob 62 LocalScheduleJob, // fpScheduleJob 63 LocalGetPrinterData, // fpGetPrinterData 64 LocalSetPrinterData, // fpSetPrinterData 65 NULL, // fpWaitForPrinterChange 66 LocalClosePrinter, // fpClosePrinter 67 NULL, // fpAddForm 68 NULL, // fpDeleteForm 69 NULL, // fpGetForm 70 NULL, // fpSetForm 71 NULL, // fpEnumForms 72 LocalEnumMonitors, // fpEnumMonitors 73 LocalEnumPorts, // fpEnumPorts 74 NULL, // fpAddPort 75 NULL, // fpConfigurePort 76 NULL, // fpDeletePort 77 NULL, // fpCreatePrinterIC 78 NULL, // fpPlayGdiScriptOnPrinterIC 79 NULL, // fpDeletePrinterIC 80 NULL, // fpAddPrinterConnection 81 NULL, // fpDeletePrinterConnection 82 NULL, // fpPrinterMessageBox 83 NULL, // fpAddMonitor 84 NULL, // fpDeleteMonitor 85 NULL, // fpResetPrinter 86 NULL, // fpGetPrinterDriverEx 87 NULL, // fpFindFirstPrinterChangeNotification 88 NULL, // fpFindClosePrinterChangeNotification 89 NULL, // fpAddPortEx 90 NULL, // fpShutDown 91 NULL, // fpRefreshPrinterChangeNotification 92 NULL, // fpOpenPrinterEx 93 NULL, // fpAddPrinterEx 94 NULL, // fpSetPort 95 NULL, // fpEnumPrinterData 96 NULL, // fpDeletePrinterData 97 NULL, // fpClusterSplOpen 98 NULL, // fpClusterSplClose 99 NULL, // fpClusterSplIsAlive 100 LocalSetPrinterDataEx, // fpSetPrinterDataEx 101 LocalGetPrinterDataEx, // fpGetPrinterDataEx 102 NULL, // fpEnumPrinterDataEx 103 NULL, // fpEnumPrinterKey 104 NULL, // fpDeletePrinterDataEx 105 NULL, // fpDeletePrinterKey 106 NULL, // fpSeekPrinter 107 NULL, // fpDeletePrinterDriverEx 108 NULL, // fpAddPerMachineConnection 109 NULL, // fpDeletePerMachineConnection 110 NULL, // fpEnumPerMachineConnections 111 NULL, // fpXcvData 112 NULL, // fpAddPrinterDriverEx 113 NULL, // fpSplReadPrinter 114 NULL, // fpDriverUnloadComplete 115 NULL, // fpGetSpoolFileInfo 116 NULL, // fpCommitSpoolData 117 NULL, // fpCloseSpoolFileHandle 118 NULL, // fpFlushPrinter 119 NULL, // fpSendRecvBidiData 120 NULL, // fpAddDriverCatalog 121 }; 122 123 static BOOL 124 _InitializeLocalSpooler(void) 125 { 126 const WCHAR wszPrintersPath[] = L"\\PRINTERS"; 127 const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1; 128 const WCHAR wszSpoolPath[] = L"\\spool"; 129 const DWORD cchSpoolPath = _countof(wszSpoolPath) - 1; 130 const WCHAR wszSymbolicLinkValue[] = L"\\REGISTRY\\MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers"; 131 const DWORD cbSymbolicLinkValue = sizeof(wszSymbolicLinkValue) - sizeof(WCHAR); 132 133 BOOL bReturnValue = FALSE; 134 DWORD cbData; 135 DWORD dwErrorCode; 136 HKEY hKey = NULL; 137 138 // On startup, always create a volatile symbolic link in the registry if it doesn't exist yet. 139 // "SYSTEM\CurrentControlSet\Control\Print\Printers" -> "SOFTWARE\Microsoft\Windows NT\CurrentVersion\Print\Printers" 140 // 141 // According to https://social.technet.microsoft.com/Forums/windowsserver/en-US/a683ab54-c43c-4ebe-af8f-1f7a65af2a51 142 // this is needed when having >900 printers to work around a size limit of the SYSTEM registry hive. 143 dwErrorCode = (DWORD)RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers", 0, NULL, REG_OPTION_VOLATILE | REG_OPTION_CREATE_LINK, KEY_CREATE_LINK | KEY_SET_VALUE, NULL, &hKey, NULL); 144 if (dwErrorCode == ERROR_SUCCESS) 145 { 146 // Note that wszSymbolicLink has to be stored WITHOUT the terminating null character for the symbolic link to work! 147 // See cbSymbolicLinkValue above. 148 dwErrorCode = (DWORD)RegSetValueExW(hKey, L"SymbolicLinkValue", 0, REG_LINK, (PBYTE)wszSymbolicLinkValue, cbSymbolicLinkValue); 149 if (dwErrorCode != ERROR_SUCCESS) 150 { 151 ERR("RegSetValueExW failed for the Printers symlink with error %lu!\n", dwErrorCode); 152 goto Cleanup; 153 } 154 } 155 else if (dwErrorCode != ERROR_ALREADY_EXISTS) 156 { 157 ERR("RegCreateKeyExW failed for the Printers symlink with error %lu!\n", dwErrorCode); 158 goto Cleanup; 159 } 160 161 // Open some registry keys and leave them open. We need them multiple times throughout the Local Spooler. 162 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Print", 0, KEY_ALL_ACCESS, &hPrintKey); 163 if (dwErrorCode != ERROR_SUCCESS) 164 { 165 ERR("RegOpenKeyExW failed for \"Print\" with error %lu!\n", dwErrorCode); 166 goto Cleanup; 167 } 168 169 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Print\\Printers", 0, KEY_ALL_ACCESS, &hPrintersKey); 170 if (dwErrorCode != ERROR_SUCCESS) 171 { 172 ERR("RegOpenKeyExW failed for \"Printers\" with error %lu!\n", dwErrorCode); 173 goto Cleanup; 174 } 175 176 // Construct the path to "%SystemRoot%\system32\spool". 177 // Forget about length checks here. If this doesn't fit into MAX_PATH, our OS has more serious problems... 178 cchSpoolDirectory = GetSystemDirectoryW(wszSpoolDirectory, MAX_PATH); 179 CopyMemory(&wszSpoolDirectory[cchSpoolDirectory], wszSpoolPath, (cchSpoolPath + 1) * sizeof(WCHAR)); 180 cchSpoolDirectory += cchSpoolPath; 181 182 // Query the job directory. 183 cbData = sizeof(wszJobDirectory); 184 dwErrorCode = (DWORD)RegQueryValueExW(hPrintersKey, SPLREG_DEFAULT_SPOOL_DIRECTORY, NULL, NULL, (PBYTE)wszJobDirectory, &cbData); 185 if (dwErrorCode == ERROR_SUCCESS) 186 { 187 cchJobDirectory = cbData / sizeof(WCHAR) - 1; 188 } 189 else if (dwErrorCode == ERROR_FILE_NOT_FOUND) 190 { 191 // Use the default "%SystemRoot%\system32\spool\PRINTERS". 192 CopyMemory(wszJobDirectory, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR)); 193 CopyMemory(&wszJobDirectory[cchSpoolDirectory], wszPrintersPath, (cchPrintersPath + 1) * sizeof(WCHAR)); 194 cchJobDirectory = cchSpoolDirectory + cchPrintersPath; 195 196 // Save this for next time. 197 RegSetValueExW(hPrintersKey, SPLREG_DEFAULT_SPOOL_DIRECTORY, 0, REG_SZ, (PBYTE)wszJobDirectory, (cchJobDirectory + 1) * sizeof(WCHAR)); 198 } 199 else 200 { 201 ERR("RegQueryValueExW failed for \"%S\" with error %lu!\n", SPLREG_DEFAULT_SPOOL_DIRECTORY, dwErrorCode); 202 goto Cleanup; 203 } 204 205 // Initialize all lists. 206 if (!InitializePrintMonitorList()) 207 goto Cleanup; 208 209 if (!InitializePortList()) 210 goto Cleanup; 211 212 if (!InitializePrintProcessorList()) 213 goto Cleanup; 214 215 if (!InitializePrinterList()) 216 goto Cleanup; 217 218 if (!InitializeGlobalJobList()) 219 goto Cleanup; 220 221 // Local Spooler Initialization finished successfully! 222 bReturnValue = TRUE; 223 224 Cleanup: 225 if (hKey) 226 RegCloseKey(hKey); 227 228 return bReturnValue; 229 } 230 231 BOOL WINAPI 232 DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) 233 { 234 switch (fdwReason) 235 { 236 case DLL_PROCESS_ATTACH: 237 DisableThreadLibraryCalls(hinstDLL); 238 return _InitializeLocalSpooler(); 239 240 default: 241 return TRUE; 242 } 243 } 244 245 BOOL WINAPI 246 InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor, DWORD cbPrintProvidor, LPWSTR pFullRegistryPath) 247 { 248 TRACE("InitializePrintProvidor(%p, %lu, %S)\n", pPrintProvidor, cbPrintProvidor, pFullRegistryPath); 249 250 CopyMemory(pPrintProvidor, &_PrintProviderFunctions, min(cbPrintProvidor, sizeof(PRINTPROVIDOR))); 251 252 SetLastError(ERROR_SUCCESS); 253 return TRUE; 254 } 255