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 PCWSTR 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 PCWSTR 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 } 370