[LOCALSPL]

- Get rid of the Generic Tables entirely and make use of the new Skiplist for the Global Job List, the Printer's Job List and the Printer List.
  Use a doubly linked-list for the Print Processors (there won't be many) and just save the information returned from the Print Processor's EnumPrintProcessorDatatypesW instead of putting it in another structure.
- Implement LocalAddJob and LocalGetJob (with full JOB_INFO_1W and JOB_INFO_2W support)
  This makes use of the element index in the new Skiplist implementation to retrieve the position of the job in the list.
- Do some changes to LocalStartDocPrinter, though this will make use of LocalAddJob in the future.
- Make some global variables static again.

svn path=/branches/colins-printing-for-freedom/; revision=68195
This commit is contained in:
Colin Finck 2015-06-19 15:21:30 +00:00
parent a4d50f82ff
commit 9be537abb7
7 changed files with 689 additions and 238 deletions

View file

@ -1,6 +1,8 @@
spec2def(localspl.dll localspl.spec ADD_IMPORTLIB)
include_directories(${REACTOS_SOURCE_DIR}/lib/skiplist)
list(APPEND SOURCE
jobs.c
main.c
@ -16,7 +18,7 @@ add_library(localspl SHARED
${CMAKE_CURRENT_BINARY_DIR}/localspl.def)
set_module_type(localspl win32dll UNICODE)
target_link_libraries(localspl wine)
add_importlibs(localspl advapi32 spoolss msvcrt kernel32 ntdll)
target_link_libraries(localspl skiplist16 wine)
add_importlibs(localspl advapi32 rpcrt4 spoolss msvcrt kernel32 ntdll)
add_pch(localspl precomp.h SOURCE)
add_cd_file(TARGET localspl DESTINATION reactos/system32 FOR all)

View file

@ -7,10 +7,103 @@
#include "precomp.h"
LIST_ENTRY LocalJobQueue;
// Global Variables
SKIPLIST GlobalJobList;
// Local Variables
static DWORD _dwLastJobID;
/**
* @name _GlobalJobListCompareRoutine
*
* SKIPLIST_COMPARE_ROUTINE for the Global Job List.
* We need the Global Job List to check whether a Job ID is already in use. Consequently, this list is sorted by ID.
*/
static int WINAPI
_GlobalJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
{
PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
return A->dwJobID - B->dwJobID;
}
/**
* @name _PrinterJobListCompareRoutine
*
* SKIPLIST_COMPARE_ROUTINE for the each Printer's Job List.
* Jobs in this list are sorted in the desired order of processing.
*/
static int WINAPI
_PrinterJobListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
{
PLOCAL_JOB A = (PLOCAL_JOB)FirstStruct;
PLOCAL_JOB B = (PLOCAL_JOB)SecondStruct;
int iComparison;
FILETIME ftSubmittedA;
FILETIME ftSubmittedB;
ULARGE_INTEGER uliSubmittedA;
ULARGE_INTEGER uliSubmittedB;
ULONGLONG ullResult;
// First compare the priorities to determine the order.
// The job with a higher priority shall come first.
iComparison = A->dwPriority - B->dwPriority;
if (iComparison != 0)
return iComparison;
// Both have the same priority, so go by creation time.
// Comparison is done using the MSDN-recommended way for comparing SYSTEMTIMEs.
if (!SystemTimeToFileTime(&A->stSubmitted, &ftSubmittedA))
{
ERR("SystemTimeToFileTime failed for A with error %lu!\n", GetLastError());
return 0;
}
if (!SystemTimeToFileTime(&B->stSubmitted, &ftSubmittedB))
{
ERR("SystemTimeToFileTime failed for B with error %lu!\n", GetLastError());
return 0;
}
uliSubmittedA.LowPart = ftSubmittedA.dwLowDateTime;
uliSubmittedA.HighPart = ftSubmittedA.dwHighDateTime;
uliSubmittedB.LowPart = ftSubmittedB.dwLowDateTime;
uliSubmittedB.HighPart = ftSubmittedB.dwHighDateTime;
ullResult = uliSubmittedA.QuadPart - uliSubmittedB.QuadPart;
if (ullResult < 0)
return -1;
else if (ullResult > 0)
return 1;
return 0;
}
BOOL
GetNextJobID(PDWORD dwJobID)
{
++_dwLastJobID;
while (LookupElementSkiplist(&GlobalJobList, &_dwLastJobID, NULL))
{
// This ID is already taken. Try the next one.
++_dwLastJobID;
}
if (!IS_VALID_JOB_ID(_dwLastJobID))
{
ERR("Job ID %lu isn't valid!\n", _dwLastJobID);
return FALSE;
}
*dwJobID = _dwLastJobID;
return TRUE;
}
void
InitializeJobQueue()
InitializeGlobalJobList()
{
const WCHAR wszPath[] = L"\\PRINTERS\\?????.SHD";
const DWORD cchPath = _countof(wszPath) - 1;
@ -19,11 +112,18 @@ InitializeJobQueue()
DWORD dwJobID;
HANDLE hFind;
PLOCAL_JOB pJob;
PLOCAL_JOB pJob = NULL;
PWSTR p;
WCHAR wszFullPath[MAX_PATH];
WIN32_FIND_DATAW FindData;
// This one is incremented in GetNextJobID.
_dwLastJobID = 0;
// Initialize an empty list for all jobs of all local printers.
// We will search it by Job ID (supply a pointer to a DWORD in LookupElementSkiplist).
InitializeSkiplist(&GlobalJobList, DllAllocSplMem, _GlobalJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
// Construct the full path search pattern.
CopyMemory(wszFullPath, wszSpoolDirectory, cchSpoolDirectory * sizeof(WCHAR));
CopyMemory(&wszFullPath[cchSpoolDirectory], wszPath, (cchPath + 1) * sizeof(WCHAR));
@ -33,7 +133,7 @@ InitializeJobQueue()
if (hFind == INVALID_HANDLE_VALUE)
{
// No unfinished jobs found.
return;
goto Cleanup;
}
do
@ -56,12 +156,368 @@ InitializeJobQueue()
if (!pJob)
continue;
// Add it to the job queue of the respective printer.
InsertTailList(&pJob->Printer->JobQueue, &pJob->Entry);
// Add it to the Global Job List.
if (!InsertElementSkiplist(&GlobalJobList, pJob))
{
ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
goto Cleanup;
}
// Add it to the Printer's Job List.
if (!InsertElementSkiplist(&pJob->Printer->JobList, pJob))
{
ERR("InsertElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
goto Cleanup;
}
}
while (FindNextFileW(hFind, &FindData));
FindClose(hFind);
Cleanup:
// Outside the loop
if (hFind)
FindClose(hFind);
}
void
InitializePrinterJobList(PLOCAL_PRINTER pPrinter)
{
// Initialize an empty list for this printer's jobs.
// This one is only for sorting the jobs. If you need to lookup a job, search the GlobalJobList by Job ID.
InitializeSkiplist(&pPrinter->JobList, DllAllocSplMem, _PrinterJobListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
}
BOOL WINAPI
LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded)
{
const WCHAR wszDoubleBackslash[] = L"\\";
const DWORD cchDoubleBackslash = _countof(wszDoubleBackslash) - 1;
const WCHAR wszPrintersPath[] = L"\\PRINTERS\\";
const DWORD cchPrintersPath = _countof(wszPrintersPath) - 1;
const DWORD cchSpl = _countof("?????.SPL") - 1;
ADDJOB_INFO_1W AddJobInfo1;
BOOL bReturnValue = FALSE;
DWORD cchMachineName;
DWORD cchUserName;
PBYTE p;
PLOCAL_HANDLE pHandle;
PLOCAL_JOB pJob;
PLOCAL_PRINTER_HANDLE pPrinterHandle;
RPC_BINDING_HANDLE hServerBinding = NULL;
RPC_STATUS Status;
RPC_WSTR pwszBinding = NULL;
RPC_WSTR pwszMachineName = NULL;
// Check if this is a printer handle.
pHandle = (PLOCAL_HANDLE)hPrinter;
if (pHandle->HandleType != Printer)
goto Cleanup;
pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->SpecificHandle;
// Check if this is the right structure level.
if (Level != 1)
goto Cleanup;
// FIXME: This needs to fail if the printer is set to do direct printing.
// Check if the supplied buffer is large enough.
*pcbNeeded = sizeof(ADDJOB_INFO_1W) + (cchSpoolDirectory + cchPrintersPath + cchSpl + 1) * sizeof(WCHAR);
if (cbBuf < *pcbNeeded)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
goto Cleanup;
}
// Create a new job.
pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
if (!pJob)
{
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
goto Cleanup;
}
// Reserve an ID for this job.
if (!GetNextJobID(&pJob->dwJobID))
goto Cleanup;
// Copy over defaults to the LOCAL_JOB structure.
pJob->Printer = pPrinterHandle->Printer;
pJob->dwPriority = DEF_PRIORITY;
pJob->pwszDatatype = AllocSplStr(pPrinterHandle->pwszDatatype);
pJob->pwszDocumentName = AllocSplStr(wszDefaultDocumentName);
CopyMemory(&pJob->DevMode, &pPrinterHandle->DevMode, sizeof(DEVMODEW));
GetSystemTime(&pJob->stSubmitted);
// Get the user name for the Job.
cchUserName = UNLEN + 1;
pJob->pwszUserName = DllAllocSplMem(cchUserName * sizeof(WCHAR));
if (!GetUserNameW(pJob->pwszUserName, &cchUserName))
{
ERR("GetUserNameW failed with error %lu!\n", GetLastError());
goto Cleanup;
}
// FIXME: For now, pwszNotifyName equals pwszUserName.
pJob->pwszNotifyName = AllocSplStr(pJob->pwszUserName);
// Get the name of the machine that submitted the Job over RPC.
Status = RpcBindingServerFromClient(NULL, &hServerBinding);
if (Status != RPC_S_OK)
{
ERR("RpcBindingServerFromClient failed with status %lu!\n", Status);
goto Cleanup;
}
Status = RpcBindingToStringBindingW(hServerBinding, &pwszBinding);
if (Status != RPC_S_OK)
{
ERR("RpcBindingToStringBindingW failed with status %lu!\n", Status);
goto Cleanup;
}
Status = RpcStringBindingParseW(pwszBinding, NULL, NULL, &pwszMachineName, NULL, NULL);
if (Status != RPC_S_OK)
{
ERR("RpcStringBindingParseW failed with status %lu!\n", Status);
goto Cleanup;
}
cchMachineName = wcslen(pwszMachineName);
pJob->pwszMachineName = DllAllocSplMem((cchMachineName + cchDoubleBackslash + 1) * sizeof(WCHAR));
CopyMemory(pJob->pwszMachineName, wszDoubleBackslash, cchDoubleBackslash * sizeof(WCHAR));
CopyMemory(pJob->pwszMachineName + cchDoubleBackslash, pwszMachineName, (cchMachineName + 1) * sizeof(WCHAR));
// Add the job to the Global Job List.
if (!InsertElementSkiplist(&GlobalJobList, pJob))
{
ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
goto Cleanup;
}
// Add the job at the end of the Printer's Job List.
// As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
if (!InsertTailElementSkiplist(&pJob->Printer->JobList, pJob))
{
ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
goto Cleanup;
}
// Return a proper ADDJOB_INFO_1W structure.
AddJobInfo1.JobId = pJob->dwJobID;
AddJobInfo1.Path = (PWSTR)(pData + sizeof(ADDJOB_INFO_1W));
p = pData;
CopyMemory(p, &AddJobInfo1, sizeof(ADDJOB_INFO_1W));
p += sizeof(ADDJOB_INFO_1W);
CopyMemory(p, wszSpoolDirectory, cchSpoolDirectory);
p += cchSpoolDirectory;
CopyMemory(p, wszPrintersPath, cchPrintersPath);
p += cchPrintersPath;
swprintf((PWSTR)p, L"%05lu.SPL", pJob->dwJobID);
bReturnValue = TRUE;
Cleanup:
if (pwszMachineName)
RpcStringFreeW(&pwszMachineName);
if (pwszBinding)
RpcStringFreeW(&pwszBinding);
if (hServerBinding)
RpcBindingFree(&hServerBinding);
return bReturnValue;
}
static BOOL
_LocalGetJobLevel1(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE pOutput, DWORD cbBuf, PDWORD pcbNeeded)
{
DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
DWORD cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
DWORD cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
DWORD cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
JOB_INFO_1W JobInfo1 = { 0 };
PBYTE pString;
// Check if the supplied buffer is large enough.
*pcbNeeded = sizeof(JOB_INFO_1W) + cbDatatype + cbDocumentName + cbMachineName + cbPrinterName + cbUserName;
if (cbBuf < *pcbNeeded)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
// Put the strings right after the JOB_INFO_1W structure.
pString = pOutput + sizeof(JOB_INFO_1W);
JobInfo1.pDatatype = (PWSTR)pString;
CopyMemory(pString, pJob->pwszDatatype, cbDatatype);
pString += cbDatatype;
JobInfo1.pDocument = (PWSTR)pString;
CopyMemory(pString, pJob->pwszDocumentName, cbDocumentName);
pString += cbDocumentName;
JobInfo1.pMachineName = (PWSTR)pString;
CopyMemory(pString, pJob->pwszMachineName, cbMachineName);
pString += cbMachineName;
JobInfo1.pPrinterName = (PWSTR)pString;
CopyMemory(pString, pJob->Printer->pwszPrinterName, cbPrinterName);
pString += cbPrinterName;
JobInfo1.pUserName = (PWSTR)pString;
CopyMemory(pString, pJob->pwszUserName, cbUserName);
pString += cbUserName;
// Fill the structure and copy it as well.
JobInfo1.JobId = pJob->dwJobID;
JobInfo1.Priority = pJob->dwPriority;
JobInfo1.Status = pJob->dwStatus;
JobInfo1.TotalPages = pJob->dwTotalPages;
CopyMemory(&JobInfo1.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
CopyMemory(pOutput, &JobInfo1, sizeof(JOB_INFO_1W));
return TRUE;
}
static BOOL
_LocalGetJobLevel2(PLOCAL_PRINTER_HANDLE pPrinterHandle, PLOCAL_JOB pJob, PBYTE pOutput, DWORD cbBuf, PDWORD pcbNeeded)
{
DWORD cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
DWORD cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
DWORD cbDriverName = (wcslen(pJob->Printer->pwszPrinterDriver) + 1) * sizeof(WCHAR);
DWORD cbMachineName = (wcslen(pJob->pwszMachineName) + 1) * sizeof(WCHAR);
DWORD cbNotifyName = (wcslen(pJob->pwszNotifyName) + 1) * sizeof(WCHAR);
DWORD cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
DWORD cbPrintProcessor = (wcslen(pJob->Printer->pPrintProcessor->pwszName) + 1) * sizeof(WCHAR);
DWORD cbUserName = (wcslen(pJob->pwszUserName) + 1) * sizeof(WCHAR);
FILETIME ftNow;
FILETIME ftSubmitted;
JOB_INFO_2W JobInfo2 = { 0 };
PBYTE pString;
ULARGE_INTEGER uliNow;
ULARGE_INTEGER uliSubmitted;
// Check if the supplied buffer is large enough.
*pcbNeeded = sizeof(JOB_INFO_2W) + cbDatatype + sizeof(DEVMODEW) + cbDocumentName + cbDriverName + cbMachineName + cbNotifyName + cbPrinterName + cbPrintProcessor + cbUserName;
if (cbBuf < *pcbNeeded)
{
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
// Put the strings right after the JOB_INFO_2W structure.
pString = pOutput + sizeof(JOB_INFO_2W);
JobInfo2.pDatatype = (PWSTR)pString;
CopyMemory(pString, pJob->pwszDatatype, cbDatatype);
pString += cbDatatype;
JobInfo2.pDevMode = (PDEVMODEW)pString;
CopyMemory(pString, &pJob->DevMode, sizeof(DEVMODEW));
pString += sizeof(DEVMODEW);
JobInfo2.pDocument = (PWSTR)pString;
CopyMemory(pString, pJob->pwszDocumentName, cbDocumentName);
pString += cbDocumentName;
JobInfo2.pDriverName = (PWSTR)pString;
CopyMemory(pString, pJob->Printer->pwszPrinterDriver, cbDriverName);
pString += cbDriverName;
JobInfo2.pMachineName = (PWSTR)pString;
CopyMemory(pString, pJob->pwszMachineName, cbMachineName);
pString += cbMachineName;
JobInfo2.pNotifyName = (PWSTR)pString;
CopyMemory(pString, pJob->pwszNotifyName, cbNotifyName);
pString += cbNotifyName;
JobInfo2.pPrinterName = (PWSTR)pString;
CopyMemory(pString, pJob->Printer->pwszPrinterName, cbPrinterName);
pString += cbPrinterName;
JobInfo2.pPrintProcessor = (PWSTR)pString;
CopyMemory(pString, pJob->Printer->pPrintProcessor->pwszName, cbPrintProcessor);
pString += cbPrintProcessor;
JobInfo2.pUserName = (PWSTR)pString;
CopyMemory(pString, pJob->pwszUserName, cbUserName);
pString += cbUserName;
// Time in JOB_INFO_2W is the number of milliseconds elapsed since the job was submitted. Calculate this time.
if (!SystemTimeToFileTime(&pJob->stSubmitted, &ftSubmitted))
{
ERR("SystemTimeToFileTime failed with error %lu!\n", GetLastError());
return FALSE;
}
GetSystemTimeAsFileTime(&ftNow);
uliSubmitted.LowPart = ftSubmitted.dwLowDateTime;
uliSubmitted.HighPart = ftSubmitted.dwHighDateTime;
uliNow.LowPart = ftNow.dwLowDateTime;
uliNow.HighPart = ftNow.dwHighDateTime;
JobInfo2.Time = (DWORD)((uliNow.QuadPart - uliSubmitted.QuadPart) / 10000);
// Position in JOB_INFO_2W is the 1-based index of the job in the processing queue.
// Retrieve this through the element index of the job in the Printer's Job List.
if (!LookupElementSkiplist(&pJob->Printer->JobList, pJob, &JobInfo2.Position))
{
ERR("pJob could not be located in the Printer's Job List!\n");
return FALSE;
}
// Make the index 1-based.
++JobInfo2.Position;
// Fill the rest of the structure.
JobInfo2.JobId = pJob->dwJobID;
JobInfo2.PagesPrinted = pJob->dwPagesPrinted;
JobInfo2.Priority = pJob->dwPriority;
JobInfo2.StartTime = pJob->dwStartTime;
JobInfo2.Status = pJob->dwStatus;
JobInfo2.TotalPages = pJob->dwTotalPages;
JobInfo2.UntilTime = pJob->dwUntilTime;
CopyMemory(&JobInfo2.Submitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
// Finally copy the structure to the output pointer.
CopyMemory(pOutput, &JobInfo2, sizeof(JOB_INFO_2W));
return TRUE;
}
BOOL WINAPI
LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pOutput, DWORD cbBuf, LPDWORD pcbNeeded)
{
PLOCAL_HANDLE pHandle;
PLOCAL_JOB pJob;
PLOCAL_PRINTER_HANDLE pPrinterHandle;
// Check if this is a printer handle.
pHandle = (PLOCAL_HANDLE)hPrinter;
if (pHandle->HandleType != Printer)
return FALSE;
pPrinterHandle = (PLOCAL_PRINTER_HANDLE)pHandle->SpecificHandle;
// Get the desired job.
pJob = LookupElementSkiplist(&GlobalJobList, &JobId, NULL);
if (!pJob || pJob->Printer != pPrinterHandle->Printer)
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
// The function behaves differently for each level.
if (Level == 1)
return _LocalGetJobLevel1(pPrinterHandle, pJob, pOutput, cbBuf, pcbNeeded);
else if (Level == 2)
return _LocalGetJobLevel2(pPrinterHandle, pJob, pOutput, cbBuf, pcbNeeded);
return FALSE;
}
PLOCAL_JOB
@ -80,7 +536,7 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
hFile = CreateFileW(pwszFilePath, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
ERR("CreateFileW failed with error %lu!\n", GetLastError());
ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
goto Cleanup;
}
@ -89,30 +545,30 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
pShadowFile = DllAllocSplMem(cbFileSize);
if (!pShadowFile)
{
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
ERR("DllAllocSplMem failed with error %lufor file \"%S\"!\n", GetLastError(), pwszFilePath);
goto Cleanup;
}
// Read the entire file.
if (!ReadFile(hFile, pShadowFile, cbFileSize, &cbRead, NULL))
{
ERR("ReadFile failed with error %lu!\n", GetLastError());
ERR("ReadFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
goto Cleanup;
}
// Check signature and header size.
if (pShadowFile->dwSignature != SHD_WIN2003_SIGNATURE || pShadowFile->cbHeader != sizeof(SHD_HEADER))
{
ERR("Signature or Header Size mismatch!\n");
ERR("Signature or Header Size mismatch for file \"%S\"!\n", pwszFilePath);
goto Cleanup;
}
// Retrieve the associated printer from the table.
// Retrieve the associated printer from the list.
pwszPrinterName = (PWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offPrinterName);
pPrinter = RtlLookupElementGenericTable(&PrinterTable, pwszPrinterName);
pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
if (!pPrinter)
{
ERR("This shadow file references a non-existing printer!\n");
ERR("Shadow file \"%S\" references a non-existing printer!\n", pwszFilePath);
goto Cleanup;
}
@ -120,15 +576,18 @@ ReadJobShadowFile(PCWSTR pwszFilePath)
pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
if (!pJob)
{
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
goto Cleanup;
}
pJob->dwJobID = pShadowFile->dwJobID;
pJob->dwTotalPages = pShadowFile->dwTotalPages;
pJob->dwPriority = pShadowFile->dwPriority;
pJob->Printer = pPrinter;
pJob->pwszDatatype = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDatatype));
pJob->pwszDocumentName = AllocSplStr((PCWSTR)((ULONG_PTR)pShadowFile + pShadowFile->offDocumentName));
pJob->pwszOutputFile = NULL;
CopyMemory(&pJob->stSubmitted, &pShadowFile->stSubmitted, sizeof(SYSTEMTIME));
CopyMemory(&pJob->DevMode, (PDEVMODEW)((ULONG_PTR)pShadowFile + pShadowFile->offDevMode), sizeof(DEVMODEW));
pReturnValue = pJob;
@ -160,33 +619,42 @@ WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob)
hFile = CreateFileW(pwszFilePath, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, 0, NULL);
if (hFile == INVALID_HANDLE_VALUE)
{
ERR("CreateFileW failed with error %lu!\n", GetLastError());
ERR("CreateFileW failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
goto Cleanup;
}
// Compute the total size of the shadow file.
cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
cbDatatype = (wcslen(pJob->pwszDatatype) + 1) * sizeof(WCHAR);
cbDocumentName = (wcslen(pJob->pwszDocumentName) + 1) * sizeof(WCHAR);
cbPrinterName = (wcslen(pJob->Printer->pwszPrinterName) + 1) * sizeof(WCHAR);
cbFileSize = sizeof(SHD_HEADER) + cbDatatype + cbDocumentName + cbPrinterName;
cbFileSize = sizeof(SHD_HEADER) + cbPrinterName + cbDatatype + cbDocumentName + sizeof(DEVMODEW);
// Allocate memory for it.
pShadowFile = DllAllocSplMem(cbFileSize);
if (!pShadowFile)
{
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
ERR("DllAllocSplMem failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
goto Cleanup;
}
// Fill out the shadow file header information.
pShadowFile->dwSignature = SHD_WIN2003_SIGNATURE;
pShadowFile->cbHeader = sizeof(SHD_HEADER);
// Copy the values.
pShadowFile->dwJobID = pJob->dwJobID;
pShadowFile->dwTotalPages = pJob->dwTotalPages;
pShadowFile->dwPriority = pJob->dwPriority;
CopyMemory(&pShadowFile->stSubmitted, &pJob->stSubmitted, sizeof(SYSTEMTIME));
// Add the extra values that are stored as offsets in the shadow file.
// The first value begins right after the shadow file header.
dwCurrentOffset = sizeof(SHD_HEADER);
CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->Printer->pwszPrinterName, cbPrinterName);
pShadowFile->offPrinterName = dwCurrentOffset;
dwCurrentOffset += cbPrinterName;
CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->pwszDatatype, cbDatatype);
pShadowFile->offDatatype = dwCurrentOffset;
dwCurrentOffset += cbDatatype;
@ -195,14 +663,14 @@ WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob)
pShadowFile->offDocumentName = dwCurrentOffset;
dwCurrentOffset += cbDocumentName;
CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, pJob->Printer->pwszPrinterName, cbPrinterName);
pShadowFile->offPrinterName = dwCurrentOffset;
dwCurrentOffset += cbPrinterName;
CopyMemory((PBYTE)pShadowFile + dwCurrentOffset, &pJob->DevMode, sizeof(DEVMODEW));
pShadowFile->offDevMode = dwCurrentOffset;
dwCurrentOffset += sizeof(DEVMODEW);
// Write the file.
if (!WriteFile(hFile, pShadowFile, cbFileSize, &cbWritten, NULL))
{
ERR("WriteFile failed with error %lu!\n", GetLastError());
ERR("WriteFile failed with error %lu for file \"%S\"!\n", GetLastError(), pwszFilePath);
goto Cleanup;
}

View file

@ -11,7 +11,7 @@
WCHAR wszSpoolDirectory[MAX_PATH];
DWORD cchSpoolDirectory;
// Constants
// Global Constants
const WCHAR wszCurrentEnvironment[] =
#if defined(_X86_)
L"Windows NT x86";
@ -23,16 +23,19 @@ const WCHAR wszCurrentEnvironment[] =
#error Unsupported architecture
#endif
const WCHAR wszDefaultDocumentName[] = L"Local Downlevel Document";
const WCHAR* wszPrintProviderInfo[3] = {
L"Windows NT Local Print Providor", // Name
L"Windows NT Local Printers", // Description
L"Locally connected Printers" // Comment
};
static const PRINTPROVIDOR PrintProviderFunctions = {
// Local Constants
static const PRINTPROVIDOR _PrintProviderFunctions = {
LocalOpenPrinter, // fpOpenPrinter
NULL, // fpSetJob
NULL, // fpGetJob
LocalGetJob, // fpGetJob
NULL, // fpEnumJobs
NULL, // fpAddPrinter
NULL, // fpDeletePrinter
@ -56,7 +59,7 @@ static const PRINTPROVIDOR PrintProviderFunctions = {
NULL, // fpAbortPrinter
NULL, // fpReadPrinter
LocalEndDocPrinter, // fpEndDocPrinter
NULL, // fpAddJob
LocalAddJob, // fpAddJob
NULL, // fpScheduleJob
NULL, // fpGetPrinterData
NULL, // fpSetPrinterData
@ -139,8 +142,9 @@ DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL);
_GetSpoolDirectory();
InitializePrintProcessorTable();
InitializePrinterTable();
InitializePrintProcessorList();
InitializePrinterList();
InitializeGlobalJobList();
break;
}
@ -150,7 +154,7 @@ DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
BOOL WINAPI
InitializePrintProvidor(LPPRINTPROVIDOR pPrintProvidor, DWORD cbPrintProvidor, LPWSTR pFullRegistryPath)
{
CopyMemory(pPrintProvidor, &PrintProviderFunctions, min(cbPrintProvidor, sizeof(PRINTPROVIDOR)));
CopyMemory(pPrintProvidor, &_PrintProviderFunctions, min(cbPrintProvidor, sizeof(PRINTPROVIDOR)));
return TRUE;
}

View file

@ -13,6 +13,8 @@
#include <stdlib.h>
#include <wchar.h>
#include <lmcons.h>
#include <rpc.h>
#include <windef.h>
#include <winbase.h>
#include <wingdi.h>
@ -21,6 +23,8 @@
#include <winsplp.h>
#include <ndk/rtlfuncs.h>
#define SKIPLIST_LEVELS 16
#include <skiplist.h>
#include <spoolss.h>
#include <wine/debug.h>
@ -47,8 +51,10 @@ typedef BOOL (WINAPI *PPrintDocumentOnPrintProcessor)(HANDLE, LPWSTR);
*/
typedef struct _LOCAL_PRINT_PROCESSOR
{
LIST_ENTRY Entry;
PWSTR pwszName;
RTL_GENERIC_TABLE DatatypeTable;
PDATATYPES_INFO_1W pDatatypesInfo1;
DWORD dwDatatypeCount;
PClosePrintProcessor pfnClosePrintProcessor;
PControlPrintProcessor pfnControlPrintProcessor;
PEnumPrintProcessorDatatypesW pfnEnumPrintProcessorDatatypesW;
@ -64,13 +70,15 @@ LOCAL_PRINT_PROCESSOR, *PLOCAL_PRINT_PROCESSOR;
*/
typedef struct _LOCAL_PRINTER
{
// This sort key must be the first element for LookupElementSkiplist to work!
PWSTR pwszPrinterName;
PWSTR pwszPrinterDriver;
PWSTR pwszDescription;
PWSTR pwszDefaultDatatype;
DEVMODEW DefaultDevMode;
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
LIST_ENTRY JobQueue;
SKIPLIST JobList;
}
LOCAL_PRINTER, *PLOCAL_PRINTER;
@ -80,13 +88,24 @@ LOCAL_PRINTER, *PLOCAL_PRINTER;
*/
typedef struct _LOCAL_JOB
{
LIST_ENTRY Entry;
PLOCAL_PRINTER Printer;
DWORD dwJobID;
PWSTR pwszDocumentName;
PWSTR pwszDatatype;
PWSTR pwszOutputFile;
DEVMODEW DevMode;
// This sort key must be the first element for LookupElementSkiplist to work!
DWORD dwJobID; // Internal and external ID of this Job
PLOCAL_PRINTER Printer; // Associated Printer to this Job
DWORD dwPriority; // Priority of this Job from MIN_PRIORITY to MAX_PRIORITY, default being DEF_PRIORITY
SYSTEMTIME stSubmitted; // Time of the submission of this Job
PWSTR pwszUserName; // User that submitted the Job
PWSTR pwszNotifyName; // User that shall be notified about the status of the Job
PWSTR pwszDocumentName; // Name of the Document that is printed
PWSTR pwszDatatype; // Datatype of the Document
PWSTR pwszOutputFile; // Output File to spool the Job to
DWORD dwTotalPages; // Total pages of the Document
DWORD dwPagesPrinted; // Number of pages that have already been printed
DWORD dwStartTime; // Earliest time in minutes since 12:00 AM UTC when this document can be printed
DWORD dwUntilTime; // Latest time in minutes since 12:00 AM UTC when this document can be printed
DWORD dwStatus; // JOB_STATUS_* flags of the Job
PWSTR pwszMachineName; // Name of the machine that submitted the Job (prepended with two backslashes)
DEVMODEW DevMode; // Associated Device Mode to this Job
}
LOCAL_JOB, *PLOCAL_JOB;
@ -140,35 +159,42 @@ typedef struct _SHD_HEADER
DWORD offPrintProcessor;
DWORD offDatatype;
DWORD dwUnknown2;
SYSTEMTIME stSubmitTime;
SYSTEMTIME stSubmitted;
DWORD dwStartTime;
DWORD dwUntilTime;
DWORD dwUnknown6;
DWORD dwPageCount;
DWORD dwTotalPages;
DWORD cbSecurityDescriptor;
DWORD offSecurityDescriptor;
DWORD dwUnknown3;
DWORD dwUnknown4;
DWORD dwUnknown5;
DWORD offComputerName;
DWORD offMachineName;
DWORD dwSPLSize;
}
SHD_HEADER, *PSHD_HEADER;
// jobs.c
extern SKIPLIST GlobalJobList;
BOOL GetNextJobID(PDWORD dwJobID);
void InitializeGlobalJobList();
void InitializePrinterJobList(PLOCAL_PRINTER pPrinter);
BOOL WINAPI LocalAddJob(HANDLE hPrinter, DWORD Level, LPBYTE pData, DWORD cbBuf, LPDWORD pcbNeeded);
BOOL WINAPI LocalGetJob(HANDLE hPrinter, DWORD JobId, DWORD Level, LPBYTE pOutput, DWORD cbBuf, LPDWORD pcbNeeded);
PLOCAL_JOB ReadJobShadowFile(PCWSTR pwszFilePath);
BOOL WriteJobShadowFile(PCWSTR pwszFilePath, const PLOCAL_JOB pJob);
// main.c
extern const WCHAR wszCurrentEnvironment[];
extern const WCHAR wszDefaultDocumentName[];
extern const WCHAR* wszPrintProviderInfo[3];
extern WCHAR wszSpoolDirectory[MAX_PATH];
extern DWORD cchSpoolDirectory;
// printers.c
extern RTL_GENERIC_TABLE PrinterTable;
void InitializePrinterTable();
extern SKIPLIST PrinterList;
void InitializePrinterList();
BOOL WINAPI LocalEnumPrinters(DWORD Flags, LPWSTR Name, DWORD Level, LPBYTE pPrinterEnum, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
BOOL WINAPI LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDefault);
DWORD WINAPI LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo);
@ -179,15 +205,14 @@ BOOL WINAPI LocalEndDocPrinter(HANDLE hPrinter);
BOOL WINAPI LocalClosePrinter(HANDLE hPrinter);
// printprocessors.c
extern RTL_GENERIC_TABLE PrintProcessorTable;
void InitializePrintProcessorTable();
BOOL FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor, PWSTR pwszDatatype);
PLOCAL_PRINT_PROCESSOR FindPrintProcessor(PWSTR pwszName);
void InitializePrintProcessorList();
BOOL WINAPI LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD Level, LPBYTE pDatatypes, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
BOOL WINAPI LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded, LPDWORD pcReturned);
BOOL WINAPI LocalGetPrintProcessorDirectory(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE pPrintProcessorInfo, DWORD cbBuf, LPDWORD pcbNeeded);
// tools.c
PWSTR AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName);
PVOID NTAPI GenericTableAllocateRoutine(PRTL_GENERIC_TABLE Table, CLONG ByteSize);
VOID NTAPI GenericTableFreeRoutine(PRTL_GENERIC_TABLE Table, PVOID Buffer);
#endif

View file

@ -8,40 +8,33 @@
#include "precomp.h"
// Global Variables
RTL_GENERIC_TABLE PrinterTable;
SKIPLIST PrinterList;
/**
* @name _PrinterTableCompareRoutine
* @name _PrinterListCompareRoutine
*
* RTL_GENERIC_COMPARE_ROUTINE for the Printer Table.
* SKIPLIST_COMPARE_ROUTINE for the Printer List.
* Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Printers.
*/
static RTL_GENERIC_COMPARE_RESULTS NTAPI
_PrinterTableCompareRoutine(PRTL_GENERIC_TABLE Table, PVOID FirstStruct, PVOID SecondStruct)
static int WINAPI
_PrinterListCompareRoutine(PVOID FirstStruct, PVOID SecondStruct)
{
PLOCAL_PRINTER A = (PLOCAL_PRINTER)FirstStruct;
PLOCAL_PRINTER B = (PLOCAL_PRINTER)SecondStruct;
int iResult = wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
if (iResult < 0)
return GenericLessThan;
else if (iResult > 0)
return GenericGreaterThan;
else
return GenericEqual;
return wcsicmp(A->pwszPrinterName, B->pwszPrinterName);
}
/**
* @name InitializePrinterTable
* @name InitializePrinterList
*
* Initializes a RTL_GENERIC_TABLE of locally available Printers.
* The table is searchable by name and returns information about the printers, including their job queues.
* Initializes a list of locally available Printers.
* The list is searchable by name and returns information about the printers, including their job queues.
* During this process, the job queues are also initialized.
*/
void
InitializePrinterTable()
InitializePrinterList()
{
const WCHAR wszPrintersKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Print\\Printers";
@ -52,14 +45,13 @@ InitializePrinterTable()
HKEY hKey = NULL;
HKEY hSubKey = NULL;
LONG lStatus;
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
PLOCAL_PRINTER pPrinter = NULL;
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
PWSTR pwszPrintProcessor = NULL;
WCHAR wszPrinterName[MAX_PRINTER_NAME + 1];
// Initialize an empty table for our printers.
// We will search it by printer name.
RtlInitializeGenericTable(&PrinterTable, _PrinterTableCompareRoutine, GenericTableAllocateRoutine, GenericTableFreeRoutine, NULL);
// Initialize an empty list for our printers.
InitializeSkiplist(&PrinterList, DllAllocSplMem, _PrinterListCompareRoutine, (PSKIPLIST_FREE_ROUTINE)DllFreeSplMem);
// Open our printers registry key. Each subkey is a local printer there.
lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszPrintersKey, 0, KEY_READ, &hKey);
@ -92,6 +84,15 @@ InitializePrinterTable()
if (pPrinter->pwszDefaultDatatype)
DllFreeSplStr(pPrinter->pwszDefaultDatatype);
if (pPrinter->pwszDescription)
DllFreeSplStr(pPrinter->pwszDescription);
if (pPrinter->pwszPrinterDriver)
DllFreeSplStr(pPrinter->pwszPrinterDriver);
if (pPrinter->pwszPrinterName)
DllFreeSplStr(pPrinter->pwszPrinterName);
DllFreeSplMem(pPrinter);
pPrinter = NULL;
}
@ -129,8 +130,8 @@ InitializePrinterTable()
if (!pwszPrintProcessor)
continue;
// Try to find it in the Print Processor Table.
pPrintProcessor = RtlLookupElementGenericTable(&PrintProcessorTable, pwszPrintProcessor);
// Try to find it in the Print Processor List.
pPrintProcessor = FindPrintProcessor(pwszPrintProcessor);
if (!pPrintProcessor)
{
ERR("Invalid Print Processor \"%S\" for Printer \"%S\"!\n", pwszPrintProcessor, wszPrinterName);
@ -147,7 +148,7 @@ InitializePrinterTable()
pPrinter->pwszPrinterName = AllocSplStr(wszPrinterName);
pPrinter->pPrintProcessor = pPrintProcessor;
InitializeListHead(&pPrinter->JobQueue);
InitializePrinterJobList(pPrinter);
// Get the printer driver.
pPrinter->pwszPrinterDriver = AllocAndRegQueryWSZ(hSubKey, L"Printer Driver");
@ -165,7 +166,7 @@ InitializePrinterTable()
continue;
// Verify that it's valid.
if (!RtlLookupElementGenericTable(&pPrintProcessor->DatatypeTable, pPrinter->pwszDefaultDatatype))
if (!FindDatatype(pPrintProcessor, pPrinter->pwszDefaultDatatype))
{
ERR("Invalid default datatype \"%S\" for Printer \"%S\"!\n", pPrinter->pwszDefaultDatatype, wszPrinterName);
continue;
@ -176,14 +177,14 @@ InitializePrinterTable()
lStatus = RegQueryValueExW(hSubKey, L"Default DevMode", NULL, NULL, (PBYTE)&pPrinter->DefaultDevMode, &cbDevMode);
if (lStatus != ERROR_SUCCESS || cbDevMode != sizeof(DEVMODEW))
{
ERR("Couldn't query DevMode for Printer \"%S\", status is %ld, cbDevMode is %lu!\n", wszPrinterName, lStatus, cbDevMode);
ERR("Couldn't query a valid DevMode for Printer \"%S\", status is %ld, cbDevMode is %lu!\n", wszPrinterName, lStatus, cbDevMode);
continue;
}
// Add this printer to the printer table.
if (!RtlInsertElementGenericTable(&PrinterTable, pPrinter, sizeof(LOCAL_PRINTER), NULL))
// Add this printer to the printer list.
if (!InsertElementSkiplist(&PrinterList, pPrinter))
{
ERR("RtlInsertElementGenericTable failed with error %lu!\n", GetLastError());
ERR("InsertElementSkiplist failed for Printer \"%S\"!\n", pPrinter->pwszPrinterName);
goto Cleanup;
}
@ -192,20 +193,31 @@ InitializePrinterTable()
}
Cleanup:
if (pwszPrintProcessor)
DllFreeSplStr(pwszPrintProcessor);
// Inside the loop
if (hSubKey)
RegCloseKey(hSubKey);
if (pPrinter)
{
if (pPrinter->pwszDefaultDatatype)
DllFreeSplStr(pPrinter->pwszDefaultDatatype);
if (pPrinter->pwszDescription)
DllFreeSplStr(pPrinter->pwszDescription);
if (pPrinter->pwszPrinterDriver)
DllFreeSplStr(pPrinter->pwszPrinterDriver);
if (pPrinter->pwszPrinterName)
DllFreeSplStr(pPrinter->pwszPrinterName);
DllFreeSplMem(pPrinter);
}
if (hSubKey)
RegCloseKey(hSubKey);
if (pwszPrintProcessor)
DllFreeSplStr(pwszPrintProcessor);
// Outside the loop
if (hKey)
RegCloseKey(hKey);
}
@ -223,9 +235,9 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
DWORD i;
PBYTE pPrinterInfo;
PBYTE pPrinterString;
PSKIPLIST_NODE pNode;
PLOCAL_PRINTER pPrinter;
PRINTER_INFO_1W PrinterInfo1;
PVOID pRestartKey = NULL;
WCHAR wszComputerName[2 + MAX_COMPUTERNAME_LENGTH + 1 + 1];
DWORD dwOffsets[] = {
@ -302,8 +314,10 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
}
// Count the required buffer size and the number of printers.
for (pPrinter = RtlEnumerateGenericTableWithoutSplaying(&PrinterTable, &pRestartKey); pPrinter; pPrinter = RtlEnumerateGenericTableWithoutSplaying(&PrinterTable, &pRestartKey))
for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
{
pPrinter = (PLOCAL_PRINTER)pNode->Element;
// This looks wrong, but is totally right. PRINTER_INFO_1W has three members pName, pComment and pDescription.
// But pComment equals the "Description" registry value while pDescription is concatenated out of pName and pComment.
// On top of this, the computer name is prepended to the printer name if the user supplied the local computer name during the query.
@ -328,8 +342,10 @@ _LocalEnumPrintersLevel1(DWORD Flags, LPWSTR Name, LPBYTE pPrinterEnum, DWORD cb
pPrinterString = pPrinterEnum + *pcReturned * sizeof(PRINTER_INFO_1W);
// Copy over the printer information.
for (pPrinter = RtlEnumerateGenericTableWithoutSplaying(&PrinterTable, &pRestartKey); pPrinter; pPrinter = RtlEnumerateGenericTableWithoutSplaying(&PrinterTable, &pRestartKey))
for (pNode = PrinterList.Head.Next[0]; pNode; pNode = pNode->Next[0])
{
pPrinter = (PLOCAL_PRINTER)pNode->Element;
// FIXME: As for now, the Flags member returns no information.
PrinterInfo1.Flags = 0;
@ -409,7 +425,6 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
PLOCAL_HANDLE pHandle;
PLOCAL_PRINTER pPrinter;
PLOCAL_PRINTER_HANDLE pPrinterHandle = NULL;
PLIST_ENTRY pEntry;
WCHAR wszComputerName[MAX_COMPUTERNAME_LENGTH + 1];
// Sanity checks
@ -478,8 +493,8 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
CopyMemory(pwszPrinterName, lpPrinterName, cchPrinterName * sizeof(WCHAR));
pwszPrinterName[cchPrinterName] = 0;
// Retrieve the associated printer from the table.
pPrinter = RtlLookupElementGenericTable(&PrinterTable, pwszPrinterName);
// Retrieve the associated printer from the list.
pPrinter = LookupElementSkiplist(&PrinterList, &pwszPrinterName, NULL);
if (!pPrinter)
{
// The printer does not exist.
@ -495,7 +510,7 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
if (pDefault && pDefault->pDatatype)
{
// Use the datatype if it's valid.
if (!RtlLookupElementGenericTable(&pPrinter->pPrintProcessor->DatatypeTable, pDefault->pDatatype))
if (!FindDatatype(pPrinter->pPrintProcessor, pDefault->pDatatype))
{
SetLastError(ERROR_INVALID_DATATYPE);
goto Cleanup;
@ -550,30 +565,16 @@ LocalOpenPrinter(PWSTR lpPrinterName, HANDLE* phPrinter, PPRINTER_DEFAULTSW pDef
goto Cleanup;
}
// Look for this job in the job queue of the printer.
pEntry = pPrinter->JobQueue.Flink;
for (;;)
// Look for this job in the Global Job List.
pJob = LookupElementSkiplist(&GlobalJobList, &dwJobID, NULL);
if (!pJob || pJob->Printer != pPrinter)
{
if (pEntry == &pPrinter->JobQueue)
{
// We have reached the end of the list without finding the desired Job ID.
SetLastError(ERROR_INVALID_PRINTER_NAME);
goto Cleanup;
}
// Get our job structure.
pJob = CONTAINING_RECORD(pEntry, LOCAL_JOB, Entry);
if (pJob->dwJobID == dwJobID)
{
// We have found the desired job. Give the caller a printer handle referencing it.
pPrinterHandle->StartedJob = pJob;
break;
}
pEntry = pEntry->Flink;
// The user supplied a non-existing Job ID or the Job ID does not belong to the supplied printer name.
SetLastError(ERROR_INVALID_PRINTER_NAME);
goto Cleanup;
}
pPrinterHandle->StartedJob = pJob;
}
// Create a new handle that references a printer.
@ -647,11 +648,10 @@ Cleanup:
DWORD WINAPI
LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
{
DWORD dwReturnValue = 0;
PDOC_INFO_1W pDocumentInfo1;
PLOCAL_HANDLE pHandle;
PLOCAL_PRINTER_HANDLE pPrinterHandle;
PLOCAL_JOB pJob;
PLOCAL_PRINTER_HANDLE pPrinterHandle;
// Sanity checks
if (!pDocInfo)
@ -688,15 +688,16 @@ LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
// Create a new job.
pJob = DllAllocSplMem(sizeof(LOCAL_JOB));
pJob->Printer = pPrinterHandle->Printer;
pJob->dwPriority = DEF_PRIORITY;
// Check if a datatype was given.
if (pDocumentInfo1->pDatatype)
{
// Use the datatype if it's valid.
if (!RtlLookupElementGenericTable(&pJob->Printer->pPrintProcessor->DatatypeTable, pDocumentInfo1->pDatatype))
if (!FindDatatype(pJob->Printer->pPrintProcessor, pDocumentInfo1->pDatatype))
{
SetLastError(ERROR_INVALID_DATATYPE);
goto Cleanup;
return 0;
}
pJob->pwszDatatype = AllocSplStr(pDocumentInfo1->pDatatype);
@ -717,14 +718,27 @@ LocalStartDocPrinter(HANDLE hPrinter, DWORD Level, LPBYTE pDocInfo)
if (pDocumentInfo1->pOutputFile)
pJob->pwszOutputFile = AllocSplStr(pDocumentInfo1->pOutputFile);
// Enqueue the job.
///////////// TODO /////////////////////
// Get an ID for the new job.
if (!GetNextJobID(&pJob->dwJobID))
return 0;
Cleanup:
if (pJob)
DllFreeSplMem(pJob);
// Add the job to the Global Job List.
if (!InsertElementSkiplist(&GlobalJobList, pJob))
{
ERR("InsertElementSkiplist failed for job %lu for the GlobalJobList!\n", pJob->dwJobID);
return 0;
}
return dwReturnValue;
// Add the job at the end of the Printer's Job List.
// As all new jobs are created with default priority, we can be sure that it would always be inserted at the end.
if (!InsertTailElementSkiplist(&pJob->Printer->JobList, pJob))
{
ERR("InsertTailElementSkiplist failed for job %lu for the Printer's Job List!\n", pJob->dwJobID);
return 0;
}
pPrinterHandle->StartedJob = pJob;
return pJob->dwJobID;
}
BOOL WINAPI

View file

@ -8,8 +8,8 @@
#include "precomp.h"
// Global Variables
RTL_GENERIC_TABLE PrintProcessorTable;
// Local Variables
static LIST_ENTRY _PrintProcessorList;
/**
* @name _OpenEnvironment
@ -76,83 +76,67 @@ Cleanup:
return bReturnValue;
}
/**
* @name _PrinterTableCompareRoutine
*
* RTL_GENERIC_COMPARE_ROUTINE for the Print Processor Table.
* Does a case-insensitive comparison, because e.g. LocalEnumPrintProcessorDatatypes doesn't match the case when looking for Print Processors.
*/
static RTL_GENERIC_COMPARE_RESULTS NTAPI
_PrintProcessorTableCompareRoutine(PRTL_GENERIC_TABLE Table, PVOID FirstStruct, PVOID SecondStruct)
BOOL
FindDatatype(PLOCAL_PRINT_PROCESSOR pPrintProcessor, PWSTR pwszDatatype)
{
PLOCAL_PRINT_PROCESSOR A = (PLOCAL_PRINT_PROCESSOR)FirstStruct;
PLOCAL_PRINT_PROCESSOR B = (PLOCAL_PRINT_PROCESSOR)SecondStruct;
DWORD i;
PDATATYPES_INFO_1W pCurrentDatatype = pPrintProcessor->pDatatypesInfo1;
int iResult = wcsicmp(A->pwszName, B->pwszName);
for (i = 0; i < pPrintProcessor->dwDatatypeCount; i++)
{
if (wcsicmp(pCurrentDatatype->pName, pwszDatatype) == 0)
return TRUE;
if (iResult < 0)
return GenericLessThan;
else if (iResult > 0)
return GenericGreaterThan;
else
return GenericEqual;
++pCurrentDatatype;
}
return FALSE;
}
PLOCAL_PRINT_PROCESSOR
FindPrintProcessor(PWSTR pwszName)
{
PLIST_ENTRY pEntry;
PLOCAL_PRINT_PROCESSOR pPrintProcessor;
for (pEntry = _PrintProcessorList.Flink; pEntry; pEntry = pEntry->Flink)
{
pPrintProcessor = CONTAINING_RECORD(pEntry, LOCAL_PRINT_PROCESSOR, Entry);
if (wcsicmp(pPrintProcessor->pwszName, pwszName) == 0)
return pPrintProcessor;
}
return NULL;
}
/**
* @name _DatatypeTableCompareRoutine
* @name InitializePrintProcessorList
*
* RTL_GENERIC_COMPARE_ROUTINE for the Datatype Table.
* Does a case-insensitive comparison, because e.g. LocalOpenPrinter doesn't match the case when looking for Datatypes.
*/
static RTL_GENERIC_COMPARE_RESULTS NTAPI
_DatatypeTableCompareRoutine(PRTL_GENERIC_TABLE Table, PVOID FirstStruct, PVOID SecondStruct)
{
PWSTR A = (PWSTR)FirstStruct;
PWSTR B = (PWSTR)SecondStruct;
int iResult = wcsicmp(A, B);
if (iResult < 0)
return GenericLessThan;
else if (iResult > 0)
return GenericGreaterThan;
else
return GenericEqual;
}
/**
* @name InitializePrintProcessorTable
*
* Initializes a RTL_GENERIC_TABLE of locally available Print Processors.
* The table is searchable by name and returns pointers to the functions of the loaded Print Processor DLL.
* Initializes a singly linked list of locally available Print Processors.
*/
void
InitializePrintProcessorTable()
InitializePrintProcessorList()
{
DWORD cbDatatypes;
DWORD cbFileName;
DWORD cchPrintProcessorPath;
DWORD cchMaxSubKey;
DWORD cchPrintProcessorName;
DWORD dwDatatypes;
DWORD dwSubKeys;
DWORD i;
DWORD j;
HINSTANCE hinstPrintProcessor;
HKEY hKey = NULL;
HKEY hSubKey = NULL;
HKEY hSubSubKey = NULL;
LONG lStatus;
PDATATYPES_INFO_1W pDatatypesInfo1 = NULL;
PLOCAL_PRINT_PROCESSOR pPrintProcessor = NULL;
PWSTR pwszDatatype = NULL;
PWSTR pwszPrintProcessorName = NULL;
WCHAR wszFileName[MAX_PATH];
WCHAR wszPrintProcessorPath[MAX_PATH];
// Initialize an empty table for our Print Processors.
// We will search it by Print Processor name.
RtlInitializeGenericTable(&PrintProcessorTable, _PrintProcessorTableCompareRoutine, GenericTableAllocateRoutine, GenericTableFreeRoutine, NULL);
// Initialize an empty list for our Print Processors.
InitializeListHead(&_PrintProcessorList);
// Prepare the path to the Print Processor directory.
if (!LocalGetPrintProcessorDirectory(NULL, NULL, 1, (PBYTE)wszPrintProcessorPath, sizeof(wszPrintProcessorPath), &cchPrintProcessorPath))
@ -204,16 +188,13 @@ InitializePrintProcessorTable()
if (pPrintProcessor->pwszName)
DllFreeSplStr(pPrintProcessor->pwszName);
if (pPrintProcessor->pDatatypesInfo1)
DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
DllFreeSplMem(pPrintProcessor);
pPrintProcessor = NULL;
}
if (pDatatypesInfo1)
{
DllFreeSplMem(pDatatypesInfo1);
pDatatypesInfo1 = NULL;
}
// Get the name of this Print Processor.
cchPrintProcessorName = cchMaxSubKey;
lStatus = RegEnumKeyExW(hSubKey, i, pwszPrintProcessorName, &cchPrintProcessorName, NULL, NULL, NULL, NULL);
@ -306,69 +287,47 @@ InitializePrintProcessorTable()
}
// Get all supported datatypes.
pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &dwDatatypes);
pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
if (!pDatatypesInfo1)
pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, NULL, 0, &cbDatatypes, &pPrintProcessor->dwDatatypeCount);
pPrintProcessor->pDatatypesInfo1 = DllAllocSplMem(cbDatatypes);
if (!pPrintProcessor->pDatatypesInfo1)
{
ERR("DllAllocSplMem failed with error %lu!\n", GetLastError());
goto Cleanup;
}
if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pDatatypesInfo1, cbDatatypes, &cbDatatypes, &dwDatatypes))
if (!pPrintProcessor->pfnEnumPrintProcessorDatatypesW(NULL, NULL, 1, (PBYTE)pPrintProcessor->pDatatypesInfo1, cbDatatypes, &cbDatatypes, &pPrintProcessor->dwDatatypeCount))
{
ERR("EnumPrintProcessorDatatypesW failed for Print Processor \"%S\" with error %lu!\n", wszPrintProcessorPath, GetLastError());
continue;
}
// Add the supported datatypes to the datatype table.
RtlInitializeGenericTable(&pPrintProcessor->DatatypeTable, _DatatypeTableCompareRoutine, GenericTableAllocateRoutine, GenericTableFreeRoutine, NULL);
for (j = 0; j < dwDatatypes; j++)
{
pwszDatatype = AllocSplStr(pDatatypesInfo1->pName);
if (!RtlInsertElementGenericTable(&pPrintProcessor->DatatypeTable, pDatatypesInfo1->pName, sizeof(PWSTR), NULL))
{
ERR("RtlInsertElementGenericTable failed for iteration %lu with error %lu!\n", j, GetLastError());
goto Cleanup;
}
++pDatatypesInfo1;
}
// Add the Print Processor to the table.
if (!RtlInsertElementGenericTable(&PrintProcessorTable, pPrintProcessor, sizeof(LOCAL_PRINT_PROCESSOR), NULL))
{
ERR("RtlInsertElementGenericTable failed for iteration %lu with error %lu!\n", i, GetLastError());
goto Cleanup;
}
// Add the Print Processor to the list.
InsertTailList(&_PrintProcessorList, &pPrintProcessor->Entry);
// Don't let the cleanup routines free this.
pwszDatatype = NULL;
pPrintProcessor = NULL;
}
Cleanup:
if (pwszDatatype)
DllFreeSplStr(pwszDatatype);
if (pDatatypesInfo1)
DllFreeSplMem(pDatatypesInfo1);
// Inside the loop
if (hSubSubKey)
RegCloseKey(hSubSubKey);
if (pPrintProcessor)
{
if (pPrintProcessor->pwszName)
DllFreeSplStr(pPrintProcessor->pwszName);
if (pPrintProcessor->pDatatypesInfo1)
DllFreeSplMem(pPrintProcessor->pDatatypesInfo1);
DllFreeSplMem(pPrintProcessor);
}
// Outside the loop
if (pwszPrintProcessorName)
DllFreeSplStr(pwszPrintProcessorName);
if (hSubSubKey)
RegCloseKey(hSubSubKey);
if (hSubKey)
RegCloseKey(hSubKey);
@ -423,7 +382,7 @@ LocalEnumPrintProcessorDatatypes(LPWSTR pName, LPWSTR pPrintProcessorName, DWORD
}
// Try to find the Print Processor.
pPrintProcessor = RtlLookupElementGenericTable(&PrintProcessorTable, pPrintProcessorName);
pPrintProcessor = FindPrintProcessor(pPrintProcessorName);
if (!pPrintProcessor)
{
SetLastError(ERROR_UNKNOWN_PRINTPROCESSOR);
@ -499,6 +458,7 @@ LocalEnumPrintProcessors(LPWSTR pName, LPWSTR pEnvironment, DWORD Level, LPBYTE
}
// Verify pEnvironment and open its registry key.
// We use the registry and not the PrintProcessorList here, because the caller may request information about a different environment.
if (!_OpenEnvironment(pEnvironment, &hKey))
goto Cleanup;

View file

@ -56,25 +56,3 @@ AllocAndRegQueryWSZ(HKEY hKey, PCWSTR pwszValueName)
return pwszValue;
}
/**
* @name GenericTableAllocateRoutine
*
* RTL_GENERIC_ALLOCATE_ROUTINE for all our RTL_GENERIC_TABLEs, using DllAllocSplMem.
*/
PVOID NTAPI
GenericTableAllocateRoutine(PRTL_GENERIC_TABLE Table, CLONG ByteSize)
{
return DllAllocSplMem(ByteSize);
}
/**
* @name GenericTableFreeRoutine
*
* RTL_GENERIC_FREE_ROUTINE for all our RTL_GENERIC_TABLEs, using DllFreeSplMem.
*/
VOID NTAPI
GenericTableFreeRoutine(PRTL_GENERIC_TABLE Table, PVOID Buffer)
{
DllFreeSplMem(Buffer);
}