![]() |
![]() |
|
![]() |
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 Discussion of this article |
![]() Feedback | Paradox Day | Who Uses Paradox | I Use Paradox | Downloads ![]() |
|
![]() The information provided on this Web site is not in any way sponsored or endorsed by Corel Corporation. Paradox is a registered trademark of Corel Corporation. ![]() |
|
![]() Modified: 15 May 2003 Terms of Use / Legal Disclaimer ![]() |
![]() Copyright © 2001- 2003 Paradox Community. All rights reserved. Company and product names are trademarks or registered trademarks of their respective companies. Authors hold the copyrights to their own works. Please contact the author of any article for details. ![]() |
![]() |
|