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 LocalAddPrinterDriver, // fpAddPrinterDriver
45 LocalEnumPrinterDrivers, // fpEnumPrinterDrivers
46 LocalGetPrinterDriver, // fpGetPrinterDriver
47 LocalGetPrinterDriverDirectory, // 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 LocalAddForm, // fpAddForm
68 LocalDeleteForm, // fpDeleteForm
69 LocalGetForm, // fpGetForm
70 LocalSetForm, // fpSetForm
71 LocalEnumForms, // fpEnumForms
72 LocalEnumMonitors, // fpEnumMonitors
73 LocalEnumPorts, // fpEnumPorts
74 LocalAddPort, // fpAddPort
75 LocalConfigurePort, // fpConfigurePort
76 LocalDeletePort, // fpDeletePort
77 NULL, // fpCreatePrinterIC
78 NULL, // fpPlayGdiScriptOnPrinterIC
79 NULL, // fpDeletePrinterIC
80 NULL, // fpAddPrinterConnection
81 NULL, // fpDeletePrinterConnection
82 LocalPrinterMessageBox, // fpPrinterMessageBox
83 LocalAddMonitor, // fpAddMonitor
84 LocalDeleteMonitor, // fpDeleteMonitor
85 NULL, // fpResetPrinter
86 LocalGetPrinterDriverEx, // fpGetPrinterDriverEx
87 NULL, // fpFindFirstPrinterChangeNotification
88 NULL, // fpFindClosePrinterChangeNotification
89 LocalAddPortEx, // fpAddPortEx
90 NULL, // fpShutDown
91 NULL, // fpRefreshPrinterChangeNotification
92 NULL, // fpOpenPrinterEx
93 NULL, // fpAddPrinterEx
94 LocalSetPort, // 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 LocalXcvData, // fpXcvData
112 LocalAddPrinterDriverEx, // fpAddPrinterDriverEx
113 NULL, // fpSplReadPrinter
114 NULL, // fpDriverUnloadComplete
115 LocalGetSpoolFileInfo, // fpGetSpoolFileInfo
116 LocalCommitSpoolData, // fpCommitSpoolData
117 LocalCloseSpoolFileHandle, // fpCloseSpoolFileHandle
118 NULL, // fpFlushPrinter
119 NULL, // fpSendRecvBidiData
120 NULL, // fpAddDriverCatalog
121 };
122
123 static BOOL
_InitializeLocalSpooler(void)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 if (!InitializeFormList())
222 goto Cleanup;
223
224 if (!InitializePrinterDrivers())
225 goto Cleanup;
226
227 // Local Spooler Initialization finished successfully!
228 bReturnValue = TRUE;
229
230 Cleanup:
231 if (hKey)
232 RegCloseKey(hKey);
233
234 return bReturnValue;
235 }
236
237 BOOL WINAPI
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved)238 DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
239 {
240 switch (fdwReason)
241 {
242 case DLL_PROCESS_ATTACH:
243 DisableThreadLibraryCalls(hinstDLL);
244 return _InitializeLocalSpooler();
245
246 default:
247 return TRUE;
248 }
249 }
250
251 BOOL WINAPI
InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor,DWORD cbPrintProvidor,LPWSTR pFullRegistryPath)252 InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor, DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
253 {
254 TRACE("InitializePrintProvidor(%p, %lu, %S)\n", pPrintProvidor, cbPrintProvidor, pFullRegistryPath);
255
256 CopyMemory(pPrintProvidor, &_PrintProviderFunctions, min(cbPrintProvidor, sizeof(PRINTPROVIDOR)));
257
258 SetLastError(ERROR_SUCCESS);
259 return TRUE;
260 }
261