| Win32 API and Printers: Getting Printer Jobs |
|
|
| Contributed by Rick Kelly | |
| 03 August 2002 | |
|
Get one step closer to managing your printer and print jobs with this article on retrieving printer spooler jobs.Win32 API and Printers Getting Printer Jobs © 2002 Rick Kelly www.crooit.com Preface Libraries and forms (Paradox 9) presented are available as a download here. Paradox 8 is not supported although Paradox 7-32 surprisingly works. After downloading into the folder of your choice, make that folder :WORK: and run the included form for a demonstration. You'll need to run the form before opening it in design mode as it references table :PRIV:__PJobs that is created in the form's INIT event. (If running Paradox 10, or if the table doesn't get created properly, you should first run the BldSpool.ssl script prior to running the form.) Introduction The first article in our series showed how the Win32 API could be used to retrieve the default Windows printer, interrogate some basic properties for any properly installed Windows printer and the second article covered setting the default Windows printer. In this article we'll cover how to retrieve printer spooler jobs building upon and reusing the methods presented earlier. From Windows perspective, the status of printers and print jobs is updated when a job is printing or despooling. At other times, when the printer is not printing and is not reporting any status information, the printer is considered to be ready and idle. Windows treats a physical printer as nothing more than the final destination of a print job as generated through a system defined interface. The physical interface to a printer is known as the port monitor. Port monitors are responsible for sending the print job to the printer across whatever connection is defined and to report any errors that may occur. Windows is not concerned with the state of the physical printer which ultimately determines the success of a print job at the time it is handed off to the port monitor. If the port monitor detects an error, it is posted in the print job's status flags. Consequently, a printer reports no status when its associated queue is empty and it is assumed that the printer is ready to accept print jobs. Applications are permitted to complete spooling of their print jobs and it is the user's responsibility to address any errors. Remember that the true status of a physical printer can only be determined when Windows is attempting to send a job via the port monitor. Referenced Win32 API The following Win32 API method is used to retrieve or enumerate printer spool jobs. EnumJobs( hPrn cLong, FirstJob cLong, NoJobs cLong, cLevel cLong, pJob cLong, cbBuf cLong, pcbNeeded cPtr, pcbReturned cPtr) cLong [stdcall "EnumJobsA"]hPrn - Handle to the printer FirstJob - Specifies the zero based position within the print queue of the first print job to enumerate - always 0 (start with first job) for our use NoJobs - Specifies the total number of print jobs to enumerate - always -1 (all Jobs) for our use cLevel = Level or type of spooler job information structure to return - we will use 2 pJob - pointer to address our job structures cbBuf - Specifies the size, in bytes, of pJob pcbNeeded = size of memory needed to hold pJob pcbReturned = number of job record structures returned Get Printer Spool Jobs The job information structures returned by Win32 API method EnumJobs contain numerous data fields describing the print job for the specified printer. As Paradox does not support arrays of Record Types, we will use tables to capture print job information with one record per job. To allow forms to reference temporary tables in their data model, a method for creating the table in the forms INIT event is needed. Example: method CreateSpoolTable(stTableName String, var tbAny Table) Logical ; ; Create table to hold printer spool jobs ; tbAny = create stTableName with "JobPosition" : "I", "JobID" : "I", "MachineName" : "A64", "UserName" : "A64", "DocumentName" : "A255", "NotifyName" : "A64", "DataType" : "A32", "JobStatus" : "A32", "Priority" : "I", "StartTime" : "T", "UntilTime" : "T", "TotalPages" : "S", "TotalSize" : "I", "JobSubmitted" : "@", "TimePrinting" : "I", "PagesPrinted" : "S" key "JobPosition" endCreate return isTable(stTableName) endMethodOur method to retrieve printer job characteristics will support passing the name and optional creation of the table to contain the job enumeration and will return the status flags of the current job printing. Constants referenced are: cnOneDay = 86400000.0 ;Milliseconds in a day cnOneHour = 3600000.0 ;Milliseconds in a hour cnOneMinute = 60000.0 ;Milliseconds in a minute cnOneSecond = 1000.0 ;Milliseconds in a second ; ; Printer Status ; cnStatusPrinting = 1024Example: method GetPrinterJobs(
stPrinter String,
stTableName String,
loCreate Logical,
liSpoolStatus LongInt) Logical
var
loReturn Logical
tbAny Table
tcAny TCursor
liPrinterHandle LongInt
liReturn LongInt
liSizeNeeded LongInt
liSizeUsed LongInt
liMemoryStructure LongInt
liAny LongInt
liPointer LongInt
liIndex LongInt
liOffset LongInt
siMonth SmallInt
siDay SmallInt
siYear SmallInt
siHour SmallInt
siMinute SmallInt
siSecond SmallInt
siMilliSecond SmallInt
daAny Date
tiAny Time
endVar
liSpoolStatus = 0
loReturn = False
;
; Check if spooler table needs to be created
;
switch
case loCreate = False :
loReturn = tbAny.attach(stTableName)
switch
case loReturn = True :
tbAny.empty()
endSwitch
otherwise :
loReturn = CreateSpoolTable(stTableName,tbAny)
endSwitch
;
; See if we can open our spooler table
;
switch
case loReturn = False :
case tcAny.open(tbAny) = False :
loReturn = False
otherwise :
loReturn = False
tcAny.edit()
;
; Get a handle to the printer
;
liPrinterHandle = 0
liReturn = OpenPrinter(stPrinter,liPrinterHandle,0)
;
; Check if stPrinter contained a valid printer name
;
switch
case liReturn <> 1 :
otherwise :
;
; First call is to get buffer size needed
;
liSizeNeeded = 0
liSizeUsed = 0
liReturn = ENumJobs(
liPrinterHandle,
0,
-1,
2,
0,
0,
liSizeNeeded,
liSizeUsed)
;
; Allocate memory for Jobs Info Structure Level 2
;
liMemoryStructure = GlobalAlloc(fromHex("0x40"),liSizeNeeded)
;
; Get Jobs Info Structure
;
liReturn = ENumJobs(
liPrinterHandle,
0,
-1,
2,
liMemoryStructure,
liSizeNeeded,
liSizeNeeded,
liSizeUsed)
switch
case liReturn <> 1 :
case liSizeUsed < 1 :
loReturn = True
otherwise :
loReturn = True
liAny = 0
liPointer = 0
;
; Parse Jobs Info Structure Level 2
;
for liIndex from 1 to liSizeUsed
;
; Calculate start of current job structure
;
liOffset = liMemoryStructure
+ (liIndex * 104)
- 104
;
; Build Job Record
;
tcAny.insertRecord()
MoveFromMemory(liAny,liOffset,4)
tcAny."JobID" = liAny
MoveFromMemory(liPointer, liOffset + 8,4)
tcAny."MachineName" = cmSaveString(liPointer)
MoveFromMemory(liPointer, liOffset + 12,4)
tcAny."UserName" = cmSaveString(liPointer)
MoveFromMemory(liPointer, liOffset + 16,4)
tcAny."DocumentName" = cmSaveString(liPointer)
MoveFromMemory(liPointer, liOffset + 20,4)
tcAny."NotifyName" = cmSaveString(liPointer)
MoveFromMemory(liPointer, liOffset + 24,4)
tcAny."DataType" = cmSaveString(liPointer)
MoveFromMemory(liPointer, liOffset + 44,4)
tcAny."JobStatus" = cmSaveString(liPointer)
;
; If job is printing, save status flags
;
MoveFromMemory(liAny,liOffset + 52,4)
switch
case liAny.bitAnd(cnStatusPrinting) = cnStatusPrinting :
liSpoolStatus = liAny
endSwitch
MoveFromMemory(liAny,liOffset + 56,4)
tcAny."Priority" = liAny
MoveFromMemory(liAny,liOffset + 60,4)
tcAny."JobPosition" = liAny
MoveFromMemory(liAny,liOffset + 64,4)
switch
case liAny > 0 :
tcAny."StartTime" = PW32Cnvt.GetTimeFromMinutes(liAny)
endSwitch
MoveFromMemory(liAny,liOffset + 68,4)
switch
case liAny > 0 :
tcAny."UntilTime" = PW32Cnvt.GetTimeFromMinutes(liAny)
endSwitch
MoveFromMemory(liAny,liOffset + 72,4)
tcAny."TotalPages" = liAny
MoveFromMemory(liAny,liOffset + 76,4)
tcAny."TotalSize" = liAny
;
; Retrieve system time values
;
siMonth = 0
siDay = 0
siYear = 0
siHour = 0
siMinute = 0
siSecond = 0
siMillisecond = 0
MoveFromMemory(siYear, liOffset + 80,2)
MoveFromMemory(siMonth, liOffset + 80 + 2,2)
MoveFromMemory(siDay, liOffset + 80 + 6,2)
daAny = date(siMonth,siDay,siYear)
MoveFromMemory(siHour, liOffset + 80 + 8,2)
MoveFromMemory(siMinute, liOffset + 80 + 10,2)
MoveFromMemory(siSecond, liOffset + 80 + 12,2)
MoveFromMemory(siMilliSecond, liOffset + 80 + 14,2)
tiAny = time(siHour * cnOneHour +
siMinute * cnOneMinute +
siSecond * cnOneSecond +
siMillisecond)
tcAny."JobSubmitted" =
datetime((number(daAny) * cnOneDay) +
number(tiAny) +
(cmGetTimeZoneOffset() *
cnOneMinute))
MoveFromMemory(liAny,liOffset + 96,4)
tcAny."TimePrinting" = liAny
MoveFromMemory(liAny, liOffset + 100,4)
tcAny."PagesPrinted" = liAny
tcAny.unlockRecord()
endFor
endSwitch
liReturn = GlobalFree(liMemoryStructure)
liReturn = ClosePrinter(liPrinterHandle)
endSwitch
endSwitch
switch
case tcAny.isAssigned() = True :
tcAny.close()
endSwitch
return loReturn
endMethod
Conclusion We now have methods that provide access to the Win32 API for accessing and retrieving print jobs. Next: Controlling Jobs in the Print Spooler |
| < Prev | Next > |
|---|





