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
Print Directly to a Printer
© 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
  4. Control Printers and Print Jobs
In this article we'll cover how to send or print directly to a Windows printer, bypassing the printer driver, building upon and reusing the methods presented earlier.

There are times when using Paradox? reports just doesn't get the job done or you've got a PostScript or PCL file generated on another system that you wish to print. In both cases, sending raw print data directly to the printer and bypassing the Windows print driver gives you precise control of your printed output.

To send data directly to the printer under Win32, the following steps need to be followed.
  1. Open the printer (OpenPrinter)
  2. Specify the document name (StartDocPrinter)
  3. Begin the page (StartPagePrinter)
  4. Write data (WritePrinter)
  5. Signal page end (EndPagePrinter)
  6. Signal document end (EndDocPrinter)
  7. Close the printer (ClosePrinter)
In our approach, we treat the entire stream of raw data as one logical page. It is up to each developer to insert form feeds or other printer paper controlling sequences and we will provide direct support for standard ASCII form feed and line spacing in our presented methods.


Referenced Win32 APIs

Other referenced Win32 API's have been covered in earlier articles. Newly introduced methods are:
StartDocPrinter(
  hPrn cLong,
  cLevel cLong,
  pDocInfo cLong) cLong [stdcall "StartDocPrinterA"]

StartPagePrinter(hPrn cLong) cLong [stdcall]

WritePrinter(
  hPrn cLong,
  pBuf cPtr,
  size cLong,
  pcWritten cPtr) clong [stdcall]

EndPagePrinter(hPrn cLong) cLong [stdcall]

EndDocPrinter(hPrn cLong) cLong [stdcall]
hPrn - Handle to the printer
cLevel = Level or type of printer information structure to return - always 1 for our use
pDocInfo - pointer to document information structure
pBuf - Printer data to send
size - Size of pBuf
pcWritten - Bytes written


Direct access to Windows printers

As there are several pieces of information that must be shared between our methods, we will create the following Record Type to keep everything in one place.
Type
  W32DirectPrintInfo =
    Record
      Printer               String
      DocumentName          String
      DocumentContent       String
      DocumentSpacing       SmallInt
      DocumentSpacingAfter  Logical
      DocumentFormFeed      Logical
      DocumentFormFeedAfter Logical
      PrinterHandle         LongInt ;Do not modify
      DocumentStructure     LongInt ;Do not modify
      BytesWritten          LongInt ;Do not modify
    endRecord
endType
Where:

Printer = Name of printer to write to.
DocumentName = Name of our document or print job.
DocumentContent = Raw printer data to send.
DocumentSpacing = Option line spacing. If > 0, the number specified indicates how many carriage return/line feeds to insert in the data stream.
DocumentSpacingAfter = True - After DocumentContent has been sent, False - Before.
DocumentFormFeed = True if a form feed is to be inserted into data stream
DocumentFormFeedAfter = True - After DocumentContent has been sent, False - Before
PrintHandle = Windows printer handle
DocumentStructure = Windows Document Information Level 1 Structure pointer
BytesWritten = Total bytes sent to the spooler

Constants referenced by our methods:
Const
;
; Standard ASCII printer control
;
  cnLineFeed       = 10
  cnFormFeed       = 12
  cnCarriageReturn = 13
endConst
We'll use three methods to encapsulate all the Win32 API calls needed.
method OpenPrinterDirect(var dpi W32DirectPrintInfo) Logical
var
  loReturn Logical
  liAny    LongInt
  stAny    String
  liSize   LongInt
  liReturn LongInt
  apAny    Application
endVar
  loReturn = False
;
; Get a handle to the printer and initialize our structure
;
  dpi.PrinterHandle = 0
  dpi.BytesWritten = 0
  dpi.DocumentSpacing = 0
  dpi.DocumentSpacingAfter = True
  dpi.DocumentFormFeed = False
  dpi.DocumentFormFeedAfter = True
  liReturn = OpenPrinter(dpi.Printer,dpi.PrinterHandle,0)
;
; Check if a valid printer name was found
;
  switch
    case liReturn <> 1 :
    otherwise :
;
; Ensure we have a document name
;
      switch
        case dpi.DocumentName.isAssigned() = False :
          dpi.DocumentName = apAny.getTitle()
          switch
            case dpi.DocumentName.isBlank() :
              dpi.DocumentName = "Corel Paradox"
          endSwitch
      endSwitch
;
; Allocate memory for Document Info 1 structure
;
; Document Name Pointer LongInt
; Output File Name Pointer LongInt - zero
; Data Type Pointer  LongInt
; dpi.DocumentName  String + x'00'
; Data Type   String - "RAW" + x'00'
;
      liSize = dpi.DocumentName.size()
      liAny = liSize + 17
      dpi.DocumentStructure = GlobalAlloc(fromHex("0x40"),liAny)
      liAny = dpi.DocumentStructure + 12
      MoveToMemory(dpi.DocumentStructure,liAny,4)
      MoveToMemory(liAny,dpi.DocumentName,liSize + 1)
      liAny = dpi.DocumentStructure + 13 + liSize
      MoveToMemory(dpi.DocumentStructure + 8,liAny,4)
      stAny = "RAW"
      MoveToMemory(liAny,stAny,4)
      liReturn = StartDocPrinter(
                   dpi.PrinterHandle,
                   1,
                   dpi.DocumentStructure)
      liReturn = StartPagePrinter(dpi.PrinterHandle)
      loReturn = True
  endSwitch
  return loReturn
endMethod

method WritePrinterDirect(var dpi W32DirectPrintInfo) Logical
var
  loReturn  Logical
  liAny     LongInt
  liSize    LongInt
  liReturn  LongInt
  stControl String
endVar
  loReturn = False
  stControl = blank()
;
; Check for any spacing
;
  switch
    case dpi.DocumentSpacing > 0 :
      for liAny from 1 to dpi.DocumentSpacing
        stControl = stControl
                  + chr(cnCarriageReturn)
                  + chr(cnLineFeed)
      endFor
      switch
        case dpi.DocumentSpacingAfter = True :
          dpi.DocumentContent = dpi.DocumentContent
                              + stControl
        otherwise :
          dpi.DocumentContent = stControl
                              + dpi.DocumentContent
      endSwitch
  endSwitch
;
; Check for a form feed
;
  switch
    case dpi.DocumentFormFeed = False :
    case dpi.DocumentFormFeedAfter = False :
      dpi.DocumentContent = chr(cnFormFeed)
                          + dpi.DocumentContent
    otherwise :
      dpi.DocumentContent = dpi.DocumentContent
                          + chr(cnFormFeed)
  endSwitch
  liSize = dpi.DocumentContent.size()
  liAny = 0
;
; Send the data to the printer
;
  liReturn = WritePrinter(
               dpi.PrinterHandle,
               dpi.DocumentContent,
               liSize,
               liAny)
  dpi.BytesWritten = dpi.BytesWritten
                   + liAny
  loReturn = (liReturn = 1)
  return loReturn
endMethod

method ClosePrinterDirect(var dpi W32DirectPrintInfo) Logical
var
  liReturn  LongInt
endVar
;
; End printer session and release resources
;
  liReturn = EndPagePrinter(dpi.PrinterHandle)
  liReturn = EndDocPrinter(dpi.PrinterHandle)
  liReturn = ClosePrinter(dpi.PrinterHandle)
  liReturn = GlobalFree(dpi.DocumentStructure)
  return True
endMethod
Example:

Output ten lines on two pages double spaced. Assume that PW32Prnt is opened for library PW32Prnt.lsl.
var
  dpi    W32DirectPrintInfo
  siLine SmallInt
endVar
  dpi.Printer = "Your Windows Printer Name goes here"
  dpi.DocumentName = "Paradox Print Direct "
  switch
    case PW32Prnt.OpenPrinterDirect(dpi) = False :
      msgStop("Error","Cannot open printer for direct access")
    otherwise :
      setMouseShape(MouseWait)
      dpi.DocumentSpacing = 2
      for siLine from 1 to 10
        switch
          case siLine = 5 :
            dpi.DocumentFormFeed = True
          otherwise :
            dpi.DocumentFormFeed = False
        endSwitch
        dpi.DocumentContent = "Line "
                            + strval(siLine)
                            + " - This is a test for direct printing."
        PW32Prnt.WritePrinterDirect(dpi)
      endFor
      PW32Prnt.ClosePrinterDirect(dpi)
      setMouseShape(MouseArrow)
  endSwitch

Conclusion

We now have methods that provide direct access to Windows printers.


Next: Changing Printer Attributes


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.