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