1 /* 2 * PROJECT: ReactOS Local Spooler 3 * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+) 4 * PURPOSE: Functions related to Printers and printing 5 * COPYRIGHT: Copyright 2015-2017 Colin Finck (colin@reactos.org) 6 */ 7 8 #include "precomp.h" 9 10 // Global Variables 11 SKIPLIST PrinterList; 12 13 // Forward Declarations 14 static void _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 15 static void _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 16 static void _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 17 static void _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 18 static void _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 19 static void _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 20 static void _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 21 static void _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 22 static void _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 23 static void _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName); 24 25 // Local Constants 26 typedef void (*PLocalGetPrinterLevelFunc)(PLOCAL_PRINTER, PVOID, PBYTE*, PDWORD, DWORD, PCWSTR); 27 28 static const PLocalGetPrinterLevelFunc pfnGetPrinterLevels[] = { 29 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel0, 30 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel1, 31 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel2, 32 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel3, 33 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel4, 34 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel5, 35 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel6, 36 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel7, 37 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel8, 38 (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel9 39 }; 40 41 static DWORD dwPrinterInfo0Offsets[] = { 42 FIELD_OFFSET(PRINTER_INFO_STRESS, pPrinterName), 43 MAXDWORD 44 }; 45 46 static DWORD dwPrinterInfo1Offsets[] = { 47 FIELD_OFFSET(PRINTER_INFO_1W, pName), 48 FIELD_OFFSET(PRINTER_INFO_1W, pComment), 49 FIELD_OFFSET(PRINTER_INFO_1W, pDescription), 50 MAXDWORD 51 }; 52 53 static DWORD dwPrinterInfo2Offsets[] = { 54 FIELD_OFFSET(PRINTER_INFO_2W, pPrinterName), 55 FIELD_OFFSET(PRINTER_INFO_2W, pShareName), 56 FIELD_OFFSET(PRINTER_INFO_2W, pPortName), 57 FIELD_OFFSET(PRINTER_INFO_2W, pDriverName), 58 FIELD_OFFSET(PRINTER_INFO_2W, pComment), 59 FIELD_OFFSET(PRINTER_INFO_2W, pLocation), 60 FIELD_OFFSET(PRINTER_INFO_2W, pSepFile), 61 FIELD_OFFSET(PRINTER_INFO_2W, pPrintProcessor), 62 FIELD_OFFSET(PRINTER_INFO_2W, pDatatype), 63 FIELD_OFFSET(PRINTER_INFO_2W, pParameters), 64 MAXDWORD 65 }; 66 67 static DWORD dwPrinterInfo4Offsets[] = { 68 FIELD_OFFSET(PRINTER_INFO_4W, pPrinterName), 69 MAXDWORD 70 }; 71 72 static DWORD dwPrinterInfo5Offsets[] = { 73 FIELD_OFFSET(PRINTER_INFO_5W, pPrinterName), 74 FIELD_OFFSET(PRINTER_INFO_5W, pPortName), 75 MAXDWORD 76 }; 77 78 /** These values serve no purpose anymore, but are still used in PRINTER_INFO_5 and 79 HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts */ 80 static const DWORD dwDeviceNotSelectedTimeout = 15000; 81 static const DWORD dwTransmissionRetryTimeout = 45000; 82 83 84 /** 85 * @name _PrinterListCompareRoutine 86 * 87 * SKIPLIST_COMPARE_ROUTINE for the Printer List. 88 * Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers. 89 */ 90 static int WINAPI 91 _PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct) 92 { 93 PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct; 94 PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct; 95 96 return _wcsicmp(A->pwszPrinterName, B->pwszPrinterName); 97 } 98 99 /** 100 * @name InitializePrinterList 101 * 102 * Initializes a list of locally available Printers. 103 * The list is searchable by name and returns information about the printers, including their job queues. 104 * During this process, the job queues are also initialized. 105 */ 106 BOOL 107 InitializePrinterList(VOID) 108 { 109 DWORD cbData; 110 DWORD cchPrinterName; 111 DWORD dwErrorCode; 112 DWORD dwSubKeys; 113 DWORD i; 114 HKEY hSubKey = NULL; 115 PLOCAL_PORT pPort; 116 PLOCAL_PRINTER pPrinter = NULL; 117 PLOCAL_PRINT_PROCESSOR pPrintProcessor; 118 PWSTR pwszPort = NULL; 119 PWSTR pwszPrintProcessor = NULL; 120 WCHAR wszPrinterName[MAX_PRINTER_NAME + 1]; 121 122 TRACE("InitializePrinterList()\n"); 123 124 // Initialize an empty list for our printers. 125 InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem); 126 127 // Get the number of subkeys of the printers registry key. Each subkey is a local printer there. 128 dwErrorCode = (DWORD)RegQueryInfoKeyW(hPrintersKey, NULL, NULL, NULL, &dwSubKeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); 129 if (dwErrorCode != ERROR_SUCCESS) 130 { 131 ERR("RegQueryInfoKeyW failed with status %lu!\n", dwErrorCode); 132 goto Cleanup; 133 } 134 135 // Loop through all available local printers. 136 for (i = 0; i < dwSubKeys; i++) 137 { 138 // Cleanup tasks from the previous run 139 if (hSubKey) 140 { 141 RegCloseKey(hSubKey); 142 hSubKey = NULL; 143 } 144 145 if (pPrinter) 146 { 147 if (pPrinter->pDefaultDevMode) 148 DllFreeSplMem(pPrinter->pDefaultDevMode); 149 150 if (pPrinter->pwszDefaultDatatype) 151 DllFreeSplStr(pPrinter->pwszDefaultDatatype); 152 153 if (pPrinter->pwszDescription) 154 DllFreeSplStr(pPrinter->pwszDescription); 155 156 if (pPrinter->pwszPrinterDriver) 157 DllFreeSplStr(pPrinter->pwszPrinterDriver); 158 159 if (pPrinter->pwszPrinterName) 160 DllFreeSplStr(pPrinter->pwszPrinterName); 161 162 DllFreeSplMem(pPrinter); 163 pPrinter = NULL; 164 } 165 166 if (pwszPrintProcessor) 167 { 168 DllFreeSplStr(pwszPrintProcessor); 169 pwszPrintProcessor = NULL; 170 } 171 172 // Get the name of this printer. 173 cchPrinterName = _countof(wszPrinterName); 174 dwErrorCode = (DWORD)RegEnumKeyExW(hPrintersKey, i, wszPrinterName, &cchPrinterName, NULL, NULL, NULL, NULL); 175 if (dwErrorCode == ERROR_MORE_DATA) 176 { 177 // This printer name exceeds the maximum length and is invalid. 178 continue; 179 } 180 else if (dwErrorCode != ERROR_SUCCESS) 181 { 182 ERR("RegEnumKeyExW failed for iteration %lu with status %lu!\n", i, dwErrorCode); 183 continue; 184 } 185 186 // Open this Printer's registry key. 187 dwErrorCode = (DWORD)RegOpenKeyExW(hPrintersKey, wszPrinterName, 0, KEY_READ, &hSubKey); 188 if (dwErrorCode != ERROR_SUCCESS) 189 { 190 ERR("RegOpenKeyExW failed for Printer \"%S\" with status %lu!\n", wszPrinterName, dwErrorCode); 191 continue; 192 } 193 194 // Get the Print Processor. 195 pwszPrintProcessor = AllocAndRegQueryWSZ(hSubKey, L"Print Processor"); 196 if (!pwszPrintProcessor) 197 continue; 198 199 // Try to find it in the Print Processor List. 200 pPrintProcessor = FindPrintProcessor(pwszPrintProcessor); 201 if (!pPrintProcessor) 202 { 203 ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName); 204 continue; 205 } 206 207 // Get the Port. 208 pwszPort = AllocAndRegQueryWSZ(hSubKey, L"Port"); 209 if (!pwszPort) 210 continue; 211 212 // Try to find it in the Port List. 213 pPort = FindPort(pwszPort); 214 if (!pPort) 215 { 216 ERR("Invalid Port \"%S\" for Printer \"%S\"!\n", pwszPort, wszPrinterName); 217 continue; 218 } 219 220 // Create a new LOCAL_PRINTER structure for it. 221 pPrinter = DllAllocSplMem(sizeof(LOCAL_PRINTER)); 222 if (!pPrinter) 223 { 224 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 225 ERR("DllAllocSplMem failed!\n"); 226 goto Cleanup; 227 } 228 229 pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName); 230 pPrinter->pPrintProcessor = pPrintProcessor; 231 pPrinter->pPort = pPort; 232 InitializePrinterJobList(pPrinter); 233 234 // Get the location. 235 pPrinter->pwszLocation = AllocAndRegQueryWSZ(hSubKey, L"Location"); 236 if (!pPrinter->pwszLocation) 237 continue; 238 239 // Get the printer driver. 240 pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver"); 241 if (!pPrinter->pwszPrinterDriver) 242 continue; 243 244 // Get the description. 245 pPrinter->pwszDescription = AllocAndRegQueryWSZ(hSubKey, L"Description"); 246 if (!pPrinter->pwszDescription) 247 continue; 248 249 // Get the default datatype. 250 pPrinter->pwszDefaultDatatype = AllocAndRegQueryWSZ(hSubKey, L"Datatype"); 251 if (!pPrinter->pwszDefaultDatatype) 252 continue; 253 254 // Verify that it's valid. 255 if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype)) 256 { 257 ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName); 258 continue; 259 } 260 261 // Determine the size of the DevMode. 262 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, NULL, &cbData); 263 if (dwErrorCode != ERROR_SUCCESS) 264 { 265 ERR("Couldn't query the size of the DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData); 266 continue; 267 } 268 269 // Allocate enough memory for the DevMode. 270 pPrinter->pDefaultDevMode = DllAllocSplMem(cbData); 271 if (!pPrinter->pDefaultDevMode) 272 { 273 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 274 ERR("DllAllocSplMem failed!\n"); 275 goto Cleanup; 276 } 277 278 // Get the default DevMode. 279 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)pPrinter->pDefaultDevMode, &cbData); 280 if (dwErrorCode != ERROR_SUCCESS) 281 { 282 ERR("Couldn't query a DevMode for Printer \"%S\", status is %lu, cbData is %lu!\n", wszPrinterName, dwErrorCode, cbData); 283 continue; 284 } 285 286 // Get the Attributes. 287 cbData = sizeof(DWORD); 288 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Attributes", NULL, NULL, (PBYTE)&pPrinter->dwAttributes, &cbData); 289 if (dwErrorCode != ERROR_SUCCESS) 290 { 291 ERR("Couldn't query Attributes for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode); 292 continue; 293 } 294 295 // Get the Status. 296 cbData = sizeof(DWORD); 297 dwErrorCode = (DWORD)RegQueryValueExW(hSubKey, L"Status", NULL, NULL, (PBYTE)&pPrinter->dwStatus, &cbData); 298 if (dwErrorCode != ERROR_SUCCESS) 299 { 300 ERR("Couldn't query Status for Printer \"%S\", status is %lu!\n", wszPrinterName, dwErrorCode); 301 continue; 302 } 303 304 // Add this printer to the printer list. 305 if (!InsertElementSkiplist(&PrinterList, pPrinter)) 306 { 307 ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName); 308 goto Cleanup; 309 } 310 311 // Don't let the cleanup routines free this. 312 pPrinter = NULL; 313 } 314 315 dwErrorCode = ERROR_SUCCESS; 316 317 Cleanup: 318 // Inside the loop 319 if (hSubKey) 320 RegCloseKey(hSubKey); 321 322 if (pPrinter) 323 { 324 if (pPrinter->pDefaultDevMode) 325 DllFreeSplMem(pPrinter->pDefaultDevMode); 326 327 if (pPrinter->pwszDefaultDatatype) 328 DllFreeSplStr(pPrinter->pwszDefaultDatatype); 329 330 if (pPrinter->pwszDescription) 331 DllFreeSplStr(pPrinter->pwszDescription); 332 333 if (pPrinter->pwszPrinterDriver) 334 DllFreeSplStr(pPrinter->pwszPrinterDriver); 335 336 if (pPrinter->pwszPrinterName) 337 DllFreeSplStr(pPrinter->pwszPrinterName); 338 339 DllFreeSplMem(pPrinter); 340 } 341 342 if (pwszPrintProcessor) 343 DllFreeSplStr(pwszPrintProcessor); 344 345 SetLastError(dwErrorCode); 346 return (dwErrorCode == ERROR_SUCCESS); 347 } 348 349 VOID 350 BroadcastChange(PLOCAL_HANDLE pHandle) 351 { 352 PLOCAL_PRINTER pPrinter; 353 PSKIPLIST_NODE pNode; 354 DWORD cchMachineName = 0; 355 WCHAR wszMachineName[MAX_PATH] = {0}; // if not local, use Machine Name then Printer Name... pPrinter->pJob->pwszMachineName? 356 357 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0]) 358 { 359 pPrinter = (PLOCAL_PRINTER)pNode->Element; 360 361 StringCchCopyW( &wszMachineName[cchMachineName], sizeof(wszMachineName), pPrinter->pwszPrinterName ); 362 363 PostMessageW( HWND_BROADCAST, WM_DEVMODECHANGE, 0, (LPARAM)&wszMachineName ); 364 } 365 } 366 367 /** 368 * @name _LocalEnumPrintersCheckName 369 * 370 * Checks the Name parameter supplied to a call to EnumPrinters. 371 * 372 * @param Flags 373 * Flags parameter of EnumPrinters. 374 * 375 * @param Name 376 * Name parameter of EnumPrinters to check. 377 * 378 * @param pwszComputerName 379 * Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters. 380 * On return, it may contain a computer name to prepend in EnumPrinters depending on the case. 381 * 382 * @param pcchComputerName 383 * If a string to prepend is returned, this pointer receives its length in characters. 384 * 385 * @return 386 * ERROR_SUCCESS if processing in EnumPrinters can be continued. 387 * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider. 388 * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters. 389 */ 390 static DWORD 391 _LocalEnumPrintersCheckName(DWORD Flags, PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName) 392 { 393 PCWSTR pName; 394 PCWSTR pComputerName; 395 396 // If there is no Name parameter to check, we can just continue in EnumPrinters. 397 if (!Name) 398 return ERROR_SUCCESS; 399 400 // Check if Name does not begin with two backslashes (required for specifying Computer Names). 401 if (Name[0] != L'\\' || Name[1] != L'\\') 402 { 403 if (Flags & PRINTER_ENUM_NAME) 404 { 405 // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the 406 // Print Provider Name or the local Computer Name. 407 408 // Compare with the Print Provider Name. 409 if (_wcsicmp(Name, wszPrintProviderInfo[0]) == 0) 410 return ERROR_SUCCESS; 411 412 // Dismiss anything else. 413 return ERROR_INVALID_NAME; 414 } 415 else 416 { 417 // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name. 418 return ERROR_SUCCESS; 419 } 420 } 421 422 // Prepend the backslashes to the output computer name. 423 pwszComputerName[0] = L'\\'; 424 pwszComputerName[1] = L'\\'; 425 426 // Get the local computer name for comparison. 427 *pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1; 428 if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName)) 429 { 430 ERR("GetComputerNameW failed with error %lu!\n", GetLastError()); 431 return GetLastError(); 432 } 433 434 // Add the leading slashes to the total length. 435 *pcchComputerName += 2; 436 437 // Compare both names. 438 pComputerName = &pwszComputerName[2]; 439 pName = &Name[2]; 440 for (;;) 441 { 442 // Are we at the end of the local Computer Name string? 443 if (!*pComputerName) 444 { 445 // Are we also at the end of the supplied Name parameter? 446 // A terminating NUL character and a backslash are both treated as the end, but they are treated differently. 447 if (!*pName) 448 { 449 // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters. 450 // Add a trailing backslash for that. 451 pwszComputerName[(*pcchComputerName)++] = L'\\'; 452 pwszComputerName[*pcchComputerName] = 0; 453 return ERROR_SUCCESS; 454 } 455 else if (*pName == L'\\') 456 { 457 if (Flags & PRINTER_ENUM_NAME) 458 { 459 // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local 460 // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed. 461 return ERROR_INVALID_NAME; 462 } 463 else 464 { 465 // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash. 466 // Only the Computer Name between the backslashes is checked then. 467 // This is largely undocumented, but verified by tests (see winspool_apitest). 468 // In this case, no computer name is prepended in EnumPrinters though. 469 *pwszComputerName = 0; 470 *pcchComputerName = 0; 471 return ERROR_SUCCESS; 472 } 473 } 474 } 475 476 // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match. 477 if (towlower(*pName) != towlower(*pComputerName)) 478 return ERROR_INVALID_NAME; 479 480 pName++; 481 pComputerName++; 482 } 483 } 484 485 static DWORD 486 _DumpLevel1PrintProviderInformation(PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned) 487 { 488 int i; 489 490 // Count the needed bytes for Print Provider information. 491 *pcbNeeded = sizeof(PRINTER_INFO_1W); 492 493 for (i = 0; i < 3; i++) 494 *pcbNeeded += (wcslen(wszPrintProviderInfo[i]) + 1) * sizeof(WCHAR); 495 496 // Check if the supplied buffer is large enough. 497 if (cbBuf < *pcbNeeded) 498 return ERROR_INSUFFICIENT_BUFFER; 499 500 // Copy over the Print Provider information. 501 ((PPRINTER_INFO_1W)pPrinterEnum)->Flags = 0; 502 PackStrings(wszPrintProviderInfo, pPrinterEnum, dwPrinterInfo1Offsets, &pPrinterEnum[*pcbNeeded]); 503 *pcReturned = 1; 504 505 return ERROR_SUCCESS; 506 } 507 508 static void 509 _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 510 { 511 size_t cbName; 512 PWSTR p, Allocation; 513 PCWSTR pwszStrings[1]; 514 SYSTEM_INFO SystemInfo; 515 516 // Calculate the string lengths. 517 cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR); 518 519 if (!ppPrinterInfo) 520 { 521 *pcbNeeded += sizeof(PRINTER_INFO_STRESS) + cbName; 522 return; 523 } 524 525 // Set the general fields. 526 ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_STRESS)); 527 (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount; 528 (*ppPrinterInfo)->dwGetVersion = GetVersion(); 529 (*ppPrinterInfo)->Status = pPrinter->dwStatus; 530 531 #if !DBG 532 (*ppPrinterInfo)->fFreeBuild = 1; 533 #endif 534 535 GetSystemInfo(&SystemInfo); 536 (*ppPrinterInfo)->dwNumberOfProcessors = SystemInfo.dwNumberOfProcessors; 537 (*ppPrinterInfo)->dwProcessorType = SystemInfo.dwProcessorType; 538 (*ppPrinterInfo)->wProcessorArchitecture = SystemInfo.wProcessorArchitecture; 539 (*ppPrinterInfo)->wProcessorLevel = SystemInfo.wProcessorLevel; 540 541 // Copy the Printer Name. 542 p = Allocation = DllAllocSplMem(cbName); 543 pwszStrings[0] = Allocation; 544 StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0); 545 StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0); 546 547 // Finally copy the structure and advance to the next one in the output buffer. 548 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo0Offsets, *ppPrinterInfoEnd); 549 (*ppPrinterInfo)++; 550 551 // Free the memory for temporary strings. 552 DllFreeSplMem(Allocation); 553 } 554 555 static void 556 _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 557 { 558 const WCHAR wszComma[] = L","; 559 560 size_t cbName; 561 size_t cbComment; 562 size_t cbDescription; 563 PWSTR p, Allocation1, Allocation2; 564 PCWSTR pwszStrings[3]; 565 566 // Calculate the string lengths. 567 // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings. 568 // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query. 569 cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR); 570 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR); 571 cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR); 572 573 if (!ppPrinterInfo) 574 { 575 *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription; 576 return; 577 } 578 579 // Indicate that this is a Printer. 580 (*ppPrinterInfo)->Flags = PRINTER_ENUM_ICON8; 581 582 // Copy the Printer Name. 583 p = Allocation1 = DllAllocSplMem(cbName); 584 pwszStrings[0] = Allocation1; 585 StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0); 586 StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0); 587 588 // Copy the Printer comment (equals the "Description" registry value). 589 pwszStrings[1] = pPrinter->pwszDescription; 590 591 // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location" 592 p = Allocation2 = DllAllocSplMem(cbDescription); 593 pwszStrings[2] = Allocation2; 594 StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0); 595 StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0); 596 StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0); 597 StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0); 598 StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0); 599 StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0); 600 601 // Finally copy the structure and advance to the next one in the output buffer. 602 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo1Offsets, *ppPrinterInfoEnd); 603 (*ppPrinterInfo)++; 604 605 // Free the memory for temporary strings. 606 DllFreeSplMem(Allocation1); 607 DllFreeSplMem(Allocation2); 608 } 609 610 static void 611 _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 612 { 613 WCHAR wszEmpty[] = L""; 614 615 size_t cbDevMode; 616 size_t cbPrinterName; 617 size_t cbShareName; 618 size_t cbPortName; 619 size_t cbDriverName; 620 size_t cbComment; 621 size_t cbLocation; 622 size_t cbSepFile; 623 size_t cbPrintProcessor; 624 size_t cbDatatype; 625 size_t cbParameters; 626 PWSTR p, Allocation; 627 PCWSTR pwszStrings[10]; 628 FIXME("LocalGetPrinterLevel2\n"); 629 // Calculate the string lengths. 630 cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra; 631 cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR); 632 633 if (!ppPrinterInfo) 634 { 635 // Attention: pComment equals the "Description" registry value. 636 cbShareName = sizeof(wszEmpty); 637 cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR); 638 cbDriverName = (wcslen(pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR); 639 cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR); 640 cbLocation = (wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR); 641 cbSepFile = sizeof(wszEmpty); 642 cbPrintProcessor = (wcslen(pPrinter->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR); 643 cbDatatype = (wcslen(pPrinter->pwszDefaultDatatype) + 1) * sizeof(WCHAR); 644 cbParameters = sizeof(wszEmpty); 645 646 *pcbNeeded += sizeof(PRINTER_INFO_2W) + cbDevMode + cbPrinterName + cbShareName + cbPortName + cbDriverName + cbComment + cbLocation + cbSepFile + cbPrintProcessor + cbDatatype + cbParameters; 647 FIXME("LocalGetPrinterLevel2 Needed %d\n",*pcbNeeded); 648 return; 649 } 650 651 // Set the general fields. 652 ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_2W)); 653 (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes; 654 (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount; 655 (*ppPrinterInfo)->Status = pPrinter->dwStatus; 656 657 // Set the pDevMode field (and copy the DevMode). 658 *ppPrinterInfoEnd -= cbDevMode; 659 CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode); 660 (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd); 661 662 // Set the pPrinterName field. 663 p = Allocation = DllAllocSplMem(cbPrinterName); 664 pwszStrings[0] = Allocation; 665 StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0); 666 StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0); 667 668 // Set the pShareName field. 669 pwszStrings[1] = wszEmpty; 670 671 // Set the pPortName field. 672 pwszStrings[2] = pPrinter->pPort->pwszName; 673 674 // Set the pDriverName field. 675 pwszStrings[3] = pPrinter->pwszPrinterDriver; 676 677 // Set the pComment field ((equals the "Description" registry value). 678 pwszStrings[4] = pPrinter->pwszDescription; 679 680 // Set the pLocation field. 681 pwszStrings[5] = pPrinter->pwszLocation; 682 683 // Set the pSepFile field. 684 pwszStrings[6] = wszEmpty; 685 686 // Set the pPrintProcessor field. 687 pwszStrings[7] = pPrinter->pPrintProcessor->pwszName; 688 689 // Set the pDatatype field. 690 pwszStrings[8] = pPrinter->pwszDefaultDatatype; 691 692 // Set the pParameters field. 693 pwszStrings[9] = wszEmpty; 694 695 // Finally copy the structure and advance to the next one in the output buffer. 696 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo2Offsets, *ppPrinterInfoEnd); 697 (*ppPrinterInfo)++; 698 699 // Free the memory for temporary strings. 700 DllFreeSplMem(Allocation); 701 } 702 703 static void 704 _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 705 { 706 SECURITY_DESCRIPTOR SecurityDescriptor = { 0 }; 707 708 if (!ppPrinterInfo) 709 { 710 *pcbNeeded += sizeof(PRINTER_INFO_3) + sizeof(SECURITY_DESCRIPTOR); 711 return; 712 } 713 714 FIXME("Return a valid security descriptor for PRINTER_INFO_3\n"); 715 716 // Set the pSecurityDescriptor field (and copy the Security Descriptor). 717 *ppPrinterInfoEnd -= sizeof(SECURITY_DESCRIPTOR); 718 CopyMemory(*ppPrinterInfoEnd, &SecurityDescriptor, sizeof(SECURITY_DESCRIPTOR)); 719 (*ppPrinterInfo)->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)(*ppPrinterInfoEnd); 720 721 // Advance to the next structure. 722 (*ppPrinterInfo)++; 723 } 724 725 static void 726 _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 727 { 728 size_t cbPrinterName; 729 PWSTR p, Allocation; 730 PCWSTR pwszStrings[1]; 731 732 // Calculate the string lengths. 733 cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR); 734 735 if (!ppPrinterInfo) 736 { 737 *pcbNeeded += sizeof(PRINTER_INFO_4W) + cbPrinterName; 738 return; 739 } 740 741 // Set the general fields. 742 (*ppPrinterInfo)->pServerName = NULL; 743 (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes; 744 745 // Set the pPrinterName field. 746 p = Allocation = DllAllocSplMem(cbPrinterName); 747 pwszStrings[0] = Allocation; 748 StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0); 749 StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0); 750 751 // Finally copy the structure and advance to the next one in the output buffer. 752 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo4Offsets, *ppPrinterInfoEnd); 753 (*ppPrinterInfo)++; 754 755 // Free the memory for temporary strings. 756 DllFreeSplMem(Allocation); 757 } 758 759 static void 760 _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 761 { 762 size_t cbPrinterName; 763 size_t cbPortName; 764 PWSTR p, Allocation; 765 PCWSTR pwszStrings[2]; 766 767 // Calculate the string lengths. 768 cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR); 769 770 if (!ppPrinterInfo) 771 { 772 cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR); 773 774 *pcbNeeded += sizeof(PRINTER_INFO_5W) + cbPrinterName + cbPortName; 775 return; 776 } 777 778 // Set the general fields. 779 (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes; 780 (*ppPrinterInfo)->DeviceNotSelectedTimeout = dwDeviceNotSelectedTimeout; 781 (*ppPrinterInfo)->TransmissionRetryTimeout = dwTransmissionRetryTimeout; 782 783 // Set the pPrinterName field. 784 p = Allocation = DllAllocSplMem(cbPrinterName); 785 pwszStrings[0] = Allocation; 786 StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0); 787 StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0); 788 789 // Set the pPortName field. 790 pwszStrings[1] = pPrinter->pPort->pwszName; 791 792 // Finally copy the structure and advance to the next one in the output buffer. 793 *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo5Offsets, *ppPrinterInfoEnd); 794 (*ppPrinterInfo)++; 795 796 // Free the memory for temporary strings. 797 DllFreeSplMem(Allocation); 798 } 799 800 static void 801 _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 802 { 803 if (!ppPrinterInfo) 804 { 805 *pcbNeeded += sizeof(PRINTER_INFO_6); 806 return; 807 } 808 809 // Set the general fields. 810 (*ppPrinterInfo)->dwStatus = pPrinter->dwStatus; 811 812 // Advance to the next structure. 813 (*ppPrinterInfo)++; 814 } 815 816 static void 817 _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 818 { 819 if (!ppPrinterInfo) 820 { 821 *pcbNeeded += sizeof(PRINTER_INFO_7W); 822 return; 823 } 824 825 FIXME("No Directory Support, returning DSPRINT_UNPUBLISH for PRINTER_INFO_7 all the time!\n"); 826 827 // Set the general fields. 828 (*ppPrinterInfo)->dwAction = DSPRINT_UNPUBLISH; 829 (*ppPrinterInfo)->pszObjectGUID = NULL; 830 831 // Advance to the next structure. 832 (*ppPrinterInfo)++; 833 } 834 835 static void 836 _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 837 { 838 DWORD cbDevMode; 839 840 // Calculate the string lengths. 841 cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra; 842 843 if (!ppPrinterInfo) 844 { 845 *pcbNeeded += sizeof(PRINTER_INFO_8W) + cbDevMode; 846 return; 847 } 848 849 // Set the pDevMode field (and copy the DevMode). 850 *ppPrinterInfoEnd -= cbDevMode; 851 CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode); 852 (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd); 853 854 // Advance to the next structure. 855 (*ppPrinterInfo)++; 856 } 857 858 static void 859 _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PCWSTR wszComputerName) 860 { 861 DWORD cbDevMode; 862 863 // Calculate the string lengths. 864 cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra; 865 866 if (!ppPrinterInfo) 867 { 868 *pcbNeeded += sizeof(PRINTER_INFO_9W) + cbDevMode; 869 return; 870 } 871 872 FIXME("Per-user settings are not yet implemented, returning the global DevMode for PRINTER_INFO_9!\n"); 873 874 // Set the pDevMode field (and copy the DevMode). 875 *ppPrinterInfoEnd -= cbDevMode; 876 CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode); 877 (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd); 878 879 // Advance to the next structure. 880 (*ppPrinterInfo)++; 881 } 882 883 BOOL WINAPI 884 LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned) 885 { 886 DWORD cchComputerName = 0; 887 DWORD dwErrorCode; 888 PBYTE pPrinterInfoEnd; 889 PSKIPLIST_NODE pNode; 890 WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 }; 891 PLOCAL_PRINTER pPrinter; 892 893 FIXME("LocalEnumPrinters(%lu, %S, %lu, %p, %lu, %p, %p)\n", Flags, Name, Level, pPrinterEnum, cbBuf, pcbNeeded, pcReturned); 894 895 // Do no sanity checks or assertions for pcbNeeded and pcReturned here. 896 // This is verified and required by localspl_apitest! 897 898 // Begin counting. 899 *pcbNeeded = 0; 900 *pcReturned = 0; 901 902 if (Flags & PRINTER_ENUM_CONNECTIONS || Flags & PRINTER_ENUM_REMOTE || Flags & PRINTER_ENUM_NETWORK) 903 { 904 // If the flags for the Network Print Provider are given, bail out with ERROR_INVALID_NAME. 905 // This is the internal way for a Print Provider to signal that it doesn't handle this request. 906 dwErrorCode = ERROR_INVALID_NAME; 907 goto Cleanup; 908 } 909 910 if (!(Flags & PRINTER_ENUM_LOCAL || Flags & PRINTER_ENUM_NAME)) 911 { 912 // The Local Print Provider is the right destination for the request, but without any of these flags, 913 // there is no information that can be returned. 914 // So just signal a successful request. 915 dwErrorCode = ERROR_SUCCESS; 916 goto Cleanup; 917 } 918 919 if (Level == 3 || Level > 5) 920 { 921 // The caller supplied an invalid level for EnumPrinters. 922 dwErrorCode = ERROR_INVALID_LEVEL; 923 goto Cleanup; 924 } 925 926 if (Level == 1 && Flags & PRINTER_ENUM_NAME && !Name) 927 { 928 // The caller wants information about this Print Provider. 929 // spoolss packs this into an array of information about all Print Providers. 930 dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned); 931 goto Cleanup; 932 } 933 934 // Check the supplied Name parameter (if any). 935 // This may return a Computer Name string we later prepend to the output. 936 dwErrorCode = _LocalEnumPrintersCheckName(Flags, Name, wszComputerName, &cchComputerName); 937 if (dwErrorCode != ERROR_SUCCESS) 938 goto Cleanup; 939 940 // Count the required buffer size and the number of printers. 941 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0]) 942 { 943 pPrinter = (PLOCAL_PRINTER)pNode->Element; 944 945 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it. 946 if (Flags & PRINTER_ENUM_SHARED) 947 { 948 FIXME("Printer Sharing is not supported yet, returning no printers!\n"); 949 continue; 950 } 951 952 pfnGetPrinterLevels[Level](pPrinter, NULL, NULL, pcbNeeded, cchComputerName, wszComputerName); 953 } 954 955 // Check if the supplied buffer is large enough. 956 if (cbBuf < *pcbNeeded) 957 { 958 dwErrorCode = ERROR_INSUFFICIENT_BUFFER; 959 goto Cleanup; 960 } 961 962 // Copy over the Printer information. 963 pPrinterInfoEnd = &pPrinterEnum[*pcbNeeded]; 964 965 for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0]) 966 { 967 pPrinter = (PLOCAL_PRINTER)pNode->Element; 968 969 // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it. 970 if (Flags & PRINTER_ENUM_SHARED) 971 continue; 972 973 pfnGetPrinterLevels[Level](pPrinter, &pPrinterEnum, &pPrinterInfoEnd, NULL, cchComputerName, wszComputerName); 974 (*pcReturned)++; 975 } 976 977 dwErrorCode = ERROR_SUCCESS; 978 979 Cleanup: 980 SetLastError(dwErrorCode); 981 return (dwErrorCode == ERROR_SUCCESS); 982 } 983 984 BOOL WINAPI 985 LocalGetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded) 986 { 987 // We never prepend a Computer Name to the output, but need to provide an empty string, 988 // because this variable is passed to StringCbCopyExW. 989 const WCHAR wszDummyComputerName[] = L""; 990 991 DWORD dwErrorCode; 992 PBYTE pPrinterEnd; 993 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; 994 PLOCAL_PRINTER_HANDLE pPrinterHandle; 995 996 TRACE("LocalGetPrinter(%p, %lu, %p, %lu, %p)\n", hPrinter, Level, pPrinter, cbBuf, pcbNeeded); 997 998 // Sanity checks. 999 if (!pHandle) 1000 { 1001 dwErrorCode = ERROR_INVALID_HANDLE; 1002 goto Cleanup; 1003 } 1004 1005 // Check if this is a printer handle. 1006 if (pHandle->HandleType != HandleType_Printer) 1007 { 1008 dwErrorCode = ERROR_INVALID_HANDLE; 1009 goto Cleanup; 1010 } 1011 1012 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle; 1013 1014 if (Level > 9) 1015 { 1016 // The caller supplied an invalid level for GetPrinter. 1017 dwErrorCode = ERROR_INVALID_LEVEL; 1018 goto Cleanup; 1019 } 1020 1021 // Count the required buffer size. 1022 *pcbNeeded = 0; 1023 pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, NULL, NULL, pcbNeeded, 0, wszDummyComputerName); 1024 1025 // Check if the supplied buffer is large enough. 1026 if (cbBuf < *pcbNeeded) 1027 { 1028 dwErrorCode = ERROR_INSUFFICIENT_BUFFER; 1029 goto Cleanup; 1030 } 1031 1032 // Copy over the Printer information. 1033 pPrinterEnd = &pPrinter[*pcbNeeded]; 1034 pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, &pPrinter, &pPrinterEnd, NULL, 0, wszDummyComputerName); 1035 dwErrorCode = ERROR_SUCCESS; 1036 1037 Cleanup: 1038 SetLastError(dwErrorCode); 1039 return (dwErrorCode == ERROR_SUCCESS); 1040 } 1041 1042 static DWORD 1043 _LocalOpenPortHandle(PWSTR pwszPortName, PHANDLE phPrinter) 1044 { 1045 BOOL bReturnValue; 1046 DWORD dwErrorCode; 1047 HANDLE hPort; 1048 PLOCAL_HANDLE pHandle = NULL; 1049 PLOCAL_PORT pPort; 1050 PLOCAL_PORT_HANDLE pPortHandle = NULL; 1051 PLOCAL_PRINT_MONITOR pPrintMonitor; 1052 1053 // Look for this port in our Print Monitor Port list. 1054 pPort = FindPort(pwszPortName); 1055 if (!pPort) 1056 { 1057 // The supplied port is unknown to all our Print Monitors. 1058 dwErrorCode = ERROR_INVALID_NAME; 1059 goto Failure; 1060 } 1061 1062 pPrintMonitor = pPort->pPrintMonitor; 1063 1064 // Call the monitor's OpenPort function. 1065 if (pPrintMonitor->bIsLevel2) 1066 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnOpenPort(pPrintMonitor->hMonitor, pwszPortName, &hPort); 1067 else 1068 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnOpenPort(pwszPortName, &hPort); 1069 1070 if (!bReturnValue) 1071 { 1072 // The OpenPort function failed. Return its last error. 1073 dwErrorCode = GetLastError(); 1074 goto Failure; 1075 } 1076 1077 // Create a new generic handle. 1078 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE)); 1079 if (!pHandle) 1080 { 1081 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1082 ERR("DllAllocSplMem failed!\n"); 1083 goto Failure; 1084 } 1085 1086 // Create a new LOCAL_PORT_HANDLE. 1087 pPortHandle = DllAllocSplMem(sizeof(LOCAL_PORT_HANDLE)); 1088 if (!pPortHandle) 1089 { 1090 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1091 ERR("DllAllocSplMem failed!\n"); 1092 goto Failure; 1093 } 1094 1095 pPortHandle->hPort = hPort; 1096 pPortHandle->pPort = pPort; 1097 1098 // Make the generic handle a Port handle. 1099 pHandle->HandleType = HandleType_Port; 1100 pHandle->pSpecificHandle = pPortHandle; 1101 1102 // Return it. 1103 *phPrinter = (HANDLE)pHandle; 1104 return ERROR_SUCCESS; 1105 1106 Failure: 1107 if (pHandle) 1108 DllFreeSplMem(pHandle); 1109 1110 if (pPortHandle) 1111 DllFreeSplMem(pPortHandle); 1112 1113 return dwErrorCode; 1114 } 1115 1116 static DWORD 1117 _LocalOpenPrinterHandle(PWSTR pwszPrinterName, PWSTR pwszJobParameter, PHANDLE phPrinter, PPRINTER_DEFAULTSW pDefault) 1118 { 1119 DWORD dwErrorCode; 1120 DWORD dwJobID; 1121 PLOCAL_HANDLE pHandle = NULL; 1122 PLOCAL_JOB pJob; 1123 PLOCAL_PRINTER pPrinter; 1124 PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL; 1125 WCHAR wszFullPath[MAX_PATH]; 1126 1127 // Retrieve the printer from the list. 1128 pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL); 1129 if (!pPrinter) 1130 { 1131 // The printer does not exist. 1132 dwErrorCode = ERROR_INVALID_NAME; 1133 goto Failure; 1134 } 1135 1136 // Create a new generic handle. 1137 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE)); 1138 if (!pHandle) 1139 { 1140 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1141 ERR("DllAllocSplMem failed!\n"); 1142 goto Failure; 1143 } 1144 1145 // Create a new LOCAL_PRINTER_HANDLE. 1146 pPrinterHandle = DllAllocSplMem(sizeof(LOCAL_PRINTER_HANDLE)); 1147 if (!pPrinterHandle) 1148 { 1149 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1150 ERR("DllAllocSplMem failed!\n"); 1151 goto Failure; 1152 } 1153 1154 pPrinterHandle->hSPLFile = INVALID_HANDLE_VALUE; 1155 pPrinterHandle->pPrinter = pPrinter; 1156 1157 // Check if a datatype was given. 1158 if (pDefault && pDefault->pDatatype) 1159 { 1160 // Use the datatype if it's valid. 1161 if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype)) 1162 { 1163 dwErrorCode = ERROR_INVALID_DATATYPE; 1164 goto Failure; 1165 } 1166 1167 pPrinterHandle->pwszDatatype = AllocSplStr(pDefault->pDatatype); 1168 } 1169 else 1170 { 1171 // Use the default datatype. 1172 pPrinterHandle->pwszDatatype = AllocSplStr(pPrinter->pwszDefaultDatatype); 1173 } 1174 1175 // Check if a DevMode was given, otherwise use the default. 1176 if (pDefault && pDefault->pDevMode) 1177 pPrinterHandle->pDevMode = DuplicateDevMode(pDefault->pDevMode); 1178 else 1179 pPrinterHandle->pDevMode = DuplicateDevMode(pPrinter->pDefaultDevMode); 1180 1181 // Check if the caller wants a handle to an existing Print Job. 1182 if (pwszJobParameter) 1183 { 1184 // The "Job " string has to follow now. 1185 if (wcsncmp(pwszJobParameter, L"Job ", 4) != 0) 1186 { 1187 dwErrorCode = ERROR_INVALID_NAME; 1188 goto Failure; 1189 } 1190 1191 // Skip the "Job " string. 1192 pwszJobParameter += 4; 1193 1194 // Skip even more whitespace. 1195 while (*pwszJobParameter == ' ') 1196 ++pwszJobParameter; 1197 1198 // Finally extract the desired Job ID. 1199 dwJobID = wcstoul(pwszJobParameter, NULL, 10); 1200 if (!IS_VALID_JOB_ID(dwJobID)) 1201 { 1202 // The user supplied an invalid Job ID. 1203 dwErrorCode = ERROR_INVALID_NAME; 1204 goto Failure; 1205 } 1206 1207 // Look for this job in the Global Job List. 1208 pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL); 1209 if (!pJob || pJob->pPrinter != pPrinter) 1210 { 1211 // The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name. 1212 dwErrorCode = ERROR_INVALID_PRINTER_NAME; 1213 goto Failure; 1214 } 1215 1216 // Try to open its SPL file. 1217 GetJobFilePath(L"SPL", dwJobID, wszFullPath); 1218 pPrinterHandle->hSPLFile = CreateFileW(wszFullPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); 1219 if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE) 1220 { 1221 dwErrorCode = GetLastError(); 1222 ERR("CreateFileW failed with error %lu for \"%S\"!\n", dwErrorCode, wszFullPath); 1223 goto Failure; 1224 } 1225 1226 // Associate the job to our Printer Handle, but don't set bStartedDoc. 1227 // This prevents the caller from doing further StartDocPrinter, WritePrinter, etc. calls on it. 1228 pPrinterHandle->pJob = pJob; 1229 } 1230 1231 // Make the generic handle a Printer handle. 1232 pHandle->HandleType = HandleType_Printer; 1233 pHandle->pSpecificHandle = pPrinterHandle; 1234 1235 // Return it. 1236 *phPrinter = (HANDLE)pHandle; 1237 return ERROR_SUCCESS; 1238 1239 Failure: 1240 if (pHandle) 1241 DllFreeSplMem(pHandle); 1242 1243 if (pPrinterHandle) 1244 { 1245 if (pPrinterHandle->pwszDatatype) 1246 DllFreeSplStr(pPrinterHandle->pwszDatatype); 1247 1248 if (pPrinterHandle->pDevMode) 1249 DllFreeSplMem(pPrinterHandle->pDevMode); 1250 1251 DllFreeSplMem(pPrinterHandle); 1252 } 1253 1254 return dwErrorCode; 1255 } 1256 1257 static DWORD 1258 _LocalOpenPrintServerHandle(PHANDLE phPrinter) 1259 { 1260 PLOCAL_HANDLE pHandle; 1261 1262 // Create a new generic handle. 1263 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE)); 1264 if (!pHandle) 1265 { 1266 ERR("DllAllocSplMem failed!\n"); 1267 return ERROR_NOT_ENOUGH_MEMORY; 1268 } 1269 1270 // Make the generic handle a Print Server handle. 1271 pHandle->HandleType = HandleType_PrintServer; 1272 pHandle->pSpecificHandle = NULL; 1273 1274 // Return it. 1275 *phPrinter = (HANDLE)pHandle; 1276 return ERROR_SUCCESS; 1277 } 1278 1279 static DWORD 1280 _LocalOpenXcvHandle(PWSTR pwszParameter, PHANDLE phPrinter) 1281 { 1282 BOOL bReturnValue; 1283 DWORD dwErrorCode; 1284 HANDLE hXcv; 1285 PLOCAL_HANDLE pHandle = NULL; 1286 PLOCAL_PORT pPort; 1287 PLOCAL_PRINT_MONITOR pPrintMonitor; 1288 PLOCAL_XCV_HANDLE pXcvHandle = NULL; 1289 1290 // Skip the "Xcv" string. 1291 pwszParameter += 3; 1292 1293 // Is XcvMonitor or XcvPort requested? 1294 if (wcsncmp(pwszParameter, L"Monitor ", 8) == 0) 1295 { 1296 // Skip the "Monitor " string. 1297 pwszParameter += 8; 1298 1299 // Look for this monitor in our Print Monitor list. 1300 pPrintMonitor = FindPrintMonitor(pwszParameter); 1301 if (!pPrintMonitor) 1302 { 1303 // The caller supplied a non-existing Monitor name. 1304 dwErrorCode = ERROR_INVALID_NAME; 1305 ERR("OpenXcvHandle failed on Monitor name! %lu\n", dwErrorCode); 1306 goto Failure; 1307 } 1308 } 1309 else if (wcsncmp(pwszParameter, L"Port ", 5) == 0) 1310 { 1311 // Skip the "Port " string. 1312 pwszParameter += 5; 1313 1314 // Look for this port in our Print Monitor Port list. 1315 pPort = FindPort(pwszParameter); 1316 if (!pPort) 1317 { 1318 // The supplied port is unknown to all our Print Monitors. 1319 dwErrorCode = ERROR_INVALID_NAME; 1320 ERR("OpenXcvHandle failed on Port name! %lu\n", dwErrorCode); 1321 goto Failure; 1322 } 1323 1324 pPrintMonitor = pPort->pPrintMonitor; 1325 } 1326 else 1327 { 1328 dwErrorCode = ERROR_INVALID_NAME; 1329 ERR("OpenXcvHandle failed on bad name! %lu\n", dwErrorCode); 1330 goto Failure; 1331 } 1332 1333 // Call the monitor's XcvOpenPort function. 1334 if (pPrintMonitor->bIsLevel2) 1335 bReturnValue = ((PMONITOR2)pPrintMonitor->pMonitor)->pfnXcvOpenPort(pPrintMonitor->hMonitor, pwszParameter, SERVER_EXECUTE, &hXcv); 1336 else 1337 bReturnValue = ((LPMONITOREX)pPrintMonitor->pMonitor)->Monitor.pfnXcvOpenPort(pwszParameter, SERVER_EXECUTE, &hXcv); 1338 1339 if (!bReturnValue) 1340 { 1341 // The XcvOpenPort function failed. Return its last error. 1342 dwErrorCode = GetLastError(); 1343 ERR("XcvOpenPort function failed! %lu\n", dwErrorCode); 1344 goto Failure; 1345 } 1346 1347 // Create a new generic handle. 1348 pHandle = DllAllocSplMem(sizeof(LOCAL_HANDLE)); 1349 if (!pHandle) 1350 { 1351 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1352 ERR("DllAllocSplMem failed!\n"); 1353 goto Failure; 1354 } 1355 1356 // Create a new LOCAL_XCV_HANDLE. 1357 pXcvHandle = DllAllocSplMem(sizeof(LOCAL_XCV_HANDLE)); 1358 if (!pXcvHandle) 1359 { 1360 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1361 ERR("DllAllocSplMem failed!\n"); 1362 goto Failure; 1363 } 1364 1365 pXcvHandle->hXcv = hXcv; 1366 pXcvHandle->pPrintMonitor = pPrintMonitor; 1367 1368 // Make the generic handle a Xcv handle. 1369 pHandle->HandleType = HandleType_Xcv; 1370 pHandle->pSpecificHandle = pXcvHandle; 1371 1372 // Return it. 1373 *phPrinter = (HANDLE)pHandle; 1374 ERR("OpenXcvHandle Success! %p\n", pXcvHandle); 1375 return ERROR_SUCCESS; 1376 1377 Failure: 1378 if (pHandle) 1379 DllFreeSplMem(pHandle); 1380 1381 if (pXcvHandle) 1382 DllFreeSplMem(pXcvHandle); 1383 1384 return dwErrorCode; 1385 } 1386 1387 // 1388 // Dead API 1389 // 1390 DWORD WINAPI 1391 LocalPrinterMessageBox(HANDLE hPrinter, DWORD Error, HWND hWnd, LPWSTR pText, LPWSTR pCaption, DWORD dwType) 1392 { 1393 SetLastError(ERROR_INVALID_HANDLE); // Yes.... 1394 return 0; 1395 } 1396 1397 BOOL WINAPI 1398 LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault) 1399 { 1400 DWORD cchComputerName; 1401 DWORD cchFirstParameter; 1402 DWORD dwErrorCode; 1403 PWSTR p = lpPrinterName; 1404 PWSTR pwszFirstParameter = NULL; 1405 PWSTR pwszSecondParameter; 1406 WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1]; 1407 1408 TRACE("LocalOpenPrinter(%S, %p, %p)\n", lpPrinterName, phPrinter, pDefault); 1409 1410 ASSERT(phPrinter); 1411 *phPrinter = NULL; 1412 1413 if (!lpPrinterName) 1414 { 1415 // The caller wants a Print Server handle and provided a NULL string. 1416 dwErrorCode = _LocalOpenPrintServerHandle(phPrinter); 1417 goto Cleanup; 1418 } 1419 1420 // Skip any server name in the first parameter. 1421 // Does lpPrinterName begin with two backslashes to indicate a server name? 1422 if (lpPrinterName[0] == L'\\' && lpPrinterName[1] == L'\\') 1423 { 1424 // Skip these two backslashes. 1425 lpPrinterName += 2; 1426 1427 // Look for the terminating null character or closing backslash. 1428 p = lpPrinterName; 1429 while (*p != L'\0' && *p != L'\\') 1430 p++; 1431 1432 // Get the local computer name for comparison. 1433 cchComputerName = _countof(wszComputerName); 1434 if (!GetComputerNameW(wszComputerName, &cchComputerName)) 1435 { 1436 dwErrorCode = GetLastError(); 1437 ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode); 1438 goto Cleanup; 1439 } 1440 1441 // Now compare this string excerpt with the local computer name. 1442 // The input parameter may not be writable, so we can't null-terminate the input string at this point. 1443 // This print provider only supports local printers, so both strings have to match. 1444 if (p - lpPrinterName != cchComputerName || _wcsnicmp(lpPrinterName, wszComputerName, cchComputerName) != 0) 1445 { 1446 dwErrorCode = ERROR_INVALID_NAME; 1447 goto Cleanup; 1448 } 1449 1450 // If lpPrinterName is only "\\COMPUTERNAME" with nothing more, the caller wants a handle to the local Print Server. 1451 if (!*p) 1452 { 1453 // The caller wants a Print Server handle and provided a string like: 1454 // "\\COMPUTERNAME" 1455 dwErrorCode = _LocalOpenPrintServerHandle(phPrinter); 1456 goto Cleanup; 1457 } 1458 1459 // We have checked the server name and don't need it anymore. 1460 lpPrinterName = p + 1; 1461 } 1462 1463 // Look for a comma. If it exists, it indicates the end of the first parameter. 1464 pwszSecondParameter = wcschr(lpPrinterName, L','); 1465 if (pwszSecondParameter) 1466 cchFirstParameter = pwszSecondParameter - p; 1467 else 1468 cchFirstParameter = wcslen(lpPrinterName); 1469 1470 // We must have at least one parameter. 1471 if (!cchFirstParameter && !pwszSecondParameter) 1472 { 1473 dwErrorCode = ERROR_INVALID_NAME; 1474 goto Cleanup; 1475 } 1476 1477 // Do we have a first parameter? 1478 if (cchFirstParameter) 1479 { 1480 // Yes, extract it. 1481 // No null-termination is necessary here, because DllAllocSplMem returns a zero-initialized buffer. 1482 pwszFirstParameter = DllAllocSplMem((cchFirstParameter + 1) * sizeof(WCHAR)); 1483 CopyMemory(pwszFirstParameter, lpPrinterName, cchFirstParameter * sizeof(WCHAR)); 1484 } 1485 1486 // Do we have a second parameter? 1487 if (pwszSecondParameter) 1488 { 1489 // Yes, skip the comma at the beginning. 1490 ++pwszSecondParameter; 1491 1492 // Skip whitespace as well. 1493 while (*pwszSecondParameter == L' ') 1494 ++pwszSecondParameter; 1495 } 1496 1497 // Now we can finally check the type of handle actually requested. 1498 if (pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Port", 4) == 0) 1499 { 1500 // The caller wants a port handle and provided a string like: 1501 // "LPT1:, Port" 1502 // "\\COMPUTERNAME\LPT1:, Port" 1503 dwErrorCode = _LocalOpenPortHandle(pwszFirstParameter, phPrinter); 1504 } 1505 else if (!pwszFirstParameter && pwszSecondParameter && wcsncmp(pwszSecondParameter, L"Xcv", 3) == 0) 1506 { 1507 // The caller wants an Xcv handle and provided a string like: 1508 // ", XcvMonitor Local Port" 1509 // "\\COMPUTERNAME\, XcvMonitor Local Port" 1510 // ", XcvPort LPT1:" 1511 // "\\COMPUTERNAME\, XcvPort LPT1:" 1512 FIXME("OpenXcvHandle : %S\n",pwszSecondParameter); 1513 dwErrorCode = _LocalOpenXcvHandle(pwszSecondParameter, phPrinter); 1514 } 1515 else 1516 { 1517 // The caller wants a Printer or Printer Job handle and provided a string like: 1518 // "HP DeskJet" 1519 // "\\COMPUTERNAME\HP DeskJet" 1520 // "HP DeskJet, Job 5" 1521 // "\\COMPUTERNAME\HP DeskJet, Job 5" 1522 dwErrorCode = _LocalOpenPrinterHandle(pwszFirstParameter, pwszSecondParameter, phPrinter, pDefault); 1523 } 1524 1525 Cleanup: 1526 if (pwszFirstParameter) 1527 DllFreeSplMem(pwszFirstParameter); 1528 1529 SetLastError(dwErrorCode); 1530 return (dwErrorCode == ERROR_SUCCESS); 1531 } 1532 1533 BOOL WINAPI 1534 LocalReadPrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pNoBytesRead) 1535 { 1536 BOOL bReturnValue; 1537 DWORD dwErrorCode; 1538 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; 1539 PLOCAL_PORT_HANDLE pPortHandle; 1540 PLOCAL_PRINTER_HANDLE pPrinterHandle; 1541 1542 TRACE("LocalReadPrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pNoBytesRead); 1543 1544 // Sanity checks. 1545 if (!pHandle) 1546 { 1547 dwErrorCode = ERROR_INVALID_HANDLE; 1548 goto Cleanup; 1549 } 1550 1551 // Port handles are an entirely different thing. 1552 if (pHandle->HandleType == HandleType_Port) 1553 { 1554 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle; 1555 1556 // Call the monitor's ReadPort function. 1557 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2) 1558 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead); 1559 else 1560 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnReadPort(pPortHandle->hPort, pBuf, cbBuf, pNoBytesRead); 1561 1562 if (!bReturnValue) 1563 { 1564 // The ReadPort function failed. Return its last error. 1565 dwErrorCode = GetLastError(); 1566 goto Cleanup; 1567 } 1568 1569 // We were successful! 1570 dwErrorCode = ERROR_SUCCESS; 1571 goto Cleanup; 1572 } 1573 1574 // The remaining function deals with Printer handles only. 1575 if (pHandle->HandleType != HandleType_Printer) 1576 { 1577 dwErrorCode = ERROR_INVALID_HANDLE; 1578 goto Cleanup; 1579 } 1580 1581 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle; 1582 1583 // ReadPrinter needs an opened SPL file to work. 1584 // This only works if a Printer Job Handle was requested in OpenPrinter. 1585 if (pPrinterHandle->hSPLFile == INVALID_HANDLE_VALUE) 1586 { 1587 dwErrorCode = ERROR_INVALID_HANDLE; 1588 goto Cleanup; 1589 } 1590 1591 // Pass the parameters to ReadFile. 1592 if (!ReadFile(pPrinterHandle->hSPLFile, pBuf, cbBuf, pNoBytesRead, NULL)) 1593 { 1594 dwErrorCode = GetLastError(); 1595 ERR("ReadFile failed with error %lu!\n", dwErrorCode); 1596 goto Cleanup; 1597 } 1598 1599 dwErrorCode = ERROR_SUCCESS; 1600 1601 Cleanup: 1602 SetLastError(dwErrorCode); 1603 return (dwErrorCode == ERROR_SUCCESS); 1604 } 1605 1606 DWORD WINAPI 1607 LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, PBYTE pDocInfo) 1608 { 1609 BOOL bReturnValue; 1610 DWORD dwErrorCode; 1611 DWORD dwReturnValue = 0; 1612 PDOC_INFO_1W pDocInfo1 = (PDOC_INFO_1W)pDocInfo; 1613 PLOCAL_JOB pJob; 1614 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; 1615 PLOCAL_PORT_HANDLE pPortHandle; 1616 PLOCAL_PRINTER_HANDLE pPrinterHandle; 1617 1618 TRACE("LocalStartDocPrinter(%p, %lu, %p)\n", hPrinter, Level, pDocInfo); 1619 1620 // Sanity checks. 1621 if (!pHandle) 1622 { 1623 dwErrorCode = ERROR_INVALID_HANDLE; 1624 goto Cleanup; 1625 } 1626 1627 // Port handles are an entirely different thing. 1628 if (pHandle->HandleType == HandleType_Port) 1629 { 1630 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle; 1631 1632 // This call should come from a Print Processor and the job this port is going to print was assigned to us before opening the Print Processor. 1633 // Claim it exclusively for this port handle. 1634 pJob = pPortHandle->pPort->pNextJobToProcess; 1635 pPortHandle->pPort->pNextJobToProcess = NULL; 1636 ASSERT(pJob); 1637 1638 // Call the monitor's StartDocPort function. 1639 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2) 1640 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo); 1641 else 1642 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnStartDocPort(pPortHandle->hPort, pJob->pPrinter->pwszPrinterName, pJob->dwJobID, Level, pDocInfo); 1643 1644 if (!bReturnValue) 1645 { 1646 // The StartDocPort function failed. Return its last error. 1647 dwErrorCode = GetLastError(); 1648 goto Cleanup; 1649 } 1650 1651 // We were successful! 1652 dwErrorCode = ERROR_SUCCESS; 1653 dwReturnValue = pJob->dwJobID; 1654 goto Cleanup; 1655 } 1656 1657 // The remaining function deals with Printer handles only. 1658 if (pHandle->HandleType != HandleType_Printer) 1659 { 1660 dwErrorCode = ERROR_INVALID_HANDLE; 1661 goto Cleanup; 1662 } 1663 1664 if (!pDocInfo1) 1665 { 1666 dwErrorCode = ERROR_INVALID_PARAMETER; 1667 goto Cleanup; 1668 } 1669 1670 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle; 1671 1672 // pJob may already be occupied if this is a Print Job handle. In this case, StartDocPrinter has to fail. 1673 if (pPrinterHandle->pJob) 1674 { 1675 dwErrorCode = ERROR_INVALID_PARAMETER; 1676 goto Cleanup; 1677 } 1678 1679 // Check the validity of the datatype if we got one. 1680 if (pDocInfo1->pDatatype && !FindDatatype(pPrinterHandle->pJob->pPrintProcessor, pDocInfo1->pDatatype)) 1681 { 1682 dwErrorCode = ERROR_INVALID_DATATYPE; 1683 goto Cleanup; 1684 } 1685 1686 // Check if this is the right document information level. 1687 if (Level != 1) 1688 { 1689 dwErrorCode = ERROR_INVALID_LEVEL; 1690 goto Cleanup; 1691 } 1692 1693 // All requirements are met - create a new job. 1694 dwErrorCode = CreateJob(pPrinterHandle); 1695 if (dwErrorCode != ERROR_SUCCESS) 1696 goto Cleanup; 1697 1698 // Use any given datatype. 1699 if (pDocInfo1->pDatatype && !ReallocSplStr(&pPrinterHandle->pJob->pwszDatatype, pDocInfo1->pDatatype)) 1700 { 1701 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1702 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError()); 1703 goto Cleanup; 1704 } 1705 1706 // Use any given document name. 1707 if (pDocInfo1->pDocName && !ReallocSplStr(&pPrinterHandle->pJob->pwszDocumentName, pDocInfo1->pDocName)) 1708 { 1709 dwErrorCode = ERROR_NOT_ENOUGH_MEMORY; 1710 ERR("ReallocSplStr failed, last error is %lu!\n", GetLastError()); 1711 goto Cleanup; 1712 } 1713 1714 // We were successful! 1715 dwErrorCode = ERROR_SUCCESS; 1716 dwReturnValue = pPrinterHandle->pJob->dwJobID; 1717 1718 Cleanup: 1719 SetLastError(dwErrorCode); 1720 return dwReturnValue; 1721 } 1722 1723 BOOL WINAPI 1724 LocalStartPagePrinter(HANDLE hPrinter) 1725 { 1726 DWORD dwErrorCode; 1727 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; 1728 PLOCAL_PRINTER_HANDLE pPrinterHandle; 1729 1730 TRACE("LocalStartPagePrinter(%p)\n", hPrinter); 1731 1732 // Sanity checks. 1733 if (!pHandle || pHandle->HandleType != HandleType_Printer) 1734 { 1735 dwErrorCode = ERROR_INVALID_HANDLE; 1736 goto Cleanup; 1737 } 1738 1739 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle; 1740 1741 // We require StartDocPrinter or AddJob to be called first. 1742 if (!pPrinterHandle->bStartedDoc) 1743 { 1744 dwErrorCode = ERROR_SPL_NO_STARTDOC; 1745 goto Cleanup; 1746 } 1747 1748 // Increase the page count. 1749 ++pPrinterHandle->pJob->dwTotalPages; 1750 dwErrorCode = ERROR_SUCCESS; 1751 1752 Cleanup: 1753 SetLastError(dwErrorCode); 1754 return (dwErrorCode == ERROR_SUCCESS); 1755 } 1756 1757 BOOL WINAPI 1758 LocalWritePrinter(HANDLE hPrinter, PVOID pBuf, DWORD cbBuf, PDWORD pcWritten) 1759 { 1760 BOOL bReturnValue; 1761 DWORD dwErrorCode; 1762 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; 1763 PLOCAL_PORT_HANDLE pPortHandle; 1764 PLOCAL_PRINTER_HANDLE pPrinterHandle; 1765 1766 TRACE("LocalWritePrinter(%p, %p, %lu, %p)\n", hPrinter, pBuf, cbBuf, pcWritten); 1767 1768 // Sanity checks. 1769 if (!pHandle) 1770 { 1771 dwErrorCode = ERROR_INVALID_HANDLE; 1772 goto Cleanup; 1773 } 1774 1775 // Port handles are an entirely different thing. 1776 if (pHandle->HandleType == HandleType_Port) 1777 { 1778 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle; 1779 1780 // Call the monitor's WritePort function. 1781 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2) 1782 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten); 1783 else 1784 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnWritePort(pPortHandle->hPort, pBuf, cbBuf, pcWritten); 1785 1786 if (!bReturnValue) 1787 { 1788 // The WritePort function failed. Return its last error. 1789 dwErrorCode = GetLastError(); 1790 goto Cleanup; 1791 } 1792 1793 // We were successful! 1794 dwErrorCode = ERROR_SUCCESS; 1795 goto Cleanup; 1796 } 1797 1798 // The remaining function deals with Printer handles only. 1799 if (pHandle->HandleType != HandleType_Printer) 1800 { 1801 dwErrorCode = ERROR_INVALID_HANDLE; 1802 goto Cleanup; 1803 } 1804 1805 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle; 1806 1807 // We require StartDocPrinter or AddJob to be called first. 1808 if (!pPrinterHandle->bStartedDoc) 1809 { 1810 dwErrorCode = ERROR_SPL_NO_STARTDOC; 1811 goto Cleanup; 1812 } 1813 1814 // TODO: This function is only called when doing non-spooled printing. 1815 // This needs to be investigated further. We can't just use pPrinterHandle->hSPLFile here, because that's currently reserved for Printer Job handles (see LocalReadPrinter). 1816 #if 0 1817 // Pass the parameters to WriteFile. 1818 if (!WriteFile(SOME_SPOOL_FILE_HANDLE, pBuf, cbBuf, pcWritten, NULL)) 1819 { 1820 dwErrorCode = GetLastError(); 1821 ERR("WriteFile failed with error %lu!\n", GetLastError()); 1822 goto Cleanup; 1823 } 1824 #endif 1825 1826 dwErrorCode = ERROR_SUCCESS; 1827 1828 Cleanup: 1829 SetLastError(dwErrorCode); 1830 return (dwErrorCode == ERROR_SUCCESS); 1831 } 1832 1833 BOOL WINAPI 1834 LocalEndPagePrinter(HANDLE hPrinter) 1835 { 1836 DWORD dwErrorCode; 1837 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; 1838 1839 TRACE("LocalEndPagePrinter(%p)\n", hPrinter); 1840 1841 // Sanity checks. 1842 if (!pHandle || pHandle->HandleType != HandleType_Printer) 1843 { 1844 dwErrorCode = ERROR_INVALID_HANDLE; 1845 goto Cleanup; 1846 } 1847 1848 // This function doesn't do anything else for now. 1849 dwErrorCode = ERROR_SUCCESS; 1850 1851 Cleanup: 1852 SetLastError(dwErrorCode); 1853 return (dwErrorCode == ERROR_SUCCESS); 1854 } 1855 1856 BOOL WINAPI 1857 LocalEndDocPrinter(HANDLE hPrinter) 1858 { 1859 BOOL bReturnValue; 1860 DWORD dwErrorCode; 1861 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; 1862 PLOCAL_PORT_HANDLE pPortHandle; 1863 PLOCAL_PRINTER_HANDLE pPrinterHandle; 1864 1865 TRACE("LocalEndDocPrinter(%p)\n", hPrinter); 1866 1867 // Sanity checks. 1868 if (!pHandle) 1869 { 1870 dwErrorCode = ERROR_INVALID_HANDLE; 1871 goto Cleanup; 1872 } 1873 1874 // Port handles are an entirely different thing. 1875 if (pHandle->HandleType == HandleType_Port) 1876 { 1877 pPortHandle = (PLOCAL_PORT_HANDLE)pHandle->pSpecificHandle; 1878 1879 // Call the monitor's EndDocPort function. 1880 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2) 1881 bReturnValue = ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnEndDocPort(pPortHandle->hPort); 1882 else 1883 bReturnValue = ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnEndDocPort(pPortHandle->hPort); 1884 1885 if (!bReturnValue) 1886 { 1887 // The EndDocPort function failed. Return its last error. 1888 dwErrorCode = GetLastError(); 1889 goto Cleanup; 1890 } 1891 1892 // We were successful! 1893 dwErrorCode = ERROR_SUCCESS; 1894 goto Cleanup; 1895 } 1896 1897 // The remaining function deals with Printer handles only. 1898 if (pHandle->HandleType != HandleType_Printer) 1899 { 1900 dwErrorCode = ERROR_INVALID_HANDLE; 1901 goto Cleanup; 1902 } 1903 1904 pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle; 1905 1906 // We require StartDocPrinter or AddJob to be called first. 1907 if (!pPrinterHandle->bStartedDoc) 1908 { 1909 dwErrorCode = ERROR_SPL_NO_STARTDOC; 1910 goto Cleanup; 1911 } 1912 1913 // TODO: Something like ScheduleJob 1914 1915 // Finish the job. 1916 pPrinterHandle->bStartedDoc = FALSE; 1917 pPrinterHandle->pJob = NULL; 1918 dwErrorCode = ERROR_SUCCESS; 1919 1920 Cleanup: 1921 SetLastError(dwErrorCode); 1922 return (dwErrorCode == ERROR_SUCCESS); 1923 } 1924 1925 static void 1926 _LocalClosePortHandle(PLOCAL_PORT_HANDLE pPortHandle) 1927 { 1928 FIXME("LocalClosePortHandle\n"); 1929 // Call the monitor's ClosePort function. 1930 if (pPortHandle->pPort->pPrintMonitor->bIsLevel2) 1931 ((PMONITOR2)pPortHandle->pPort->pPrintMonitor->pMonitor)->pfnClosePort(pPortHandle->hPort); 1932 else 1933 ((LPMONITOREX)pPortHandle->pPort->pPrintMonitor->pMonitor)->Monitor.pfnClosePort(pPortHandle->hPort); 1934 } 1935 1936 static void 1937 _LocalClosePrinterHandle(PLOCAL_PRINTER_HANDLE pPrinterHandle) 1938 { 1939 FIXME("LocalClosePrinterHandle\n"); 1940 // Terminate any started job. 1941 if (pPrinterHandle->pJob) 1942 FreeJob(pPrinterHandle->pJob); 1943 1944 // Free memory for the fields. 1945 DllFreeSplMem(pPrinterHandle->pDevMode); 1946 DllFreeSplStr(pPrinterHandle->pwszDatatype); 1947 } 1948 1949 static void 1950 _LocalCloseXcvHandle(PLOCAL_XCV_HANDLE pXcvHandle) 1951 { 1952 // Call the monitor's XcvClosePort function. 1953 if (pXcvHandle->pPrintMonitor->bIsLevel2) 1954 ((PMONITOR2)pXcvHandle->pPrintMonitor->pMonitor)->pfnXcvClosePort(pXcvHandle->hXcv); 1955 else 1956 ((LPMONITOREX)pXcvHandle->pPrintMonitor->pMonitor)->Monitor.pfnXcvClosePort(pXcvHandle->hXcv); 1957 } 1958 1959 BOOL WINAPI 1960 LocalClosePrinter(HANDLE hPrinter) 1961 { 1962 PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter; 1963 1964 FIXME("LocalClosePrinter(%p)\n", hPrinter); 1965 1966 if (!pHandle) 1967 { 1968 SetLastError(ERROR_INVALID_HANDLE); 1969 return FALSE; 1970 } 1971 1972 if (pHandle->HandleType == HandleType_Port) 1973 { 1974 _LocalClosePortHandle(pHandle->pSpecificHandle); 1975 } 1976 else if (pHandle->HandleType == HandleType_Printer) 1977 { 1978 _LocalClosePrinterHandle(pHandle->pSpecificHandle); 1979 } 1980 else if (pHandle->HandleType == HandleType_PrintServer) 1981 { 1982 // Nothing to do. 1983 } 1984 else if (pHandle->HandleType == HandleType_Xcv) 1985 { 1986 _LocalCloseXcvHandle(pHandle->pSpecificHandle); 1987 } 1988 FIXME("LocalClosePrinter 1\n"); 1989 // Free memory for the handle and the specific handle (if any). 1990 if (pHandle->pSpecificHandle) 1991 DllFreeSplMem(pHandle->pSpecificHandle); 1992 FIXME("LocalClosePrinter 2\n"); 1993 DllFreeSplMem(pHandle); 1994 FIXME("LocalClosePrinter 3\n"); 1995 return TRUE; 1996 } 1997