📄 pjlmon.c
字号:
/*++
Copyright (c) 1990 Microsoft Corporation
All Rights Reserved
Module Name:
Abstract:
Author:
Revision History:
--*/
#define USECOMM
#include "precomp.h"
#include <winioctl.h>
#include <ntddpar.h>
// ---------------------------------------------------------------------
// PROTO, CONSTANT, and GLOBAL
//
// ---------------------------------------------------------------------
DWORD ProcessPJLString(PINIPORT, CHAR *, DWORD *);
VOID ProcessParserError(DWORD);
VOID InterpreteTokens(PINIPORT, PTOKENPAIR, DWORD);
BOOL IsPJL(PINIPORT);
BOOL WriteCommand(HANDLE, LPSTR);
BOOL ReadCommand(HANDLE);
#define WAIT_FOR_WRITE 100 // 0.1 sec
#define WAIT_FOR_DATA_TIMEOUT 100 // 0.1 sec
#define WAIT_FOR_USTATUS_THREAD_TIMEOUT 500 // 0.5 sec
#define GETDEVICEID IOCTL_PAR_QUERY_DEVICE_ID
#define MAX_DEVID 1024
TCHAR cszInstalledMemory[] = TEXT("Installed Memory");
TCHAR cszAvailableMemory[] = TEXT("Available Memory");
BOOL
DllEntryPoint(
IN HANDLE hModule,
IN DWORD dwReason,
IN LPVOID lpRes
)
/*++
Routine Description:
Dll entry point
Arguments:
Return Value:
--*/
{
UNREFERENCED_PARAMETER(lpRes);
switch (dwReason) {
case DLL_PROCESS_ATTACH:
InitializeCriticalSection(&pjlMonSection);
DisableThreadLibraryCalls(hModule);
break;
default:
// do nothing
;
}
return TRUE;
}
VOID
ClearPrinterStatusAndIniJobs(
PINIPORT pIniPort
)
{
PORT_INFO_3 PortInfo3;
if ( pIniPort->PrinterStatus ||
(pIniPort->status & PP_PRINTER_OFFLINE) ) {
pIniPort->PrinterStatus = 0;
pIniPort->status &= ~PP_PRINTER_OFFLINE;
ZeroMemory(&PortInfo3, sizeof(PortInfo3));
SetPort(NULL, pIniPort->pszPortName, 3, (LPBYTE)&PortInfo3);
}
SendJobLastPageEjected(pIniPort, ALL_JOBS, FALSE);
}
VOID
RefreshPrinterInfo(
PINIPORT pIniPort
)
{
//
// Only one thread should write to the printer at a time
//
if ( WAIT_OBJECT_0 != WaitForSingleObject(pIniPort->DoneWriting,
WAIT_FOR_WRITE) ) {
return;
}
//
// If printer is power cycled and it does not talk back (but answers
// PnP id) we got to clear the error on spooler to send jobs
//
ClearPrinterStatusAndIniJobs(pIniPort);
if ( !IsPJL(pIniPort) ) {
pIniPort->status &= ~PP_IS_PJL;
}
SetEvent(pIniPort->DoneWriting);
}
VOID
UstatusThread(
HANDLE hPort
)
/*++
Routine Description:
Unsolicited status information thread. This thread will continue to
read unsolicited until it's asked to terminate, which will happen
under one of these conditions:
1) Receive EOJ confirmation from the printer.
2) Timeout waiting for EOJ confirmation.
3) The port is been closed.
Arguments:
hPort : IniPort structure for the port
Return Value:
--*/
{
PINIPORT pIniPort = (PINIPORT)((INIPORT *)hPort);
HANDLE hToken;
SPLASSERT(pIniPort &&
pIniPort->signature == PJ_SIGNATURE &&
(pIniPort->status & PP_THREAD_RUNNING) == 0);
if ( IsPJL(pIniPort) )
pIniPort->status |= PP_IS_PJL;
SetEvent(pIniPort->DoneWriting);
if ( !(pIniPort->status & PP_IS_PJL) )
goto StopThread;
//
// manual-reset event, initially signal state
//
pIniPort->DoneReading = CreateEvent(NULL, TRUE, TRUE, NULL);
if ( !pIniPort->DoneReading )
goto StopThread;
pIniPort->status |= PP_THREAD_RUNNING;
pIniPort->PrinterStatus = 0;
pIniPort->status &= ~PP_PRINTER_OFFLINE;
pIniPort->dwLastReadTime = 0;
for ( ; ; ) {
//
// check if PP_RUN_THREAD has been cleared to terminate
//
if ( !(pIniPort->status & PP_RUN_THREAD) ) {
if ( pIniPort->status & PP_INSTARTDOC ) {
//
// there's an active job, can't end the thread
//
pIniPort->status |= PP_RUN_THREAD;
} else {
DBGMSG(DBG_INFO,
("PJLMon Read Thread for Port %ws Closing Down.\n",
pIniPort->pszPortName));
pIniPort->status &= ~PP_THREAD_RUNNING;
ClearPrinterStatusAndIniJobs(pIniPort);
goto StopThread;
}
}
//
// check if the printer is bi-di
//
if (pIniPort->status & PP_IS_PJL) {
(VOID)ReadCommand(hPort);
//
// If we are under error condition or if we have jobs pending
// read status back from printer more frequently
//
if ( pIniPort->pIniJob ||
(pIniPort->status & PP_PRINTER_OFFLINE) ||
(pIniPort->status & PP_WRITE_ERROR) ) {
WaitForSingleObject(pIniPort->WakeUp,
dwReadThreadErrorTimeout);
} else {
WaitForSingleObject(pIniPort->WakeUp,
dwReadThreadIdleTimeoutOther);
}
if ( pIniPort->pIniJob &&
!(pIniPort->status & PP_PRINTER_OFFLINE) &&
!(pIniPort->status & PP_WRITE_ERROR) ) {
//
// Some printers are PJL bi-di, but do not send
// EOJ. We want jobs to disappear from printman
//
SendJobLastPageEjected(pIniPort,
GetTickCount() - dwReadThreadEOJTimeout,
TRUE);
}
//
// If we did not read from printer for more than a minute
// and no more jobs talk to printer again
//
if ( !(pIniPort->status & PP_INSTARTDOC) &&
(GetTickCount() - pIniPort->dwLastReadTime) > 240000
)
RefreshPrinterInfo(pIniPort);
} else {
//
// exit the thread if printer is not PJL bi-di capable
//
Sleep(2000);
pIniPort->status &= ~PP_RUN_THREAD;
#ifdef DEBUG
OutputDebugStringA("Set ~PP_RUN_THREAD because printer is not bi-di\n");
#endif
}
}
StopThread:
pIniPort->status &= ~PP_RUN_THREAD;
pIniPort->status &= ~PP_THREAD_RUNNING;
CloseHandle(pIniPort->DoneReading);
//
// By closing the handle and then setting it to NULL we know the main
// thread will not end up setting a wrong event
//
CloseHandle(pIniPort->WakeUp);
pIniPort->WakeUp = NULL;
}
BOOL
CreateUstatusThread(
PINIPORT pIniPort
)
/*++
Routine Description:
Creates the Ustatus thread
Arguments:
pIniPort : IniPort structure for the port
Return Value:
TRUE on succesfully creating the thread, else FALSE
--*/
{
HANDLE ThreadHandle;
DWORD ThreadId;
DBGMSG(DBG_INFO, ("PJLMon Read Thread for Port %ws Starting.\n",
pIniPort->pszPortName));
pIniPort->status |= PP_RUN_THREAD;
WaitForSingleObject(pIniPort->DoneWriting, INFINITE);
pIniPort->WakeUp = CreateEvent(NULL, FALSE, FALSE, NULL);
if ( !pIniPort->WakeUp )
goto Fail;
ThreadHandle = CreateThread(NULL, 16*1024,
(LPTHREAD_START_ROUTINE)UstatusThread,
pIniPort,
0, &ThreadId);
if ( ThreadHandle ) {
SetThreadPriority(ThreadHandle, THREAD_PRIORITY_LOWEST);
CloseHandle(ThreadHandle);
return TRUE;
}
Fail:
if ( pIniPort->WakeUp ) {
CloseHandle(pIniPort->WakeUp);
pIniPort->WakeUp = NULL;
}
pIniPort->status &= ~PP_RUN_THREAD;
SetEvent(pIniPort->DoneWriting);
return FALSE;
}
BOOL
WINAPI
PJLMonOpenPortEx(
IN LPTSTR pszPortName,
IN LPTSTR pszPrinterName,
IN OUT LPHANDLE pHandle,
IN OUT LPMONITOR pMonitor
)
/*++
Routine Description:
Opens the port
Arguments:
pszPortName : Port name
pszPrinterName : Printer name
pHandle : Pointer to the handle to return
pMonitor : Port monitor function table
Return Value:
TRUE on success, FALSE on error
--*/
{
PINIPORT pIniPort;
BOOL bRet = FALSE;
BOOL bInSem = FALSE;
//
// Validate port monitor
//
if ( !pMonitor ||
!pMonitor->pfnOpenPort ||
!pMonitor->pfnStartDocPort ||
!pMonitor->pfnWritePort ||
!pMonitor->pfnReadPort ||
!pMonitor->pfnClosePort ) {
DBGMSG(DBG_WARNING,
("PjlMon: Invalid port monitors passed to OpenPortEx\n"));
SetLastError(ERROR_INVALID_PRINT_MONITOR);
goto Cleanup;
}
EnterSplSem();
bInSem = TRUE;
//
// Is the port open already?
//
if ( pIniPort = FindIniPort(pszPortName) ) {
SetLastError(ERROR_BUSY);
goto Cleanup;
}
pIniPort = CreatePortEntry(pszPortName);
LeaveSplSem();
bInSem = FALSE;
if ( pIniPort &&
(*pMonitor->pfnOpenPort)(pszPortName, &pIniPort->hPort) ) {
*pHandle = pIniPort;
CopyMemory((LPBYTE)&pIniPort->fn, (LPBYTE)pMonitor, sizeof(*pMonitor));
//
// Create the ustatus thread always
// If printer is not PJL it will die by itself
// We do not want to write to the printer in this thread to determine
// printer is PJL since that may take several seconds to fail
//
CreateUstatusThread(pIniPort);
bRet = TRUE;
} else {
DBGMSG(DBG_WARNING, ("PjlMon: OpenPort %s : Failed\n", pszPortName));
}
Cleanup:
if ( bInSem ) {
LeaveSplSem();
}
SplOutSem();
return bRet;
}
BOOL
WINAPI
PJLMonStartDocPort(
IN HANDLE hPort,
IN LPTSTR pszPrinterName,
IN DWORD dwJobId,
IN DWORD dwLevel,
IN LPBYTE pDocInfo
)
/*++
Routine Description:
Language monitor StartDocPort
Arguments:
hPort : Port handle
pszPrinterName : Printer name
dwJobId : Job identifier
dwLevel : Level of Doc info strucuture
pDocInfo : Pointer to doc info structure
Return Value:
TRUE on success, FALSE on error
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -