// Global Variables
SKIPLIST PrinterList;
+// Forward Declarations
+static void _LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+static void _LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName);
+
// Local Constants
+typedef void (*PLocalGetPrinterLevelFunc)(PLOCAL_PRINTER, PVOID, PBYTE*, PDWORD, DWORD, PWSTR);
+
+static const PLocalGetPrinterLevelFunc pfnGetPrinterLevels[] = {
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel0,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel1,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel2,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel3,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel4,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel5,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel6,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel7,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel8,
+ (PLocalGetPrinterLevelFunc)&_LocalGetPrinterLevel9
+};
+
+static DWORD dwPrinterInfo0Offsets[] = {
+ FIELD_OFFSET(PRINTER_INFO_STRESS, pPrinterName),
+ MAXDWORD
+};
+
static DWORD dwPrinterInfo1Offsets[] = {
FIELD_OFFSET(PRINTER_INFO_1W, pName),
FIELD_OFFSET(PRINTER_INFO_1W, pComment),
MAXDWORD
};
+static DWORD dwPrinterInfo2Offsets[] = {
+ FIELD_OFFSET(PRINTER_INFO_2W, pPrinterName),
+ FIELD_OFFSET(PRINTER_INFO_2W, pShareName),
+ FIELD_OFFSET(PRINTER_INFO_2W, pPortName),
+ FIELD_OFFSET(PRINTER_INFO_2W, pDriverName),
+ FIELD_OFFSET(PRINTER_INFO_2W, pComment),
+ FIELD_OFFSET(PRINTER_INFO_2W, pLocation),
+ FIELD_OFFSET(PRINTER_INFO_2W, pSepFile),
+ FIELD_OFFSET(PRINTER_INFO_2W, pPrintProcessor),
+ FIELD_OFFSET(PRINTER_INFO_2W, pDatatype),
+ FIELD_OFFSET(PRINTER_INFO_2W, pParameters),
+ MAXDWORD
+};
+
+static DWORD dwPrinterInfo4Offsets[] = {
+ FIELD_OFFSET(PRINTER_INFO_4W, pPrinterName),
+ MAXDWORD
+};
+
+static DWORD dwPrinterInfo5Offsets[] = {
+ FIELD_OFFSET(PRINTER_INFO_5W, pPrinterName),
+ FIELD_OFFSET(PRINTER_INFO_5W, pPortName),
+ MAXDWORD
+};
+
+/** These values serve no purpose anymore, but are still used in PRINTER_INFO_5 and
+ HKEY_CURRENT_USER\Software\Microsoft\Windows NT\CurrentVersion\PrinterPorts */
+static const DWORD dwDeviceNotSelectedTimeout = 15000;
+static const DWORD dwTransmissionRetryTimeout = 45000;
+
+
/**
* @name _PrinterListCompareRoutine
*
}
/**
- * @name _IsLocalComputerName
+ * @name _LocalEnumPrintersCheckName
+ *
+ * Checks the Name parameter supplied to a call to EnumPrinters.
*
- * Checks if the given Computer Name matches the local Computer Name.
+ * @param Flags
+ * Flags parameter of EnumPrinters.
*
* @param Name
- * Computer Name prepended with two backslashes to check.
+ * Name parameter of EnumPrinters to check.
*
* @param pwszComputerName
* Pointer to a string able to hold 2 + MAX_COMPUTERNAME_LENGTH + 1 + 1 characters.
- * Will contain a string "\\COMPUTERNAME\" on success that can be prepended in EnumPrinters.
+ * On return, it may contain a computer name to prepend in EnumPrinters depending on the case.
*
* @param pcchComputerName
- * On success, this pointer receives the length in characters of pwszComputerName.
+ * If a string to prepend is returned, this pointer receives its length in characters.
*
* @return
- * ERROR_SUCCESS on success or an error code on failure.
+ * ERROR_SUCCESS if processing in EnumPrinters can be continued.
+ * ERROR_INVALID_NAME if the Name parameter is invalid for the given flags and this Print Provider.
+ * Any other error code if GetComputerNameW fails. Error codes indicating failure should then be returned by EnumPrinters.
*/
static DWORD
-_IsLocalComputerName(PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
+_LocalEnumPrintersCheckName(DWORD Flags, PCWSTR Name, PWSTR pwszComputerName, PDWORD pcchComputerName)
{
- DWORD dwErrorCode;
+ PCWSTR pName;
+ PCWSTR pComputerName;
+
+ // If there is no Name parameter to check, we can just continue in EnumPrinters.
+ if (!Name)
+ return ERROR_SUCCESS;
+
+ // Check if Name does not begin with two backslashes (required for specifying Computer Names).
+ if (Name[0] != L'\\' || Name[1] != L'\\')
+ {
+ if (Flags & PRINTER_ENUM_NAME)
+ {
+ // If PRINTER_ENUM_NAME is specified, any given Name parameter may only contain the
+ // Print Provider Name or the local Computer Name.
+
+ // Compare with the Print Provider Name.
+ if (wcsicmp(Name, wszPrintProviderInfo[0]) == 0)
+ return ERROR_SUCCESS;
- // Prepend slashes to the computer name.
+ // Dismiss anything else.
+ return ERROR_INVALID_NAME;
+ }
+ else
+ {
+ // If PRINTER_ENUM_NAME is not specified, we just ignore anything that is not a Computer Name.
+ return ERROR_SUCCESS;
+ }
+ }
+
+ // Prepend the backslashes to the output computer name.
pwszComputerName[0] = L'\\';
pwszComputerName[1] = L'\\';
*pcchComputerName = MAX_COMPUTERNAME_LENGTH + 1;
if (!GetComputerNameW(&pwszComputerName[2], pcchComputerName))
{
- dwErrorCode = GetLastError();
- ERR("GetComputerNameW failed with error %lu!\n", dwErrorCode);
- goto Cleanup;
+ ERR("GetComputerNameW failed with error %lu!\n", GetLastError());
+ return GetLastError();
}
// Add the leading slashes to the total length.
*pcchComputerName += 2;
- // Now compare this with the local computer name and reject it with ERROR_INVALID_NAME if it doesn't match.
- if (wcsicmp(&Name[2], &pwszComputerName[2]) != 0)
+ // Compare both names.
+ pComputerName = &pwszComputerName[2];
+ pName = &Name[2];
+ for (;;)
{
- dwErrorCode = ERROR_INVALID_NAME;
- goto Cleanup;
- }
-
- // Add a trailing backslash to pwszComputerName, which will later be prepended in front of the printer names.
- pwszComputerName[(*pcchComputerName)++] = L'\\';
- pwszComputerName[*pcchComputerName] = 0;
+ // Are we at the end of the local Computer Name string?
+ if (!*pComputerName)
+ {
+ // Are we also at the end of the supplied Name parameter?
+ // A terminating NUL character and a backslash are both treated as the end, but they are treated differently.
+ if (!*pName)
+ {
+ // If both names match and Name ends with a NUL character, the computer name will be prepended in EnumPrinters.
+ // Add a trailing backslash for that.
+ pwszComputerName[(*pcchComputerName)++] = L'\\';
+ pwszComputerName[*pcchComputerName] = 0;
+ return ERROR_SUCCESS;
+ }
+ else if (*pName == L'\\')
+ {
+ if (Flags & PRINTER_ENUM_NAME)
+ {
+ // If PRINTER_ENUM_NAME is specified and a Name parameter is given, it must be exactly the local
+ // Computer Name with two backslashes prepended. Anything else (like "\\COMPUTERNAME\") is dismissed.
+ return ERROR_INVALID_NAME;
+ }
+ else
+ {
+ // If PRINTER_ENUM_NAME is not specified and a Name parameter is given, it may also end with a backslash.
+ // Only the Computer Name between the backslashes is checked then.
+ // This is largely undocumented, but verified by tests (see winspool_apitest).
+ // In this case, no computer name is prepended in EnumPrinters though.
+ *pwszComputerName = 0;
+ *pcchComputerName = 0;
+ return ERROR_SUCCESS;
+ }
+ }
+ }
- dwErrorCode = ERROR_SUCCESS;
+ // Compare both Computer Names case-insensitively and reject with ERROR_INVALID_NAME if they don't match.
+ if (towlower(*pName) != towlower(*pComputerName))
+ return ERROR_INVALID_NAME;
-Cleanup:
- return dwErrorCode;
+ pName++;
+ pComputerName++;
+ }
}
static DWORD
return ERROR_SUCCESS;
}
-static DWORD
-_LocalEnumPrintersLevel0(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+static void
+_LocalGetPrinterLevel0(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_STRESS* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
{
- return ERROR_INVALID_LEVEL;
+ size_t cbName;
+ PWSTR p;
+ PWSTR pwszStrings[1];
+ SYSTEM_INFO SystemInfo;
+
+ // Calculate the string lengths.
+ cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+
+ if (!ppPrinterInfo)
+ {
+ *pcbNeeded += sizeof(PRINTER_INFO_STRESS) + cbName;
+ return;
+ }
+
+ // Set the general fields.
+ ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_STRESS));
+ (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount;
+ (*ppPrinterInfo)->dwGetVersion = GetVersion();
+ (*ppPrinterInfo)->Status = pPrinter->dwStatus;
+
+#if !defined(DBG)
+ (*ppPrinterInfo)->fFreeBuild = 1;
+#endif
+
+ GetSystemInfo(&SystemInfo);
+ (*ppPrinterInfo)->dwNumberOfProcessors = SystemInfo.dwNumberOfProcessors;
+ (*ppPrinterInfo)->dwProcessorType = SystemInfo.dwProcessorType;
+ (*ppPrinterInfo)->wProcessorArchitecture = SystemInfo.wProcessorArchitecture;
+ (*ppPrinterInfo)->wProcessorLevel = SystemInfo.wProcessorLevel;
+
+ // Copy the Printer Name.
+ pwszStrings[0] = DllAllocSplMem(cbName);
+ p = pwszStrings[0];
+ StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
+ StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
+
+ // Finally copy the structure and advance to the next one in the output buffer.
+ *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo0Offsets, *ppPrinterInfoEnd);
+ (*ppPrinterInfo)++;
+
+ // Free the memory for temporary strings.
+ DllFreeSplMem(pwszStrings[0]);
}
-static DWORD
-_LocalEnumPrintersLevel1(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+static void
+_LocalGetPrinterLevel1(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_1W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
{
const WCHAR wszComma[] = L",";
size_t cbName;
size_t cbComment;
size_t cbDescription;
- DWORD cchComputerName = 0;
- DWORD dwErrorCode;
- DWORD i;
- PBYTE pPrinterInfo;
- PBYTE pPrinterStrings;
- PSKIPLIST_NODE pNode;
- PLOCAL_PRINTER pPrinter;
PWSTR p;
PWSTR pwszStrings[3];
- WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
- if (Flags & PRINTER_ENUM_NAME)
+ // Calculate the string lengths.
+ // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
+ // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
+ cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+ cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
+ cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+
+ if (!ppPrinterInfo)
{
- if (Name)
- {
- // The user supplied a Computer Name (with leading double backslashes) or Print Provider Name.
- // Only process what's directed at us.
- if (Name[0] == L'\\' && Name[1] == L'\\')
- {
- dwErrorCode = _IsLocalComputerName(Name, wszComputerName, &cchComputerName);
- if (dwErrorCode != ERROR_SUCCESS)
- goto Cleanup;
- }
- else if (wcsicmp(Name, wszPrintProviderInfo[0]) != 0)
- {
- // The user supplied a name that cannot be processed by the Local Print Provider.
- dwErrorCode = ERROR_INVALID_NAME;
- goto Cleanup;
- }
- }
- else
- {
- // The caller wants information about this Print Provider.
- // spoolss packs this into an array of information about all Print Providers.
- dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
- goto Cleanup;
- }
+ *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
+ return;
}
- // Count the required buffer size and the number of printers.
- i = 0;
+ // Indicate that this is a Printer.
+ (*ppPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
+
+ // Copy the Printer Name.
+ pwszStrings[0] = DllAllocSplMem(cbName);
+ p = pwszStrings[0];
+ StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
+ StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
+
+ // Copy the Printer comment (equals the "Description" registry value).
+ pwszStrings[1] = pPrinter->pwszDescription;
+
+ // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
+ pwszStrings[2] = DllAllocSplMem(cbDescription);
+ p = pwszStrings[2];
+ StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
+ StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
+ StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
+ StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
+ StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
+ StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
+
+ // Finally copy the structure and advance to the next one in the output buffer.
+ *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo1Offsets, *ppPrinterInfoEnd);
+ (*ppPrinterInfo)++;
+
+ // Free the memory for temporary strings.
+ DllFreeSplMem(pwszStrings[0]);
+ DllFreeSplMem(pwszStrings[2]);
+}
- for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
- {
- pPrinter = (PLOCAL_PRINTER)pNode->Element;
+static void
+_LocalGetPrinterLevel2(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_2W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+ WCHAR wszEmpty[] = L"";
- // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
- if (Flags & PRINTER_ENUM_SHARED)
- continue;
+ size_t cbDevMode;
+ size_t cbPrinterName;
+ size_t cbShareName;
+ size_t cbPortName;
+ size_t cbDriverName;
+ size_t cbComment;
+ size_t cbLocation;
+ size_t cbSepFile;
+ size_t cbPrintProcessor;
+ size_t cbDatatype;
+ size_t cbParameters;
+ PWSTR p;
+ PWSTR pwszStrings[10];
- // Attention: pComment equals the "Description" registry value while pDescription is concatenated out of several strings.
- // On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
- cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
- cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
- cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+ // Calculate the string lengths.
+ cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
+ cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
- *pcbNeeded += sizeof(PRINTER_INFO_1W) + cbName + cbComment + cbDescription;
- i++;
+ if (!ppPrinterInfo)
+ {
+ // Attention: pComment equals the "Description" registry value.
+ cbShareName = sizeof(wszEmpty);
+ cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR);
+ cbDriverName = (wcslen(pPrinter->pwszPrinterDriver) + 1) * sizeof(WCHAR);
+ cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
+ cbLocation = (wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+ cbSepFile = sizeof(wszEmpty);
+ cbPrintProcessor = (wcslen(pPrinter->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
+ cbDatatype = (wcslen(pPrinter->pwszDefaultDatatype) + 1) * sizeof(WCHAR);
+ cbParameters = sizeof(wszEmpty);
+
+ *pcbNeeded += sizeof(PRINTER_INFO_2W) + cbDevMode + cbPrinterName + cbShareName + cbPortName + cbDriverName + cbComment + cbLocation + cbSepFile + cbPrintProcessor + cbDatatype + cbParameters;
+ return;
}
- // Check if the supplied buffer is large enough.
- if (cbBuf < *pcbNeeded)
+ // Set the general fields.
+ ZeroMemory(*ppPrinterInfo, sizeof(PRINTER_INFO_2W));
+ (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
+ (*ppPrinterInfo)->cJobs = pPrinter->JobList.NodeCount;
+ (*ppPrinterInfo)->Status = pPrinter->dwStatus;
+
+ // Set the pDevMode field (and copy the DevMode).
+ *ppPrinterInfoEnd -= cbDevMode;
+ CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
+ (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
+
+ // Set the pPrinterName field.
+ pwszStrings[0] = DllAllocSplMem(cbPrinterName);
+ p = pwszStrings[0];
+ StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
+ StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
+
+ // Set the pShareName field.
+ pwszStrings[1] = wszEmpty;
+
+ // Set the pPortName field.
+ pwszStrings[2] = pPrinter->pPort->pwszName;
+
+ // Set the pDriverName field.
+ pwszStrings[3] = pPrinter->pwszPrinterDriver;
+
+ // Set the pComment field ((equals the "Description" registry value).
+ pwszStrings[4] = pPrinter->pwszDescription;
+
+ // Set the pLocation field.
+ pwszStrings[5] = pPrinter->pwszLocation;
+
+ // Set the pSepFile field.
+ pwszStrings[6] = wszEmpty;
+
+ // Set the pPrintProcessor field.
+ pwszStrings[7] = pPrinter->pPrintProcessor->pwszName;
+
+ // Set the pDatatype field.
+ pwszStrings[8] = pPrinter->pwszDefaultDatatype;
+
+ // Set the pParameters field.
+ pwszStrings[9] = wszEmpty;
+
+ // Finally copy the structure and advance to the next one in the output buffer.
+ *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo2Offsets, *ppPrinterInfoEnd);
+ (*ppPrinterInfo)++;
+
+ // Free the memory for temporary strings.
+ DllFreeSplMem(pwszStrings[0]);
+}
+
+static void
+_LocalGetPrinterLevel3(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_3* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+ SECURITY_DESCRIPTOR SecurityDescriptor = { 0 };
+
+ if (!ppPrinterInfo)
{
- dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
- goto Cleanup;
+ *pcbNeeded += sizeof(PRINTER_INFO_3) + sizeof(SECURITY_DESCRIPTOR);
+ return;
}
- // Initialize the variables for filling the output buffer using PackStrings.
- pPrinterInfo = pPrinterEnum;
- pPrinterStrings = &pPrinterEnum[*pcbNeeded];
+ FIXME("Return a valid security descriptor for PRINTER_INFO_3\n");
- // Copy over the Printer information.
- for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
+ // Set the pSecurityDescriptor field (and copy the Security Descriptor).
+ *ppPrinterInfoEnd -= sizeof(SECURITY_DESCRIPTOR);
+ CopyMemory(*ppPrinterInfoEnd, &SecurityDescriptor, sizeof(SECURITY_DESCRIPTOR));
+ (*ppPrinterInfo)->pSecurityDescriptor = (PSECURITY_DESCRIPTOR)(*ppPrinterInfoEnd);
+
+ // Advance to the next structure.
+ (*ppPrinterInfo)++;
+}
+
+static void
+_LocalGetPrinterLevel4(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_4W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+ size_t cbPrinterName;
+ PWSTR p;
+ PWSTR pwszStrings[1];
+
+ // Calculate the string lengths.
+ cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
+
+ if (!ppPrinterInfo)
{
- pPrinter = (PLOCAL_PRINTER)pNode->Element;
+ *pcbNeeded += sizeof(PRINTER_INFO_4W) + cbPrinterName;
+ return;
+ }
- // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
- if (Flags & PRINTER_ENUM_SHARED)
- continue;
+ // Set the general fields.
+ (*ppPrinterInfo)->pServerName = NULL;
+ (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
- // Indicate that this is a Printer.
- ((PPRINTER_INFO_1W)pPrinterInfo)->Flags = PRINTER_ENUM_ICON8;
+ // Set the pPrinterName field.
+ pwszStrings[0] = DllAllocSplMem(cbPrinterName);
+ p = pwszStrings[0];
+ StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
+ StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
- // Calculate the string lengths.
- cbName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
- cbComment = (wcslen(pPrinter->pwszDescription) + 1) * sizeof(WCHAR);
- cbDescription = cbName + (wcslen(pPrinter->pwszPrinterDriver) + 1 + wcslen(pPrinter->pwszLocation) + 1) * sizeof(WCHAR);
+ // Finally copy the structure and advance to the next one in the output buffer.
+ *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo4Offsets, *ppPrinterInfoEnd);
+ (*ppPrinterInfo)++;
- // Copy the Printer Name.
- pwszStrings[0] = DllAllocSplMem(cbName);
- p = pwszStrings[0];
- StringCbCopyExW(p, cbName, wszComputerName, &p, &cbName, 0);
- StringCbCopyExW(p, cbName, pPrinter->pwszPrinterName, &p, &cbName, 0);
+ // Free the memory for temporary strings.
+ DllFreeSplMem(pwszStrings[0]);
+}
- // Copy the Printer comment (equals the "Description" registry value).
- pwszStrings[1] = pPrinter->pwszDescription;
+static void
+_LocalGetPrinterLevel5(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_5W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+ size_t cbPrinterName;
+ size_t cbPortName;
+ PWSTR p;
+ PWSTR pwszStrings[1];
- // Copy the description, which for PRINTER_INFO_1W has the form "Name,Printer Driver,Location"
- pwszStrings[2] = DllAllocSplMem(cbDescription);
- p = pwszStrings[2];
- StringCbCopyExW(p, cbDescription, wszComputerName, &p, &cbDescription, 0);
- StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterName, &p, &cbDescription, 0);
- StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
- StringCbCopyExW(p, cbDescription, pPrinter->pwszPrinterDriver, &p, &cbDescription, 0);
- StringCbCopyExW(p, cbDescription, wszComma, &p, &cbDescription, 0);
- StringCbCopyExW(p, cbDescription, pPrinter->pwszLocation, &p, &cbDescription, 0);
+ // Calculate the string lengths.
+ cbPrinterName = (cchComputerName + wcslen(pPrinter->pwszPrinterName) + 1) * sizeof(WCHAR);
- // Finally copy the structure and advance to the next one in the output buffer.
- pPrinterStrings = PackStrings(pwszStrings, pPrinterInfo, dwPrinterInfo1Offsets, pPrinterStrings);
- pPrinterInfo += sizeof(PRINTER_INFO_1W);
+ if (!ppPrinterInfo)
+ {
+ cbPortName = (wcslen(pPrinter->pPort->pwszName) + 1) * sizeof(WCHAR);
- // Free the memory for temporary strings.
- DllFreeSplMem(pwszStrings[0]);
- DllFreeSplMem(pwszStrings[2]);
+ *pcbNeeded += sizeof(PRINTER_INFO_5W) + cbPrinterName + cbPortName;
+ return;
}
- *pcReturned = i;
- dwErrorCode = ERROR_SUCCESS;
+ // Set the general fields.
+ (*ppPrinterInfo)->Attributes = pPrinter->dwAttributes;
+ (*ppPrinterInfo)->DeviceNotSelectedTimeout = dwDeviceNotSelectedTimeout;
+ (*ppPrinterInfo)->TransmissionRetryTimeout = dwTransmissionRetryTimeout;
-Cleanup:
- return dwErrorCode;
+ // Set the pPrinterName field.
+ pwszStrings[0] = DllAllocSplMem(cbPrinterName);
+ p = pwszStrings[0];
+ StringCbCopyExW(p, cbPrinterName, wszComputerName, &p, &cbPrinterName, 0);
+ StringCbCopyExW(p, cbPrinterName, pPrinter->pwszPrinterName, &p, &cbPrinterName, 0);
+
+ // Set the pPortName field.
+ pwszStrings[1] = pPrinter->pPort->pwszName;
+
+ // Finally copy the structure and advance to the next one in the output buffer.
+ *ppPrinterInfoEnd = PackStrings(pwszStrings, (PBYTE)(*ppPrinterInfo), dwPrinterInfo5Offsets, *ppPrinterInfoEnd);
+ (*ppPrinterInfo)++;
+
+ // Free the memory for temporary strings.
+ DllFreeSplMem(pwszStrings[0]);
}
-static DWORD
-_LocalEnumPrintersLevel2(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+static void
+_LocalGetPrinterLevel6(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_6* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
{
- return ERROR_INVALID_LEVEL;
+ if (!ppPrinterInfo)
+ {
+ *pcbNeeded += sizeof(PRINTER_INFO_6);
+ return;
+ }
+
+ // Set the general fields.
+ (*ppPrinterInfo)->dwStatus = pPrinter->dwStatus;
+
+ // Advance to the next structure.
+ (*ppPrinterInfo)++;
}
-static DWORD
-_LocalEnumPrintersLevel4(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+static void
+_LocalGetPrinterLevel7(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_7W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
{
- return ERROR_INVALID_LEVEL;
+ if (!ppPrinterInfo)
+ {
+ *pcbNeeded += sizeof(PRINTER_INFO_7W);
+ return;
+ }
+
+ FIXME("No Directory Support, returning DSPRINT_UNPUBLISH for PRINTER_INFO_7 all the time!\n");
+
+ // Set the general fields.
+ (*ppPrinterInfo)->dwAction = DSPRINT_UNPUBLISH;
+ (*ppPrinterInfo)->pszObjectGUID = NULL;
+
+ // Advance to the next structure.
+ (*ppPrinterInfo)++;
}
-static DWORD
-_LocalEnumPrintersLevel5(DWORD Flags, PCWSTR Name, PBYTE pPrinterEnum, DWORD cbBuf, PDWORD pcbNeeded, PDWORD pcReturned)
+static void
+_LocalGetPrinterLevel8(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_8W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
{
- return ERROR_INVALID_LEVEL;
+ DWORD cbDevMode;
+
+ // Calculate the string lengths.
+ cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
+
+ if (!ppPrinterInfo)
+ {
+ *pcbNeeded += sizeof(PRINTER_INFO_8W) + cbDevMode;
+ return;
+ }
+
+ // Set the pDevMode field (and copy the DevMode).
+ *ppPrinterInfoEnd -= cbDevMode;
+ CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
+ (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
+
+ // Advance to the next structure.
+ (*ppPrinterInfo)++;
+}
+
+static void
+_LocalGetPrinterLevel9(PLOCAL_PRINTER pPrinter, PPRINTER_INFO_9W* ppPrinterInfo, PBYTE* ppPrinterInfoEnd, PDWORD pcbNeeded, DWORD cchComputerName, PWSTR wszComputerName)
+{
+ DWORD cbDevMode;
+
+ // Calculate the string lengths.
+ cbDevMode = pPrinter->pDefaultDevMode->dmSize + pPrinter->pDefaultDevMode->dmDriverExtra;
+
+ if (!ppPrinterInfo)
+ {
+ *pcbNeeded += sizeof(PRINTER_INFO_9W) + cbDevMode;
+ return;
+ }
+
+ FIXME("Per-user settings are not yet implemented, returning the global DevMode for PRINTER_INFO_9!\n");
+
+ // Set the pDevMode field (and copy the DevMode).
+ *ppPrinterInfoEnd -= cbDevMode;
+ CopyMemory(*ppPrinterInfoEnd, pPrinter->pDefaultDevMode, cbDevMode);
+ (*ppPrinterInfo)->pDevMode = (PDEVMODEW)(*ppPrinterInfoEnd);
+
+ // Advance to the next structure.
+ (*ppPrinterInfo)++;
}
BOOL WINAPI
LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned)
{
+ DWORD cchComputerName = 0;
DWORD dwErrorCode;
+ DWORD i;
+ PBYTE pPrinterInfoEnd;
+ PSKIPLIST_NODE pNode;
+ WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1] = { 0 };
+ PLOCAL_PRINTER pPrinter;
- // Do no sanity checks here. This is verified by localspl_apitest!
+ ASSERT(pcbNeeded);
+ ASSERT(pcReturned);
// Begin counting.
*pcbNeeded = 0;
goto Cleanup;
}
- if (Level == 0)
+ if (Level == 3 || Level > 5)
{
- dwErrorCode = _LocalEnumPrintersLevel0(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+ // The caller supplied an invalid level for EnumPrinters.
+ dwErrorCode = ERROR_INVALID_LEVEL;
+ goto Cleanup;
}
- else if (Level == 1)
+
+ if (Level == 1 && Flags & PRINTER_ENUM_NAME && !Name)
{
- dwErrorCode = _LocalEnumPrintersLevel1(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+ // The caller wants information about this Print Provider.
+ // spoolss packs this into an array of information about all Print Providers.
+ dwErrorCode = _DumpLevel1PrintProviderInformation(pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+ goto Cleanup;
}
- else if (Level == 2)
+
+ // Check the supplied Name parameter (if any).
+ // This may return a Computer Name string we later prepend to the output.
+ dwErrorCode = _LocalEnumPrintersCheckName(Flags, Name, wszComputerName, &cchComputerName);
+ if (dwErrorCode != ERROR_SUCCESS)
+ goto Cleanup;
+
+ // Count the required buffer size and the number of printers.
+ i = 0;
+ for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
{
- dwErrorCode = _LocalEnumPrintersLevel2(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+ pPrinter = (PLOCAL_PRINTER)pNode->Element;
+
+ // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
+ if (Flags & PRINTER_ENUM_SHARED)
+ {
+ FIXME("Printer Sharing is not supported yet, returning no printers!\n");
+ continue;
+ }
+
+ pfnGetPrinterLevels[Level](pPrinter, NULL, NULL, pcbNeeded, cchComputerName, wszComputerName);
+ i++;
}
- else if (Level == 4)
+
+ // Check if the supplied buffer is large enough.
+ if (cbBuf < *pcbNeeded)
{
- dwErrorCode = _LocalEnumPrintersLevel4(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+ dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+ goto Cleanup;
}
- else if (Level == 5)
+
+ // Copy over the Printer information.
+ pPrinterInfoEnd = &pPrinterEnum[*pcbNeeded];
+
+ for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
{
- dwErrorCode = _LocalEnumPrintersLevel5(Flags, Name, pPrinterEnum, cbBuf, pcbNeeded, pcReturned);
+ pPrinter = (PLOCAL_PRINTER)pNode->Element;
+
+ // TODO: If PRINTER_ENUM_SHARED is given, add this Printer if it's shared instead of just ignoring it.
+ if (Flags & PRINTER_ENUM_SHARED)
+ continue;
+
+ pfnGetPrinterLevels[Level](pPrinter, &pPrinterEnum, &pPrinterInfoEnd, NULL, cchComputerName, wszComputerName);
}
- else
+
+ *pcReturned = i;
+ dwErrorCode = ERROR_SUCCESS;
+
+Cleanup:
+ SetLastError(dwErrorCode);
+ return (dwErrorCode == ERROR_SUCCESS);
+}
+
+BOOL WINAPI
+LocalGetPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pPrinter, DWORD cbBuf, LPDWORD pcbNeeded)
+{
+ DWORD dwErrorCode;
+ PBYTE pPrinterEnd;
+ PLOCAL_HANDLE pHandle = (PLOCAL_HANDLE)hPrinter;
+ PLOCAL_PRINTER_HANDLE pPrinterHandle;
+
+ // Check if this is a printer handle.
+ if (pHandle->HandleType != HandleType_Printer)
+ {
+ dwErrorCode = ERROR_INVALID_HANDLE;
+ goto Cleanup;
+ }
+
+ pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->pSpecificHandle;
+
+ if (Level > 9)
{
- // The caller supplied an invalid level.
+ // The caller supplied an invalid level for GetPrinter.
dwErrorCode = ERROR_INVALID_LEVEL;
+ goto Cleanup;
}
+ // Count the required buffer size.
+ pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, NULL, NULL, pcbNeeded, 0, NULL);
+
+ // Check if the supplied buffer is large enough.
+ if (cbBuf < *pcbNeeded)
+ {
+ dwErrorCode = ERROR_INSUFFICIENT_BUFFER;
+ goto Cleanup;
+ }
+
+ // Copy over the Printer information.
+ pPrinterEnd = &pPrinter[*pcbNeeded];
+ pfnGetPrinterLevels[Level](pPrinterHandle->pPrinter, &pPrinter, &pPrinterEnd, NULL, 0, NULL);
+ dwErrorCode = ERROR_SUCCESS;
+
Cleanup:
SetLastError(dwErrorCode);
return (dwErrorCode == ERROR_SUCCESS);