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