You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
679 lines
14 KiB
679 lines
14 KiB
/*++
|
|
|
|
Copyright (c) 1990-2003 Microsoft Corporation
|
|
All Rights Reserved
|
|
|
|
Module Name:
|
|
|
|
util.c
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
#ifdef INTERNAL
|
|
MODULE_DEBUG_INIT( DBG_WARN|DBG_ERROR, DBG_ERROR );
|
|
#endif
|
|
// @@END_DDKSPLIT
|
|
|
|
CRITICAL_SECTION pjlMonSection = {0,0,0,0,0,0};
|
|
|
|
PINIPORT pIniFirstPort = NULL;
|
|
|
|
DWORD dwReadThreadErrorTimeout;
|
|
DWORD dwReadThreadEOJTimeout;
|
|
DWORD dwReadThreadIdleTimeoutOther;
|
|
|
|
TCHAR cszEOJTimeout[] = TEXT("EOJTimeout");
|
|
|
|
|
|
VOID
|
|
SplInSem(
|
|
VOID
|
|
)
|
|
{
|
|
if ( pjlMonSection.OwningThread != (HANDLE) UIntToPtr(GetCurrentThreadId()) ) {
|
|
DBGMSG(DBG_ERROR, ("Pjlmon: Not in spooler semaphore !!\n"));
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
SplOutSem(
|
|
VOID
|
|
)
|
|
{
|
|
if ( pjlMonSection.OwningThread == (HANDLE) UIntToPtr(GetCurrentThreadId()) ) {
|
|
DBGMSG(DBG_ERROR, ("Pjlmon: Inside spooler semaphore !!\n"));
|
|
}
|
|
}
|
|
|
|
VOID
|
|
EnterSplSem(
|
|
VOID
|
|
)
|
|
{
|
|
EnterCriticalSection(&pjlMonSection);
|
|
}
|
|
|
|
|
|
VOID
|
|
LeaveSplSem(
|
|
VOID
|
|
)
|
|
{
|
|
SplInSem();
|
|
LeaveCriticalSection(&pjlMonSection);
|
|
}
|
|
|
|
|
|
VOID
|
|
UpdateRegistryValue(
|
|
IN HKEY hKey,
|
|
IN LPCTSTR cszValueName,
|
|
OUT LPDWORD pdwValue,
|
|
IN DWORD dwDefault,
|
|
IN OUT LPDWORD pdwLastError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Gets value assoicated with give value name from the registry. If value name
|
|
is not found default value is written to registry.
|
|
|
|
On error last error value is set to pdwLastError.
|
|
|
|
Arguments:
|
|
hKey : Registry key under which value should be searched
|
|
cszValueName : Value name to search in the registry
|
|
pdwValue : On return will have the value
|
|
dwDefault : If value name not found in the registry set to this value
|
|
pdwLastError : On error set last error to this
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DWORD dwSize = sizeof(*pdwValue);
|
|
|
|
if ( *pdwLastError != ERROR_SUCCESS )
|
|
return;
|
|
|
|
if ( ERROR_SUCCESS != RegQueryValueEx(hKey,
|
|
cszValueName,
|
|
NULL,
|
|
NULL,
|
|
(LPBYTE)pdwValue,
|
|
&dwSize) ) {
|
|
|
|
*pdwValue = dwDefault;
|
|
*pdwLastError = RegSetValueEx(hKey,
|
|
cszValueName,
|
|
0,
|
|
REG_DWORD,
|
|
(LPBYTE)pdwValue,
|
|
sizeof(*pdwValue));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
DWORD
|
|
UpdateTimeoutsFromRegistry(
|
|
IN LPTSTR pszRegistryRoot
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Get the timeout values from the registry, or initialize registry with
|
|
default values if entries are not found.
|
|
|
|
Users/apps can change the registry to change the behavior.
|
|
|
|
Arguments:
|
|
pszRegistryRoot : Registry root to be used by this dll
|
|
|
|
Return Value:
|
|
ERROR_SUCCESS on success, else last error value
|
|
|
|
--*/
|
|
{
|
|
HKEY hKey;
|
|
DWORD dwLastError = ERROR_SUCCESS;
|
|
|
|
dwLastError = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
|
|
pszRegistryRoot,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
&hKey,
|
|
NULL);
|
|
|
|
if ( dwLastError != ERROR_SUCCESS ) {
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
UpdateRegistryValue(hKey,
|
|
cszEOJTimeout,
|
|
&dwReadThreadEOJTimeout,
|
|
READ_THREAD_EOJ_TIMEOUT,
|
|
&dwLastError);
|
|
|
|
dwReadThreadErrorTimeout = READ_THREAD_ERROR_WAITTIME;
|
|
dwReadThreadIdleTimeoutOther = READ_THREAD_IDLE_WAITTIME;
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
Cleanup:
|
|
|
|
if ( dwLastError != ERROR_SUCCESS ) {
|
|
|
|
DBGMSG(DBG_ERROR,
|
|
("UpdateTimeoutsFromRegistry: Failed with %d", dwLastError));
|
|
}
|
|
|
|
return dwLastError;
|
|
}
|
|
|
|
|
|
PINIPORT
|
|
FindIniPort(
|
|
IN LPTSTR pszName
|
|
)
|
|
{
|
|
PINIPORT pIniPort = pIniFirstPort;
|
|
|
|
if ( !pszName || !*pszName )
|
|
return NULL;
|
|
|
|
SplInSem();
|
|
|
|
while ( pIniPort && lstrcmpi(pszName, pIniPort->pszPortName))
|
|
pIniPort = pIniPort->pNext;
|
|
|
|
return pIniPort;
|
|
}
|
|
|
|
|
|
PINIPORT
|
|
CreatePortEntry(
|
|
IN LPTSTR pszPortName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Creates a IniPort entry for a port. Needs to be called inside monitor
|
|
critical section.
|
|
|
|
Arguments:
|
|
pszPortName : Name of the port
|
|
|
|
Return Value:
|
|
On success pointer to the IniPort stucture.
|
|
On failure NULL
|
|
|
|
--*/
|
|
{
|
|
PINIPORT pIniPort = NULL;
|
|
HANDLE DoneWriting = NULL;
|
|
HANDLE DoneReading = NULL;
|
|
HANDLE WakeUp = NULL;
|
|
LPWSTR pszString = NULL;
|
|
|
|
SplInSem();
|
|
|
|
|
|
if (!(DoneWriting = CreateEvent(NULL, FALSE, TRUE, NULL)))
|
|
{
|
|
goto Fail;
|
|
}
|
|
if (!(WakeUp = CreateEvent(NULL, FALSE, FALSE, NULL)))
|
|
{
|
|
goto Fail;
|
|
}
|
|
//
|
|
// manual-reset event, initially signal state
|
|
//
|
|
if (!(DoneReading = CreateEvent(NULL, TRUE, TRUE, NULL)))
|
|
{
|
|
goto Fail;
|
|
}
|
|
if (!(pIniPort = (PINIPORT) AllocSplMem(sizeof(*pIniPort))))
|
|
{
|
|
goto Fail;
|
|
}
|
|
if (!(pszString = AllocSplStr(pszPortName)))
|
|
{
|
|
goto Fail;
|
|
}
|
|
pIniPort->pNext = NULL;
|
|
pIniPort->signature = PJ_SIGNATURE;
|
|
pIniPort->DoneWriting = DoneWriting;
|
|
pIniPort->DoneReading = DoneReading;
|
|
pIniPort->WakeUp = WakeUp;
|
|
pIniPort->hUstatusThread= NULL;
|
|
pIniPort->pszPortName = pszString;
|
|
|
|
pIniPort->pNext = pIniFirstPort;
|
|
pIniFirstPort = pIniPort;
|
|
return pIniPort;
|
|
|
|
Fail:
|
|
if (DoneWriting)
|
|
{
|
|
CloseHandle (DoneWriting);
|
|
}
|
|
if (DoneReading)
|
|
{
|
|
CloseHandle (DoneReading);
|
|
}
|
|
if (WakeUp)
|
|
{
|
|
CloseHandle (WakeUp);
|
|
}
|
|
FreeSplMem (pszString);
|
|
FreeSplMem (pIniPort);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
DeletePortEntry(
|
|
IN PINIPORT pIniPort
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Deletes a port entry. Needs to be called inside monitor critical section
|
|
|
|
Arguments:
|
|
pIniPort : Pointer to the IniPort structure to be deleted
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
SplInSem();
|
|
|
|
if ( pIniPort == pIniFirstPort ) {
|
|
|
|
pIniFirstPort = pIniPort->pNext;
|
|
} else {
|
|
|
|
PINIPORT pPort;
|
|
|
|
pPort = pIniFirstPort;
|
|
while ( pPort && pPort->pNext != pIniPort )
|
|
pPort = pPort->pNext;
|
|
|
|
if (pPort) {
|
|
|
|
pPort->pNext = pIniPort->pNext;
|
|
} else {
|
|
|
|
DBGMSG(DBG_ERROR, ("pjlmon: DeletePortEntry port not found\n"));
|
|
return;
|
|
}
|
|
}
|
|
if (pIniPort-> DoneWriting)
|
|
{
|
|
CloseHandle(pIniPort->DoneWriting);
|
|
pIniPort->DoneWriting = NULL;
|
|
}
|
|
if (pIniPort-> DoneReading)
|
|
{
|
|
CloseHandle(pIniPort->DoneReading);
|
|
pIniPort->DoneReading = NULL;
|
|
}
|
|
if (pIniPort-> WakeUp)
|
|
{
|
|
CloseHandle(pIniPort->WakeUp);
|
|
pIniPort->WakeUp = NULL;
|
|
}
|
|
if (pIniPort-> hUstatusThread)
|
|
{
|
|
CloseHandle (pIniPort-> hUstatusThread);
|
|
pIniPort->hUstatusThread = NULL;
|
|
}
|
|
FreeIniJobs(pIniPort);
|
|
FreeSplStr(pIniPort->pszPortName);
|
|
pIniPort->pszPortName = NULL;
|
|
FreeSplMem(pIniPort);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeIniJob(
|
|
IN OUT PINIJOB pIniJob
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Deletes a job entry.
|
|
|
|
Arguments:
|
|
pIniJob : Pointer to the IniJob structure to be deleted
|
|
|
|
Return Value:
|
|
None
|
|
|
|
--*/
|
|
{
|
|
SPLASSERT(pIniJob);
|
|
if ( pIniJob->hPrinter )
|
|
ClosePrinter(pIniJob->hPrinter);
|
|
FreeSplMem(pIniJob);
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeIniJobs(
|
|
PINIPORT pIniPort
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Free all the InJob structures assigned to this port
|
|
|
|
Arguments:
|
|
pIniPort : IniPort for the port for which all jobs need to be freed
|
|
|
|
--*/
|
|
{
|
|
PINIJOB pIniJob, pIniNextJob;
|
|
|
|
EnterSplSem();
|
|
pIniJob = pIniPort->pIniJob;
|
|
while ( pIniJob ) {
|
|
|
|
pIniNextJob = pIniJob->pNext;
|
|
FreeIniJob(pIniJob);
|
|
pIniJob = pIniNextJob;
|
|
}
|
|
|
|
pIniPort->pIniJob = NULL;
|
|
LeaveSplSem();
|
|
}
|
|
|
|
|
|
VOID
|
|
SendLastPageEjectedForIniJob(
|
|
PINIPORT pIniPort,
|
|
PINIJOB pIniJob
|
|
)
|
|
{
|
|
SplInSem();
|
|
|
|
if ( !SetJob(pIniJob->hPrinter, pIniJob->JobId, 0,
|
|
NULL, JOB_CONTROL_LAST_PAGE_EJECTED) ) {
|
|
|
|
DBGMSG(DBG_WARNING,
|
|
("SetJob failed with last error %d\n", GetLastError()));
|
|
}
|
|
}
|
|
|
|
|
|
PINIJOB
|
|
FindIniJobFromJobId(
|
|
PINIPORT pIniPort,
|
|
DWORD dwJobId,
|
|
PINIJOB *ppPrevIniJob
|
|
)
|
|
{
|
|
PINIJOB pCur, pPre, pIniJob;
|
|
|
|
SplInSem();
|
|
|
|
//
|
|
// If JOB_RESTART is given there will be multiple jobs with same id
|
|
// we need to find the last entry with given id in the list
|
|
//
|
|
for ( pCur = pIniPort->pIniJob, pPre = pIniJob = *ppPrevIniJob = NULL ;
|
|
pCur ;
|
|
pPre = pCur, pCur = pCur->pNext ) {
|
|
|
|
if ( pCur->JobId == dwJobId ) {
|
|
|
|
*ppPrevIniJob = pPre;
|
|
pIniJob = pCur;
|
|
}
|
|
}
|
|
|
|
return pIniJob;
|
|
}
|
|
|
|
|
|
PINIJOB
|
|
FindFirstIniJobTimedOut(
|
|
PINIPORT pIniPort,
|
|
DWORD dwTime,
|
|
PINIJOB *ppPrevIniJob
|
|
)
|
|
{
|
|
PINIJOB pIniJob = pIniPort->pIniJob;
|
|
|
|
SplInSem();
|
|
*ppPrevIniJob = NULL;
|
|
|
|
//
|
|
// Look for a job not in STARTDOC and timedout
|
|
//
|
|
while ( pIniJob &&
|
|
( (pIniJob->status & PP_INSTARTDOC) ||
|
|
pIniJob->TimeoutCount > dwTime ) ) {
|
|
|
|
*ppPrevIniJob = pIniJob;
|
|
pIniJob = pIniJob->pNext;
|
|
}
|
|
|
|
if ( !pIniJob )
|
|
*ppPrevIniJob = NULL;
|
|
|
|
return pIniJob;
|
|
}
|
|
|
|
|
|
VOID
|
|
SendJobLastPageEjected(
|
|
PINIPORT pIniPort,
|
|
DWORD dwValue,
|
|
BOOL bTime
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Send LastPageEjected notification for 1 or more jobs to spooler
|
|
|
|
Arguments:
|
|
pIniPort : IniPort for the port for which all jobs need to be freed
|
|
dwValue : if bTime is TRUE send EOJ to any jobs rcvd before dwValue
|
|
else dwValue is JobId -- ALL_JOBS is for all jobs
|
|
bTime : Tells how to interpret dwValue
|
|
|
|
--*/
|
|
{
|
|
PINIJOB pIniJob;
|
|
|
|
EnterSplSem();
|
|
//
|
|
// JobId == ALL_JOBS is a special case where we want to send LastPage
|
|
// ejected for all jobs pending
|
|
//
|
|
if ( !bTime && dwValue == ALL_JOBS ) {
|
|
|
|
pIniJob = pIniPort->pIniJob;
|
|
pIniPort->pIniJob = NULL;
|
|
|
|
while ( pIniJob ) {
|
|
|
|
PINIJOB pTempJob = pIniJob;
|
|
|
|
SendLastPageEjectedForIniJob(pIniPort, pIniJob);
|
|
pIniJob = pIniJob->pNext;
|
|
FreeIniJob(pTempJob);
|
|
}
|
|
|
|
} else {
|
|
|
|
PINIJOB pPrevIniJob = NULL;
|
|
|
|
pIniJob = pIniPort->pIniJob;
|
|
|
|
//
|
|
// If bTime we want to send LastPageEjected for all jobs timedout
|
|
//
|
|
if ( bTime ) {
|
|
|
|
pIniJob = FindFirstIniJobTimedOut(pIniPort, dwValue, &pPrevIniJob);
|
|
} else {
|
|
|
|
pIniJob = FindIniJobFromJobId(pIniPort, dwValue, &pPrevIniJob);
|
|
}
|
|
|
|
if ( pIniJob ) {
|
|
|
|
//
|
|
// Send notifications for any previous jobs too
|
|
//
|
|
if ( pIniPort->pIniJob == pIniJob )
|
|
pIniPort->pIniJob = NULL;
|
|
else
|
|
pPrevIniJob->pNext = NULL;
|
|
|
|
do {
|
|
|
|
SendLastPageEjectedForIniJob(pIniPort, pIniJob);
|
|
|
|
pPrevIniJob = pIniJob;
|
|
pIniJob = pIniJob->pNext;
|
|
FreeIniJob(pPrevIniJob);
|
|
} while ( pIniJob );
|
|
}
|
|
}
|
|
|
|
LeaveSplSem();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
//
|
|
// String helper function to remove crt dependency
|
|
//
|
|
// -----------------------------------------------------------------------
|
|
LPSTR
|
|
mystrchr(
|
|
LPSTR cs,
|
|
char c
|
|
)
|
|
{
|
|
while (*cs != 0)
|
|
{
|
|
if (IsDBCSLeadByte(*cs))
|
|
cs++;
|
|
else
|
|
if (*cs == c)
|
|
return cs;
|
|
cs++;
|
|
}
|
|
|
|
// fail to find c in cs
|
|
return NULL;
|
|
}
|
|
|
|
|
|
int
|
|
mystrncmp(
|
|
LPSTR cs,
|
|
LPSTR ct,
|
|
int n
|
|
)
|
|
{
|
|
char ret = 0;
|
|
|
|
while (n--)
|
|
{
|
|
ret = *cs - *ct;
|
|
|
|
if (ret)
|
|
break;
|
|
|
|
cs++;
|
|
ct++;
|
|
}
|
|
|
|
return (int)ret;
|
|
}
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
#ifndef INTERNAL
|
|
// @@END_DDKSPLIT
|
|
|
|
LPWSTR
|
|
AllocSplStr(
|
|
LPWSTR pStr
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function will allocate enough local memory to store the specified
|
|
string, and copy that string to the allocated memory
|
|
|
|
Arguments:
|
|
|
|
pStr - Pointer to the string that needs to be allocated and stored
|
|
|
|
Return Value:
|
|
|
|
NON-NULL - A pointer to the allocated memory containing the string
|
|
|
|
FALSE/NULL - The operation failed. Extended error status is available
|
|
using GetLastError.
|
|
|
|
--*/
|
|
|
|
{
|
|
LPWSTR pMem;
|
|
DWORD cbStr;
|
|
|
|
if (!pStr) {
|
|
return NULL;
|
|
}
|
|
|
|
cbStr = wcslen(pStr)*sizeof(WCHAR) + sizeof(WCHAR);
|
|
|
|
if (pMem = AllocSplMem( cbStr )) {
|
|
CopyMemory( pMem, pStr, cbStr );
|
|
}
|
|
return pMem;
|
|
}
|
|
|
|
|
|
LPVOID
|
|
AllocSplMem(
|
|
DWORD cbAlloc
|
|
)
|
|
|
|
{
|
|
PVOID pvMemory;
|
|
|
|
pvMemory = GlobalAlloc(GMEM_FIXED, cbAlloc);
|
|
|
|
if( pvMemory ){
|
|
ZeroMemory( pvMemory, cbAlloc );
|
|
}
|
|
|
|
return pvMemory;
|
|
}
|
|
|
|
|
|
// @@BEGIN_DDKSPLIT
|
|
#endif
|
|
// @@END_DDKSPLIT
|