Paradox Community
Search:

 Welcome |  What is Paradox |  Paradox Folk |  Paradox Solutions |
 Interactive Paradox |  Paradox Programming |  Internet/Intranet Development |
 Support Options |  Classified Ads |  Wish List |  Submissions 


Paradox Programming Articles  |  Beyond Help Articles  |  Tips & Tricks Articles  


Win32 API and Printers
Controlling Printers and 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

Previous articles in this series:
  1. Retrieve Default Windows Printer and interrogate printer properties
  2. Setting the Default Windows Printer
  3. Retrieve Print Jobs
In this article we’ll cover how to control the status of a printer and manage print jobs building upon and reusing the methods presented earlier.


Referenced Win32 APIs

Win32 API method used to set or modify printer attributes.
SetPrinter(
  hPrn cLong,
  cLevel cLong,
  pPrinter cLong,
  command cLong) cLong [stdcall "SetPrinterA"]
hPrn - Handle to the printer
cLevel = Level or type of printer information structure to return - always 0 for our use
pPrinter - pointer to address printer structures - always 0 for our use
command - Printer action command to invoke

Win32 API methods used to retrieve and set print job attributes.
SetJob(
  hPrn cLong,
  JobID cLong,
  cLevel cLong,
  pJob cLong,
  command cLong) cLong [stdcall "SetJobA"]
GetJob(
  hPrn cLong,
  JobID cLong,
  cLevel cLong,
  pJob cLong,
  cdBuf cLong,
  pcbNeeded cPtr) cLong [stdcall "GetJobA"]
hPrn - Handle to the printer
JobID - Windows generated print job number
cLevel = Level or type of job information structure to return
pJob - pointer to address print job structures
command - Print job action command to invoke
cbBuf - Specifies the size, in bytes, of pJob
pcbNeeded = size of memory needed to hold pJob


Controlling Printer Status

The three actions we will cover are:
  1. Pause Printer
  2. Resume Printer
  3. Purge Printer
Pausing the printer will stop Windows from sending print jobs to the associated port monitor which results in all print jobs being held. Resuming the printer will release print jobs for printing. Purging the printer deletes all jobs not currently printing.

In networked environments, you must have adequate permissions to perform these operations. As a check on your permissions, go to Start/Settings/Printers and right click on a printer and select Pause. If you can pause the printer, your permissions are ok.

The Win32 API calls for all three actions are identical except for the command code used and we will use the following common procedure:
Proc cmSendPrinterControl(var stPrinter String,liControlCode LongInt) Logical
;
; Pause/Purge/Resume Printer
;
var
  liPrinterHandle LongInt
  liReturn        LongInt
  loReturn        Logical
endVar
  liPrinterHandle = 0
  loReturn = False
  switch
    case cmOpenPrinterAdmin(stPrinter,liPrinterHandle) = False :
    otherwise :
      liReturn = SetPrinter(
        liPrinterHandle,
        0,
        0,
        liControlCode)
      loReturn = (liReturn = 1)
      liReturn = ClosePrinter(liPrinterHandle)
      switch
        case loReturn = False :
          msgStop(stPrinter,"You don't have permission to modify the " +
            "settings for this printer. If you need to " +
            "change the settings, contact your network " +
            "administrator.")
      endSwitch
  endSwitch
  return loReturn
endProc
The procedure "cmOpenPrinterAdmin" was covered in the previous article.

With the following referenced constants representing each of the three commands, it is straightforward to develop "wrapper" methods for printer control.
Const
;
; Printer Control Codes
;
  cnPrinterControlPause  = 1
  cnPrinterControlResume = 2
  cnPrinterControlPurge  = 3
endConst

method PausePrinter(stPrinter String) Logical
  return cmSendPrinterControl(stPrinter,cnPrinterControlPause)
endMethod

method ResumePrinter(stPrinter String) Logical
  return cmSendPrinterControl(stPrinter,cnPrinterControlPause)
endMethod

method PurgePrinter(stPrinter String) Logical
  return cmSendPrinterControl(stPrinter,cnPrinterControlPause)
endMethod

Controlling Print Jobs

The six actions we will cover are:
  1. Pause Print Job
  2. Resume Print Job
  3. Cancel Print Job
  4. Restart Print Job
  5. Move Print Job up one position in queue
  6. Move Print Job down one position in queue
Pausing the print job will stop Windows from sending the job to the associated port monitor which results in the print job being held. Resuming the print job will allow the job to continue printing. Cancelling the print job removes it from the spooler queue. Restarting a print job begins printing from the first page. Print jobs in the spooler queue are normally processed in the order received based on priority and we will cover how to re-arrange existing print jobs by adjusting their position up or down.

The Win32 API calls for all pausing, resuming, canceling and restarting print jobs are identical except for the command code used and we will use the following common procedure:
Proc cmSendJobControl(var stPrinter String,
                      var liJobID LongInt,
                      liControlCode LongInt) Logical
;
; Pause/Purge/Resume Printer Jobs
;
var
  liPrinterHandle LongInt
  liReturn        LongInt
  loReturn        Logical
endVar
  liPrinterHandle = 0
  loReturn = False
;
; Get a handle to the printer
;
  liReturn = OpenPrinter(stPrinter,liPrinterHandle,0)
  switch
    case liPrinterHandle = 0 :
    otherwise :
      liReturn = SetJob(
        liPrinterHandle,
        liJobID,
        0,
        0,
        liControlCode)
      loReturn = (liReturn = 1)
      liReturn = ClosePrinter(liPrinterHandle)
  endSwitch
  return loReturn
endProc
With the following referenced constants representing each of the three commands, it is straightforward to develop "wrapper" methods for print jobs.
Const
;
; Job Control Codes
;
 cnJobControlPause   = 1
 cnJobControlResume  = 2
 cnJobControlCancel  = 3
 cnJobControlRestart = 4
 cnJobControlDelete  = 5
endConst

method PauseJob(stPrinter String,liJobID LongInt) Logical
  return cmSendJobControl(stPrinter,liJobID,cnJobControlPause)
endMethod

method ResumeJob(stPrinter String,liJobID LongInt) Logical
  return cmSendJobControl(stPrinter,liJobID,cnJobControlResume)
endMethod

method CancelJob(stPrinter String,liJobID LongInt) Logical
;
; Starting with NT4, the control code for a job cancel
; changed
;
  return cmSendJobControl(stPrinter,liJobID,
                          iif(osi.OSPlatformID = 2 and
                              osi.OSMajorVersion >= 4,
                              cnJobControlDelete,
                              cnJobControlCancel))
endMethod

method RestartJob(stPrinter String,liJobID LongInt) Logical
  return cmSendJobControl(stPrinter,liJobID,cnJobControlRestart)
endMethod
Note the extra check for the Windows version (covered in the first article) when we want to cancel a print job.

Adjusting a print jobs position in the spooler queue involves retrieving and updating the print job information attributes. The re-usuable procedure is shown below.
Proc cmAdjustJobPosition(var stPrinter String,
                         var liJobID LongInt,
                         liDirection LongInt) Logical
;
; Adjust Print Job queue positon
;
var
  liPrinterHandle   LongInt
  liReturn          LongInt
  loReturn          Logical
  liSizeNeeded      LongInt
  liMemoryStructure LongInt
  liPosition        LongInt
endVar
  liPrinterHandle = 0
  loReturn = False
  switch
;
; Administrative access required
;
    case cmOpenPrinterAdmin(stPrinter,liPrinterHandle) = False :
    otherwise :
      liSizeNeeded = 0
;
; Get Size of Job structure needed
;
      liReturn = GetJob(
        liPrinterHandle,
        liJobID,
        1,
        0,
        0,
        liSizeNeeded)
      switch
        case liSizeNeeded = 0 :
        otherwise :
;
; Allocate memory for Job Info Level 1 Structure and fill it
;
; Job Info Level 1 Definition
;
; +0   JobID  LongInt
; +4   PrinterName LongInt (Pointer)
; +8   MachineName LongInt (Pointer)
; +12 UserName LongInt (Pointer)
; +16 DocumentName LongInt (Pointer)
; +20 DataType  LongInt (Pointer)
; +24 StatusString LongInt (Pointer)
; +28 Status  LongInt
; +32 Priority  LongInt
; +36 Position  LongInt
; +40 TotalPages LongInt
; +44 PagesPrinted LongInt
; +48 SystemTime
; +48 Year  SmallInt
; +50 Month  SmallInt
; +52 WeekDay  SmallInt
; +54 Day  SmallInt
; +56 Hour  SmallInt
; +58 Minute  SmallInt
; +60 Second  SmallInt
; +62 MilliSecond SmallInt
;
          liMemoryStructure = GlobalAlloc(fromHex("0x40"),liSizeNeeded)
          liReturn = GetJob(
            liPrinterHandle,
            liJobID,
            1,
            liMemoryStructure,
            liSizeNeeded,
            liSizeNeeded)
          switch
            case liReturn <> 1 :
            otherwise :
;
; Retrieve current position, adjust it up or down
; and attempt to update job
;
              liPosition = 0
              MoveFromMemory(
                liPosition,
                liMemoryStructure + 36,4)
              liPosition = liPosition + liDirection
              MoveToMemory(
                liMemoryStructure + 36,
                liPosition,4)
              liReturn = SetJob(
                liPrinterHandle,
                liJobID,
                1,
                liMemoryStructure,
                0)
              loReturn = (liReturn = 1)
          endSwitch
          GlobalFree(liMemoryStructure)
      endSwitch
      liReturn = ClosePrinter(liPrinterHandle)
  endSwitch
  return loReturn
endProc
Our supporting "wrappers" are:
method MoveJobDown(stPrinter String,liJobID LongInt) Logical
  return cmAdjustJobPosition(stPrinter,liJobID,1)
endMethod

method MoveJobUp(stPrinter String,liJobID LongInt) Logical
  return cmAdjustJobPosition(stPrinter,liJobID,-1)
endMethod

Conclusion

We now have methods that provide access to the Win32 API for controlling printers and print jobs.


Next: Printing Directly to a Printer


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.