[LOCALSPL]
[reactos.git] / reactos / win32ss / printing / providers / localspl / monitors.c
1 /*
2 * PROJECT: ReactOS Local Spooler
3 * LICENSE: GNU LGPL v2.1 or any later version as published by the Free Software Foundation
4 * PURPOSE: Functions related to Print Monitors
5 * COPYRIGHT: Copyright 2015 Colin Finck <colin@reactos.org>
6 */
7
8 #include "precomp.h"
9
10 // Global Variables
11 LIST_ENTRY PrintMonitorList;
12
13
14 PLOCAL_PRINT_MONITOR
15 FindPrintMonitor(PCWSTR pwszName)
16 {
17 PLIST_ENTRY pEntry;
18 PLOCAL_PRINT_MONITOR pPrintMonitor;
19
20 for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
21 {
22 pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
23
24 if (_wcsicmp(pPrintMonitor->pwszName, pwszName) == 0)
25 return pPrintMonitor;
26 }
27
28 return NULL;
29 }
30
31 BOOL
32 InitializePrintMonitorList()
33 {
34 const WCHAR wszMonitorsPath[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Monitors";
35 const DWORD cchMonitorsPath = _countof(wszMonitorsPath) - 1;
36
37 DWORD cchMaxSubKey;
38 DWORD cchPrintMonitorName;
39 DWORD dwErrorCode;
40 DWORD dwSubKeys;
41 DWORD i;
42 HINSTANCE hinstPrintMonitor = NULL;
43 HKEY hKey = NULL;
44 HKEY hSubKey = NULL;
45 MONITORINIT MonitorInit;
46 PInitializePrintMonitor pfnInitializePrintMonitor;
47 PInitializePrintMonitor2 pfnInitializePrintMonitor2;
48 PLOCAL_PRINT_MONITOR pPrintMonitor = NULL;
49 PWSTR pwszRegistryPath = NULL;
50
51 // Initialize an empty list for our Print Monitors.
52 InitializeListHead(&PrintMonitorList);
53
54 // Open the key containing Print Monitors.
55 dwErrorCode = (DWORD)RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszMonitorsPath, 0, KEY_READ, &hKey);
56 if (dwErrorCode != ERROR_SUCCESS)
57 {
58 ERR("RegOpenKeyExW failed with status %lu!\n", dwErrorCode);
59 goto Cleanup;
60 }
61
62 // Get the number of Print Providers and maximum sub key length.
63 dwErrorCode = (DWORD)RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &dwSubKeys, &cchMaxSubKey, NULL, NULL, NULL, NULL, NULL, NULL);
64 if (dwErrorCode != ERROR_SUCCESS)
65 {
66 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode);
67 goto Cleanup;
68 }
69
70 // Loop through all available Print Providers.
71 for (i = 0; i < dwSubKeys; i++)
72 {
73 // Cleanup tasks from the previous run
74 if (hSubKey)
75 {
76 RegCloseKey(hSubKey);
77 hSubKey = NULL;
78 }
79
80 if (pwszRegistryPath)
81 {
82 DllFreeSplMem(pwszRegistryPath);
83 pwszRegistryPath = NULL;
84 }
85
86 if (pPrintMonitor)
87 {
88 if (pPrintMonitor->pwszFileName)
89 DllFreeSplMem(pPrintMonitor->pwszFileName);
90
91 if (pPrintMonitor->pwszName)
92 DllFreeSplMem(pPrintMonitor->pwszName);
93
94 DllFreeSplMem(pPrintMonitor);
95 pPrintMonitor = NULL;
96 }
97
98 // Create a new LOCAL_PRINT_MONITOR structure for it.
99 pPrintMonitor = DllAllocSplMem(sizeof(LOCAL_PRINT_MONITOR));
100 if (!pPrintMonitor)
101 {
102 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
103 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
104 goto Cleanup;
105 }
106
107 // Allocate memory for the Print Monitor Name.
108 pPrintMonitor->pwszName = DllAllocSplMem((cchMaxSubKey + 1) * sizeof(WCHAR));
109 if (!pPrintMonitor->pwszName)
110 {
111 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
112 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
113 goto Cleanup;
114 }
115
116 // Get the name of this Print Monitor.
117 cchPrintMonitorName = cchMaxSubKey + 1;
118 dwErrorCode = (DWORD)RegEnumKeyExW(hKey, i, pPrintMonitor->pwszName, &cchPrintMonitorName, NULL, NULL, NULL, NULL);
119 if (dwErrorCode != ERROR_SUCCESS)
120 {
121 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode);
122 continue;
123 }
124
125 // Open this Print Monitor's registry key.
126 dwErrorCode = (DWORD)RegOpenKeyExW(hKey, pPrintMonitor->pwszName, 0, KEY_READ, &hSubKey);
127 if (dwErrorCode != ERROR_SUCCESS)
128 {
129 ERR("RegOpenKeyExW failed for Print Provider \"%S\" with status %lu!\n", pPrintMonitor->pwszName, dwErrorCode);
130 continue;
131 }
132
133 // Get the file name of the Print Monitor.
134 pPrintMonitor->pwszFileName = AllocAndRegQueryWSZ(hSubKey, L"Driver");
135 if (!pPrintMonitor->pwszFileName)
136 continue;
137
138 // Try to load it.
139 hinstPrintMonitor = LoadLibraryW(pPrintMonitor->pwszFileName);
140 if (!hinstPrintMonitor)
141 {
142 ERR("LoadLibraryW failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
143 continue;
144 }
145
146 // Try to find a Level 2 initialization routine first.
147 pfnInitializePrintMonitor2 = (PInitializePrintMonitor2)GetProcAddress(hinstPrintMonitor, "InitializePrintMonitor2");
148 if (pfnInitializePrintMonitor2)
149 {
150 // Prepare a MONITORINIT structure.
151 MonitorInit.cbSize = sizeof(MONITORINIT);
152 MonitorInit.bLocal = TRUE;
153
154 // TODO: Fill the other fields.
155
156 // Call the Level 2 initialization routine.
157 pPrintMonitor->pMonitor = (PMONITOR2)pfnInitializePrintMonitor2(&MonitorInit, &pPrintMonitor->hMonitor);
158 if (!pPrintMonitor->pMonitor)
159 {
160 ERR("InitializePrintMonitor2 failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
161 continue;
162 }
163
164 pPrintMonitor->bIsLevel2 = TRUE;
165 }
166 else
167 {
168 // Try to find a Level 1 initialization routine then.
169 pfnInitializePrintMonitor = (PInitializePrintMonitor)GetProcAddress(hinstPrintMonitor, "InitializePrintMonitor");
170 if (pfnInitializePrintMonitor)
171 {
172 // Construct the registry path.
173 pwszRegistryPath = DllAllocSplMem((cchMonitorsPath + 1 + cchPrintMonitorName + 1) * sizeof(WCHAR));
174 if (!pwszRegistryPath)
175 {
176 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY;
177 ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
178 goto Cleanup;
179 }
180
181 CopyMemory(pwszRegistryPath, wszMonitorsPath, cchMonitorsPath * sizeof(WCHAR));
182 pwszRegistryPath[cchMonitorsPath] = L'\\';
183 CopyMemory(&pwszRegistryPath[cchMonitorsPath + 1], pPrintMonitor->pwszName, (cchPrintMonitorName + 1) * sizeof(WCHAR));
184
185 // Call the Level 1 initialization routine.
186 pPrintMonitor->pMonitor = (LPMONITOREX)pfnInitializePrintMonitor(pwszRegistryPath);
187 if (!pPrintMonitor->pMonitor)
188 {
189 ERR("InitializePrintMonitor failed for \"%S\" with error %lu!\n", pPrintMonitor->pwszFileName, GetLastError());
190 continue;
191 }
192 }
193 else
194 {
195 ERR("No initialization routine found for \"%S\"!\n", pPrintMonitor->pwszFileName);
196 continue;
197 }
198 }
199
200 // Add this Print Monitor to the list.
201 InsertTailList(&PrintMonitorList, &pPrintMonitor->Entry);
202
203 // Don't let the cleanup routine free this.
204 pPrintMonitor = NULL;
205 }
206
207 dwErrorCode = ERROR_SUCCESS;
208
209 Cleanup:
210 // Inside the loop
211 if (hSubKey)
212 RegCloseKey(hSubKey);
213
214 if (pwszRegistryPath)
215 DllFreeSplMem(pwszRegistryPath);
216
217 if (pPrintMonitor)
218 {
219 if (pPrintMonitor->pwszFileName)
220 DllFreeSplMem(pPrintMonitor->pwszFileName);
221
222 if (pPrintMonitor->pwszName)
223 DllFreeSplMem(pPrintMonitor->pwszName);
224
225 DllFreeSplMem(pPrintMonitor);
226 }
227
228 // Outside the loop
229 if (hKey)
230 RegCloseKey(hKey);
231
232 SetLastError(dwErrorCode);
233 return (dwErrorCode == ERROR_SUCCESS);
234 }
235
236 BOOL WINAPI
237 LocalEnumMonitors(PWSTR pName, DWORD Level, PBYTE pMonitors, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
238 {
239 DWORD cbFileName;
240 DWORD cbMonitorName;
241 DWORD dwErrorCode;
242 PBYTE pStart;
243 PBYTE pEnd;
244 PLIST_ENTRY pEntry;
245 PLOCAL_PRINT_MONITOR pPrintMonitor;
246 MONITOR_INFO_2W MonitorInfo2; // MONITOR_INFO_1W is a subset of MONITOR_INFO_2W, so we can handle both in one function here.
247
248 // Sanity checks.
249 if (Level > 2)
250 {
251 dwErrorCode = ERROR_INVALID_LEVEL;
252 goto Cleanup;
253 }
254
255 if ((cbBuf && !pMonitors) || !pcbNeeded || !pcReturned)
256 {
257 dwErrorCode = ERROR_INVALID_PARAMETER;
258 goto Cleanup;
259 }
260
261 // Begin counting.
262 *pcbNeeded = 0;
263 *pcReturned = 0;
264
265 // Count the required buffer size and the number of monitors.
266 for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
267 {
268 pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
269
270 cbMonitorName = (wcslen(pPrintMonitor->pwszName) + 1) * sizeof(WCHAR);
271 cbFileName = (wcslen(pPrintMonitor->pwszFileName) + 1) * sizeof(WCHAR);
272
273 if (Level == 1)
274 *pcbNeeded += sizeof(MONITOR_INFO_1W) + cbMonitorName;
275 else
276 *pcbNeeded += sizeof(MONITOR_INFO_2W) + cbMonitorName + cbCurrentEnvironment + cbFileName;
277 }
278
279 // Check if the supplied buffer is large enough.
280 if (cbBuf < *pcbNeeded)
281 {
282 dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
283 goto Cleanup;
284 }
285
286 // Put the strings at the end of the buffer.
287 pStart = pMonitors;
288 pEnd = &pMonitors[cbBuf];
289
290 for (pEntry = PrintMonitorList.Flink; pEntry != &PrintMonitorList; pEntry = pEntry->Flink)
291 {
292 pPrintMonitor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_MONITOR, Entry);
293
294 // Copy the monitor name.
295 cbMonitorName = (wcslen(pPrintMonitor->pwszName) + 1) * sizeof(WCHAR);
296 pEnd -= cbMonitorName;
297 MonitorInfo2.pName = (PWSTR)pEnd;
298 CopyMemory(pEnd, pPrintMonitor->pwszName, cbMonitorName);
299
300 if (Level == 1)
301 {
302 // Finally copy the structure.
303 CopyMemory(pStart, &MonitorInfo2, sizeof(MONITOR_INFO_1W));
304 pStart += sizeof(MONITOR_INFO_1W);
305 }
306 else
307 {
308 // Copy the environment.
309 pEnd -= cbCurrentEnvironment;
310 MonitorInfo2.pEnvironment = (PWSTR)pEnd;
311 CopyMemory(pEnd, wszCurrentEnvironment, cbCurrentEnvironment);
312
313 // Copy the file name.
314 cbFileName = (wcslen(pPrintMonitor->pwszFileName) + 1) * sizeof(WCHAR);
315 pEnd -= cbFileName;
316 MonitorInfo2.pDLLName = (PWSTR)pEnd;
317 CopyMemory(pEnd, pPrintMonitor->pwszFileName, cbFileName);
318
319 // Finally copy the structure.
320 CopyMemory(pStart, &MonitorInfo2, sizeof(MONITOR_INFO_2W));
321 pStart += sizeof(MONITOR_INFO_2W);
322 }
323
324 (*pcReturned)++;
325 }
326
327 dwErrorCode = ERROR_SUCCESS;
328
329 Cleanup:
330 SetLastError(dwErrorCode);
331 return (dwErrorCode == ERROR_SUCCESS);
332 }