Senin, 18 Oktober 2010

Win32 API and Printers: Get Default Printer and Printer Properties


Learn how the Win32 API can give you more printing control than ever before!Win32 API and Printers
Get Default Printer and Printer Properties


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.


Introduction

There are moments when the ObjectPAL operatives dealing with printers do not provide access to some of the underlying properties of Windows printers or are too slow or unreliable to use in production applications.

This is the first article in a series that explores the Win32 API and adds additional or enhanced functionality for printers to Paradox applications running on all Win32 platforms (95,98,ME,NT,W2K,XP).


Win32 API

The Win32 Application Programming Interface (API) has been used for writing Windows programs since version 1.0. It is the combination of data types, definition and functions which C programmers include as a set of header files. Most of the functions are implemented in DLLs, to which applications link at run time - either directly or through COM interfaces.

The core API divides into three sections:
KERNEL32.DLL - All the low level kernel services
GDI32.DLL - Graphics Device Interface: drawing and printing
USER32.DLL - User Interface controls, windows and messaging services
In Windows NT/W2K/XP many of the services are implemented in Kernel Mode and managed by various forms of inter-process communication.

Many of the Win32 functions referenced for printers are in WINSPOOL.DRV.

Many functions use structures which are not directly supported in Paradox®. Pascal Hutton pioneered the basic technique for using structures and you are encouraged to review his article at:

http://www.thedbcommunity.com/code/structures.htm

This is the basic technique used in this series in passing and retrieving structures when calling Win32 functions.

We will be using Paradox Record Types to map the contents of various Win32 API structures, at times, merging several Win32 structures into one Record Type.


Determining Windows Version

Some of our approaches require knowing what version of Windows is running. The following Win32 method is available:
Uses "kernel32.dll"
  GetVersionEx(OSVersionInfo cLong) cLong [stdcall "GetVersionExA"]
endUses
OSVersionInfo is a structure returned with the following format:

Structure Size - LongInt
Major Version - LongInt
Minor Version - LongInt
Build Number - LongInt
Platform ID - LongInt
CSD Version - String (Service Pack Level)

This structure is mapped into the following Paradox Record Type:
Type
  OSVersionInfo =
    Record
      OSMajorVersion  LongInt
      OSMinorVersion  LongInt
      OSBuildNumber   LongInt
      OSPlatformID    LongInt ;0 = Win32s
                              ;1 = Win32
                              ;2 = WinNT
      OSCSDVersion    String
      OSName          String
    EndRecord
OSName is derived using the following matrix.

Platform ID Major Version Minor Version Operating System
Win32 >= 4 0 Win95
Win32 >= 4 > 0 and < 90 Win98
Win32 >= 4 >0 and >=90 WinMe
WinNT <= 4 0 WinNT
WinNT 5 0 Win2K
WinNT 5 > 0 WinXP


Commonly Referenced Win32 Methods

The following Win32 methods are frequently called as part of other methods and/or procedures.
Uses "kernel32.dll"
  GlobalAlloc(
      wFlags cLong,
      dwBytes cLong) cLong [stdcall]
  GlobalFree(hMem cLong) cLong [stdcall]
  MoveToMemory(
      wDestination cLong,
      wSource cPtr,
      wLength cLong) cLong [stdcall "RtlMoveMemory"]
  MoveFromMemory(
      wDestination cPtr,
      wSource cLong,
      wLength cLong) cLong [stdcall "RtlMoveMemory"]
  GetStringSize(cString cLong) cLong [stdcall "lstrlenA"]
EndUses
GlobalAlloc allocates an area of memory, dwBytes in size and returns the address as a LongInt.

GlobalFree takes a memory address obtained with GlobalAlloc and frees it for reuse.

MoveToMemory takes the value of a Paradox variable of size wLength and saves a copy of it at address wDestination.

MoveFromMemory takes the contents at memory address wSource of size wLength and saves a copy in a Paradox variable.

GetStringSize takes string pointer cString and returns the string size.

Windows supports multiple string formats. Two of them are single byte fixed and variable length types. Variable length strings are terminated with a x"00". Fixed length strings are padded with x"00" to a specified size - typically 32. The following proc's are used to handle the saving of strings.
Proc cmSaveString(var liPointer LongInt) String
;
; Given a pointer to string, return the string
;
var
  stAny   String
  liSize  LongInt
endVar
  stAny = blank()
;
; Get length of string
;
  liSize = GetStringSize(liPointer)
;
; Save string
;
  switch
    case liSize > 0 :
      stAny = space(liSize)
      MoveFromMemory(stAny,liPointer,liSize)
    endSwitch
  return stAny
endProc
Proc cmSaveFixedString(liPointer LongInt,liMaxSize SmallInt) String
;
; Given a pointer to fixed length string, return the string
;
var
  stAny   String
  liSize  LongInt
endVar
  stAny = blank()
;
; Get length of string
;
  liSize = GetStringSize(liPointer)
;
; Save string
;
  switch
    case liSize > 0 :
      liSize = iif(liSize > liMaxSize,liMaxSize,liSize)
      stAny = space(liSize)
      MoveFromMemory(stAny,liPointer,liSize)
    endSwitch
  return stAny
endProc

Get Default Windows Printer

One way of determining the default Windows printer using OPAL is:
;
; Retrieve all defined windows printers
;
var
 arPrinters  Array[] String
 arAny       Array[] String
 stAny       String
 siIndex     SmallInt
 piAny       PrinterInfo
endVar
 arPrinterNames.empty()
 enumPrinters(arPrinters)
 for siIndex from 1 to arPrinters.size()
  stAny = arPrinters[siIndex]
  stAny.breakApart(arAny,",")
  printerSetCurrent(stAny)
  printerGetInfo(piAny)
;
; Check for default windows printer
;
  switch
    case piAny.DefaultPrinter = True :
      msgInfo("Default Printer",arAny[1])
      quitloop
  endSwitch
 endFor
Please note that the current printer should be saved and switched back to and was omitted from the above.

The drawback with this code is it is slow - in tests on a NetWare LAN with 13 printers defined, it consistently took 12+ seconds to run and Paradox 8 returned piAny.DefaultPrinter = True for every printer!

Our approach to retrieving the default printer is based on the version of the operating system and is derived from article Q246772 at:
http://support.microsoft.com/support/kb/articles/q246/7/72.asp
Win95/Win98/WinME

The following Win32 API method is used with Win9x versions to retrieve the default printer name.
Uses "winspool.drv"
  EnumDefaultPrinter(pFlags cLong,
                     pNull cLong,
                     Level cLong,
                     pPrinterEnum cLong,
                     cbBuf cLong,
                     pcbNeeded cptr,
                     pcbReturned cptr) clong [stdcall "EnumPrintersA"]
endUses
pFlags = Type of enumeration to perform - default printer = 1
pNull = Always 0
Level = Level or type of printer information structure to return - we will use 5
pPrinterEnum = pointer to address of returned structure
cbBuf = size of memory addressed by pPrinterEnum
pcbNeeded = size of memory needed to hold pPrinterEnum
pcbReturned = size of structure returned

A commonly used feature in Win32 programming is that you can often call a method without passing an address for any returned structures and the structure size is returned. Given the requisite size, we'll just allocate some memory, call our method once again and parse the results.

Example:

Assume that "cnPrinterEnumDefault" is a constant with value = 1.
var
  liSizeNeeded      LongInt
  liSizeReturned    LongInt
  liReturn          LongInt
  liMemoryStructure LongInt
  liPrinterAddress  LongInt
  stPrinter         String
endVar
  liSizeNeeded = 0
  liSizeReturned = 0
  liPrinterAddress = 0
  stPrinter = blank()
;
; First call to EnumDefaultPrinter will return the
; size of the buffer needed to hold the printer info
;
  liReturn = EnumDefaultPrinter(
    cnPrinterEnumDefault,
    0,
    5,
    0,
    0,
    liSizeNeeded,
    liSizeReturned)
  switch
    case liSizeNeeded > 0 :
;
; Allocate memory for Printer Info Structure Level 5
;
      liMemoryStructure = GlobalAlloc(fromHex("0x40"),liSizeNeeded)
;
; Get Default Printer Info
;
      liReturn = EnumDefaultPrinter(
        cnPrinterEnumDefault,
        0,
        5,
        liMemoryStructure,
        liSizeNeeded,
        liSizeNeeded,
        liSizeReturned)
      switch
        case liReturn = 1 :
;
; First 4 bytes are a pointer to the printer name
;
          MoveFromMemory(
            liPrinterAddress,
            liMemoryStructure,
            4)
          stPrinter = cmSaveString(liPrinterAddress)
      endSwitch
;
; Release Memory
;
      liReturn = GlobalFree(liMemoryStructure)
  endSwitch
The variable "stPrinter" will contain the default printer name or be blank if a problem was found. (i.e. No default printer defined)

WinNT

For WinNT, the default printer can be found in WIN.INI in the "windows" section with the keyword "device=" and we can use normal OPAL to retrieve it.

Example:
var
  stAny   String
  arAny   Array[] String
endVar
  stAny = readProfileString(windowsDir() + "\\win.ini","windows","device")
  stAny = stAny.rTrim()
  stAny = stAny.lTrim()
  switch
    case stAny.isBlank() = False :
      stAny.breakApart(arAny,",")
      stAny = arAny[1]
  endSwitch
  return stAny
The variable "stAny" will contain the default printer name or be blank if a problem was found. (i.e. No default printer defined)

W2K and XP

For W2K and XP, the following Win32 API (only present in those OS versions) is used.
Uses "winspool.drv"
  GetDefaultPrinter(
    stPrinter cptr,
    liBufferSize cptr) cLong [stdcall "GetDefaultPrinterA"]
endUses
stPrinter = Paradox String Type to receive the default printer name
liBufferSize = Length of default printer name

Example:
var
  liBufferSize  LongInt
  stPrinter     String
endVar
  stPrinter = blank()
  liBufferSize = 0
;
; First call will return the printer name size in liBufferSize
;
  liReturn = GetDefaultPrinter(stPrinter,liBufferSize)
  switch
    case liBufferSize > 0 :
;
; Initialize stPrinter for printer name size
;
      stPrinter = space(liBufferSize)
;
; Get default printer name
;
;    liReturn = 1 if successful
;
      switch
        case GetDefaultPrinter(stPrinter,liBufferSize) = 1 :
        otherwise :
          stPrinter = blank()
      endSwitch
  endSwitch
The variable "stPrinter" will contain the default printer name or be blank if a problem was found. (i.e. No default printer defined)


Get Printer Information

The Win32 API method "GetPrinter" will retrieve the properties common to all printers. GetPrinter returns a structure (Windows calls this a Printer Info 2 structure) which in turn contains a DEVMODE structure pointer further describing the physical characteristics of the specified printer. Information from both the Printer Info 2 and DEVMODE structures are mapped into the following Paradox Record Types:
Type
W32PrinterInfo =
  Record
    RecordAvailable    Logical  ;True = Record mapped successfully
    PrintServer        String
    PrinterName        String
    ShareName          String
    Port               String
    Driver             String
    Comment            String
    Location           String
    SeparatorPage      String
    PrintProcessor     String
    DataType           String
    Parameters         String
    DeviceName         String
    SpecVersion        SmallInt
    DriverVersion      SmallInt
    Orientation        SmallInt  ;Portrait = 1
                                 ;Landscape = 2

    PaperSize          SmallInt  ;1 = Letter 8 1/2 x 11 in
                                 ;2 = Letter Small 8 1/2 x 11 in
                                 ;3 =  Tabloid 11 x 17 in
                                 ;4 =  Ledger 17 x 11 in
                                 ;5 =  Legal 8 1/2 x 14 in
                                 ;6 =  Statement 5 1/2 x 8 1/2 in
                                 ;7 =  Executive 7 1/4 x 10 1/2 in
                                 ;8 =  A3 297 x 420 mm
                                 ;9 =  A4 210 x 297 mm
                                 ;10 =  A4 Small 210 x 297 mm
                                 ;11 =  A5 148 x 210 mm
                                 ;12 =  B4 250 x 354
                                 ;13 =  B5 182 x 257 mm
                                 ;14 =  Folio 8 1/2 x 13 in
                                 ;15 =  Quarto 215 x 275 mm
                                 ;16 =  10x14 in
                                 ;17 =  11x17 in
                                 ;18 =  Note 8 1/2 x 11 in
                                 ;19 =  Envelope #9 3 7/8 x 8 7/8
                                 ;20 =  Envelope #10 4 1/8 x 9 1/2
                                 ;21 =  Envelope #11 4 1/2 x 10 3/8
                                 ;22 =  Envelope #12 4 \276 x 11
                                 ;23 =  Envelope #14 5 x 11 1/2
                                 ;24 =  C size sheet
                                 ;25 =  D size sheet
                                 ;26 =  E size sheet
                                 ;27 =  Envelope DL 110 x 220mm
                                 ;28 =  Envelope C5 162 x 229 mm
                                 ;29 =  Envelope C3  324 x 458 mm
                                 ;30 =  Envelope C4  229 x 324 mm
                                 ;31 =  Envelope C6  114 x 162 mm
                                 ;32 =  Envelope C65 114 x 229 mm
                                 ;33 =  Envelope B4  250 x 353 mm
                                 ;34 =  Envelope B5  176 x 250 mm
                                 ;35 =  Envelope B6  176 x 125 mm
                                 ;36 =  Envelope 110 x 230 mm
                                 ;37 =  Envelope Monarch 3.875 x 7.5 in
                                 ;38 =  6 3/4 Envelope 3 5/8 x 6 1/2 in
                                 ;39 =  US Std Fanfold 14 7/8 x 11 in
                                 ;40 =  German Std Fanfold 8 1/2 x 12 in
                                 ;41 =  German Legal Fanfold 8 1/2 x 13 in

    PaperLength        SmallInt  ;Overrides PaperSize Length
    PaperWidth         SmallInt  ;Overrides PaperSize Width
    Scale              SmallInt  ;Factor by which print is scaled
    Copies             SmallInt
    DefaultPaperSource SmallInt
    PrintQuality       SmallInt  ;Draft = -1
                                 ;Low = -2
                                 ;Medium = -3
                                 ;High = -4
                                 ;Positive number = dots per inch

    Color              SmallInt  ;Monochrome = 1
                                 ;Color = 2

    Duplex             SmallInt  ;Simplex = 1
                                 ;Vertical = 2
                                 ;Horizontal = 3

    YResolution        SmallInt  ;Specifies the y-resolution,
                                 ;in dots per inch of the printer.
                                 ;If > 0, PrintQuality specifies
                                 ;the x-resolution

    TTOption           SmallInt  ;True Type Fonts:
                                 ;  1 = Print fonts as graphics
                                 ;  2 = Download as soft fonts
                                 ;  3 = Substitute device fonts
                                 ;  4 = Download as outline soft fonts

    Collate            SmallInt  ; 0 = False
                                 ; 1 = True

    FormName           String
    Attributes         LongInt
    Priority           LongInt
    DefaultPriority    LongInt
    StartTime          LongInt  ;Minutes since 12:00am GMT
    UntilTime          LongInt  ;Minutes since 12:00am GMT
    Status             LongInt
    JobsQueued         LongInt
    AveragePPM         LongInt
  endRecord
endType
The Attributes and Status fields are bit map flags and are further mapped into the following Paradox Record Types:
Type
W32PrinterAttributes =
  Record
    Queued        Logical
    Default       Logical
    Shared        Logical
    Network       Logical
    Hidden        Logical
    Local         Logical
    EnableDevq    Logical
    KeepJobs      Logical
    CompleteFirst Logical
    WorkOffline   Logical  ;Win 95 only
    EnableBIDI    Logical  ;Win 95 only
    RawOnly       Logical
    Published     Logical
  endRecord
endType
Type
W32PrinterStatus =
  Record
    Ready             Logical
    Paused            Logical
    Error             Logical
    PendingDeletion   Logical
    PaperJam          Logical
    PaperOut          Logical
    ManualFeed        Logical
    PaperProblem      Logical
    Offline           Logical
    IOActive          Logical
    Busy              Logical
    Printing          Logical
    OutputBinFul      Logical
    NotAvailable      Logical
    Waiting           Logical
    Processing        Logical
    Initializing      Logical
    WarmingUp         Logical
    TonerLow          Logical
    NoToner           Logical
    PagePunt          Logical
    UserIntervention  Logical
    OutOfMemory       Logical
    DoorOpen          Logical
    ServerUnknown     Logical
    PowerSave         Logical
  endRecord
endType
The Win32 API methods used are:
Uses "winspool.drv"
  OpenPrinter(
    pName cPtr,
    hPrn cPtr,
    pDefault cLong) cLong [stdcall "OpenPrinterA"]
  ClosePrinter(hPrn cLong) cLong [stdcall]
  GetPrinter(
    hPrn cLong,
    cLevel cLong,
    pPrinter cLong,
    cbBuf cLong,
    pcbNeeded cPtr) cLong [stdcall "GetPrinterA"]
endUses
hPrn - Handle to the printer
pName = Name of printer
pDefault - Always 0 for our use
cLevel = Level or type of printer information structure to return - we will use 2
pPrinter = pointer to address of returned structure
cbBuf = size of memory addressed by pPrinter
pcbNeeded = size of memory needed to hold pPrinter

Example:
method GetPrinterInfo(var stPrinter String) W32PrinterInfo
var
  liPrinterHandle   LongInt
  liReturn          LongInt
  liSizeNeeded      LongInt
  liSizeUsed        LongInt
  liMemoryStructure LongInt
  liPointer         LongInt
  liStringSize      LongInt
  liDevMode         LongInt
  liDevNameSize     LongInt
  pir               W32PrinterInfo
endVar
;
; Initialize return record
;
  pir.RecordAvailable = False
  pir.PrintServer = blank()
  pir.PrinterName = blank()
  pir.ShareName = blank()
  pir.Port = blank()
  pir.Driver = blank()
  pir.Comment = blank()
  pir.Location = blank()
  pir.SeparatorPage = blank()
  pir.PrintProcessor = blank()
  pir.DataType = blank()
  pir.Parameters = blank()
  pir.DeviceName = blank()
  pir.SpecVersion = 0
  pir.DriverVersion = 0
  pir.Orientation = 0
  pir.PaperSize = 0
  pir.PaperLength = 0
  pir.PaperWidth = 0
  pir.Scale = 0
  pir.Copires = 0
  pir.DefaultPaperSource = 0
  pir.PrintQuality = 0
  pir.Color = 0
  pir.Duplex = 0
  pir.YResolution = 0
  pir.TTOption = 0
  pir.Collate = 0
  pir.FormName = blank()
  pir.Attributes = 0
  pir.Priority = 0
  pir.DefaultPriority = 0
  pir.StartTime = 0
  pir.UntilTime = 0
  pir.Status = 0
  pir.JobsQueued = 0
  pir.AveragePPM = 0
;
; 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 = GetPrinter(
        liPrinterHandle,
        2,
        0,
        0,
        liSizeNeeded)
;
; Allocate memory for Printer Info Structure Level 2
;
      liMemoryStructure = GlobalAlloc(fromHex("0x40"),liSizeNeeded)
;
; Get Printer Info Structure
;
      liReturn = GetPrinter(
        liPrinterHandle,
        2,
        liMemoryStructure,
        liSizeNeeded,
        liSizeUsed)
      switch
        case liReturn = 1 :
;
; Parse Printer Info Structure Level 2
;
          MoveFromMemory(
            liPointer,
            liMemoryStructure,
            4)
;
; Save print server name
;
          pir.PrintServer = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 4,
            4)
;
; Save printer name
;
          pir.PrinterName = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 8,
            4)
;
; Save share name
;
          pir.ShareName = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 12,
            4)
;
; Save Port name
;
          pir.Port = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 16,
            4)
;
; Save driver name
;
          pir.Driver = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 20,
            4)
;
; Save comment
;
          pir.Comment = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 24,
            4)
;
; Save location
;
          pir.Location = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 32,
            4)
;
; Save separator page
;
          pir.SeparatorPage = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 36,
            4)
;
; Save print processor
;
          pir.PrintProcessor = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 40,
            4)
;
; Save data type
;
          pir.DataType = cmSaveString(liPointer)
          MoveFromMemory(
            liPointer,
            liMemoryStructure + 44,
            4)
;
; Save parameters
;
          pir.Parameters = cmSaveString(liPointer)
;
; Get pointer to DevMode structure
;
          MoveFromMemory(
            liDevMode,
            liMemoryStructure + 28,
            4)
;
; Save device name
;
          pir.DeviceName = cmSaveFixedString(liDevMode,32)
          liDevMode = liDevMode + 32
;
; Save spec version
;
          MoveFromMemory(
            pir.SpecVersion,
            liDevMode,
            2)
;
; Save driver version
;
          MoveFromMemory(
            pir.DriverVersion,
            liDevMode + 2,
            2)
;
; Save orientation
;
          MoveFromMemory(
            pir.Orientation,
            liDevMode + 12,
            2)
;
; Save paper size
;
          MoveFromMemory(
            pir.PaperSize,
            liDevMode + 14,
            2)
;
; Save paper length
;
          MoveFromMemory(
            pir.PaperLength,
            liDevMode + 16,
            2)
;
; Save paper width
;
          MoveFromMemory(
            pir.PaperWidth,
            liDevMode + 18,
            2)
;
; Save scaling factor
;
          MoveFromMemory(
            pir.Scale,
            liDevMode + 20,
            2)
;
; Save number copies
;
          MoveFromMemory(
            pir.Copies,
            liDevMode + 22,
            2)
;
; Save default paper source
;
          MoveFromMemory(
            pir.DefaultPaperSource,
            liDevMode + 24,
            2)
;
; Save print quality
;
          MoveFromMemory(
            pir.PrintQuality,
            liDevMode + 26,
            2)
;
; Save color
;
          MoveFromMemory(
            pir.Color,
            liDevMode + 28,
            2)
;
; Save duplex
;
          MoveFromMemory(
            pir.Duplex,
            liDevMode + 30,
            2)
;
; Save resolution
;
          MoveFromMemory(
            pir.YResolution,
            liDevMode + 32,
            2)
;
; Save TTOption
;
          MoveFromMemory(
            pir.TTOption,
            liDevMode + 34,
            2)
;
; Save Collate
;
          MoveFromMemory(
            pir.Collate,
            liDevMode + 36,
            2)
;
; Save form name
;
          pir.FormName = cmSaveFixedString(
            liDevMode + 38,
            32)
;
; Save attributes
;
          MoveFromMemory(
            pir.Attributes,
            liMemoryStructure + 52,
            4)
;
; Save priority
;
          MoveFromMemory(
            pir.Priority,
            liMemoryStructure + 56,
            4)
;
; Save default priority
;
          MoveFromMemory(
            pir.DefaultPriority,
            liMemoryStructure + 60,
            4)
;
; Save start time
;
          MoveFromMemory(
            pir.StartTime,
            liMemoryStructure + 64,
            4)
;
; Save until time
;
          MoveFromMemory(
            pir.UntilTime,
            liMemoryStructure + 68,
            4)
;
; Save status
;
          MoveFromMemory(
            pir.Status,
            liMemoryStructure + 72,
            4)
;
; Save jobs queued
;
          MoveFromMemory(
            pir.JobsQueued,
            liMemoryStructure + 76,
            4)
;
; Save average ppm
;
          MoveFromMemory(
            pir.AveragePPM,
            liMemoryStructure + 80,
            4)
      endSwitch
;
; Release Memory
;
      liReturn = GlobalFree(liMemoryStructure)
;
; Release printer handle
;
      liReturn = ClosePrinter(liPrinterHandle)
      pir.RecordAvailable = True
  endSwitch
  return pir
endMethod
The mapping of the bit map flags in pir.Status is shown below.
Const
;
; Printer Status
;
  cnStatusReady            = 0
  cnStatusPaused           = 1
  cnStatusError            = 2
  cnStatusPendingDeletion  = 4
  cnStatusPaperJam         = 8
  cnStatusPaperOut         = 16
  cnStatusManualFeed       = 32
  cnStatusPaperProblem     = 64
  cnStatusOffLine          = 128
  cnStatusIOActive         = 256
  cnStatusBusy             = 512
  cnStatusPrinting         = 1024
  cnStatusOutputBinFull    = 2048
  cnStatusNotAvailable     = 4096
  cnStatusWaiting          = 8192
  cnStatusProcessing       = 16384
  cnStatusInitializing     = 32768
  cnStatusWarmingUp        = 65536
  cnStatusTonerLow         = 131072
  cnStatusNoToner          = 262144
  cnStatusPagePunt         = 524288
  cnStatusUserIntervention = 1048576
  cnStatusOutOfMemory      = 2097152
  cnStatusDoorOpen         = 4194304
  cnStatusServerUnknown    = 8388608
  cnStatusPowerSave        = 16777216
endConst

method GetPrinterStatus(liStatus LongInt) W32PrinterStatus
var
  ps  W32PrinterStatus
endVar
;
; Get Status of Printer
;
  switch
    case liStatus.bitAnd(cnStatusReady) = cnStatusReady :
      ps.Ready = True
    otherwise :
      ps.Ready = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusPaused) = cnStatusPaused :
      ps.Paused = True
    otherwise :
      ps.Paused = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusError) = cnStatusError :
      ps.Error = True
    otherwise :
      ps.Error = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusPendingDeletion) = cnStatusPendingDeletion :
      ps.PendingDeletion = True
    otherwise :
      ps.PendingDeletion = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusPaperJam) = cnStatusPaperJam :
      ps.PaperJam = True
    otherwise :
      ps.PaperJam = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusPaperOut) = cnStatusPaperOut :
      ps.PaperOut = True
    otherwise :
      ps.PaperOut = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusManualFeed) = cnStatusManualFeed :
      ps.ManualFeed = True
    otherwise :
      ps.ManualFeed = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusPaperProblem) = cnStatusPaperProblem :
      ps.PaperProblem = True
    otherwise :
      ps.PaperProblem = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusOffLine) = cnStatusOffLine :
      ps.OffLine = True
    otherwise :
      ps.OffLine = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusIOActive) = cnStatusIOActive :
      ps.IOActive = True
    otherwise :
      ps.IOActive = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusBusy) = cnStatusBusy :
      ps.Busy = True
    otherwise :
      ps.Busy = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusPrinting) = cnStatusPrinting :
      ps.Printing = True
    otherwise :
      ps.Printing = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusOutputBinFull) = cnStatusOutputBinFull :
      ps.OutputBinFull = True
    otherwise :
      ps.OutputBinFull = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusNotAvailable) = cnStatusNotAvailable :
      ps.NotAvailable = True
    otherwise :
      ps.NotAvailable = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusWaiting) = cnStatusWaiting :
      ps.Waiting = True
    otherwise :
      ps.Waiting = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusProcessing) = cnStatusProcessing :
      ps.Processing = True
    otherwise :
      ps.Processing = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusInitializing) = cnStatusInitializing :
      ps.Initializing = True
    otherwise :
      ps.Initializing = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusWarmingUp) = cnStatusWarmingUp :
      ps.WarmingUp = True
    otherwise :
      ps.WarmingUp = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusTonerLow) = cnStatusTonerLow :
      ps.TonerLow = True
    otherwise :
      ps.TonerLow = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusNoToner) = cnStatusNoToner :
      ps.NoToner = True
    otherwise :
      ps.NoToner = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusPagePunt) = cnStatusPagePunt :
      ps.PagePunt = True
    otherwise :
      ps.PagePunt = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusUserIntervention) = cnStatusUserIntervention :
      ps.UserIntervention = True
    otherwise :
      ps.UserIntervention = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusOutOfMemory) = cnStatusOutOfMemory :
      ps.OutOfMemory = True
    otherwise :
      ps.OutOfMemory = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusDoorOpen) = cnStatusDoorOpen :
      ps.DoorOpen = True
    otherwise :
      ps.DoorOpen = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusServerUnknown) = cnStatusServerUnknown :
      ps.ServerUnknown = True
    otherwise :
      ps.ServerUnknown = False
  endSwitch
  switch
    case liStatus.bitAnd(cnStatusPowerSave) = cnStatusPowerSave :
      ps.PowerSave = True
    otherwise :
      ps.PowerSave = False
  endSwitch
  return ps
endMethod
The mapping of the bit map flags in pir.Attributes is shown below:
Const
;
; Printer Attributes
;
  cnPrinterQueued        = 1
  cnPrinterDirect        = 2
  cnPrinterDefault       = 4
  cnPrinterShared        = 8
  cnPrinterNetwork       = 16
  cnPrinterHidden        = 32
  cnPrinterLocal         = 64
  cnPrinterEnableDevq    = 128
  cnPrinterKeepJobs      = 256
  cnPrinterCompleteFirst = 512
  cnPrinterWorkOffline   = 1024
  cnPrinterEnableBIDI    = 2048
  cnPrinterRawOnly       = 4096
  cnPrinterPublished     = 8192
endConst

method GetPrinterAttributes(liAttributes LongInt) W32PrinterAttributes
var
  pa  W32PrinterAttributes
endVar
;
; Retrieve Printer Attributes
;
  switch
    case liAttributes.bitAnd(cnPrinterDirect) = cnPrinterDirect :
      pa.Queued = True
    otherwise :
      pa.Queued = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterDefault) = cnPrinterDefault :
      pa.Default = True
    otherwise :
      pa.Default = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterShared) = cnPrinterShared :
      pa.Shared = True
    otherwise :
      pa.Shared = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterNetwork) = cnPrinterNetwork :
      pa.Network = True
    otherwise :
      pa.Network = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterHidden) = cnPrinterHidden :
      pa.Hidden = True
    otherwise :
      pa.Hidden = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterLocal) = cnPrinterLocal :
      pa.Local = True
    otherwise :
      pa.Local = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterEnableDevq) = cnPrinterEnableDevq :
      pa.EnableDevq = True
    otherwise :
      pa.EnableDevq = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterKeepJobs) = cnPrinterKeepJobs :
      pa.KeepJobs = True
    otherwise :
      pa.KeepJobs = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterCompleteFirst) = cnPrinterCompleteFirst :
      pa.CompleteFirst = True
    otherwise :
      pa.CompleteFirst = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterWorkOffline) = cnPrinterWorkOffline :
      pa.WorkOffline = True
    otherwise :
      pa.WorkOffline = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterEnableBIDI) = cnPrinterEnableBIDI :
      pa.EnableBIDI = True
    otherwise :
      pa.EnableBIDI = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterRawOnly) = cnPrinterRawOnly :
      pa.RawOnly = True
    otherwise :
      pa.RawOnly = False
  endSwitch
  switch
    case liAttributes.bitAnd(cnPrinterPublished) = cnPrinterPublished :
      pa.Published = True
    otherwise :
      pa.Published = False
  endSwitch
  return pa
endMethod
There are three other fields in our Win32 based W32PrinterInfo record that need further interpretation.

DefaultPaperSource

OPAL provides the constants below as a generic description of printer paper sources

Value Constant Description
7 prnAuto Paper source selected automatically
14 prnCassette Cassette
5 prnEnvelope Envelope, automatic feed
6 prnEnvManual Envelope, manual feed
11 prnLargeCapacity Large capacity paper source
10 prnLargeFmt Large format paper source
2 prnLower Lower paper tray
4 prnManual Manual feed
3 prnMiddle Middle paper tray
1 prnOnlyOne Single paper tray
9 prnSmallFmt Small format paper source
8 prnTractor Tractor feed paper
1 prnUpper Upper paper tray

Each print driver in Windows can map values and associated descriptions in ways that reflect the capabilities of the printer and do not necessarily have a direct relationship to the supplied OPAL constants. In interpreting the default paper source value, we want the print driver itself to tell us what code/description combinations are supported. The Win32 API has numerous methods that return configuration and resource capabilities and the method that we will use for printers is:
Uses "winspool.drv"
  DeviceCapabilities(
    lpDeviceName cPtr,
    lpPort cPtr,
    iIndex cLong,
    lpOutPut cLong,
    dev cLong) cLong [stdcall "DeviceCapabilitiesA"]
endUses
lpDeviceName = Printer Name
lpPort = Printer Port
iIndex = Type of capability to return
lpOutput = Memory address of returned structure
dev = Always 0 for our use

Our basic approach will be to first request the paper bin ID's, then request the paper bin descriptions and combine the two into a DynArray where the array index is the bin ID and the value is the description. Then we can search the array for the default paper source value and return the description.

Example:
Const
;
; Printer Bins
;
  cnDCBins      = 6
  cnDCBinNames  = 12
endConst

method GetPaperSource(stPrinter String,
                      stPort String,
                      siPaperSource SmallInt) String
var
  liNumberBins  LongInt
  liBinPointer  LongInt
  liReturn      LongInt
  arBinNumbers  Array[] SmallInt
  liIndex       LongInt
  stAny         String
  dyPrinterBins DynArray[] String
endVar
  dyPrinterBins.empty()
;
; Get total number of supported printer bins
;
  liNumberBins = DeviceCapabilities(stPrinter,stPort,cnDCBins,0,0)
  switch
    case liNumberBins > 0 :
;
; Allocate memory for Printer Bin ID's
;
      liBinPointer = GlobalAlloc(fromHex("0x40"),liNumberBins * 2)
;
; Fetch supported Bin ID's
;
      liReturn = DeviceCapabilities(stPrinter,stPort,cnDCBins,liBinPointer,0)
;
; Save Bin ID's in local array
;
      arBinNumbers.grow(liNumberBins)
      siAny = 0
      for liIndex from 1 to liNumberBins
        arBinNumbers[liIndex] = 0
        MoveFromMemory(arBinNumbers[liIndex],liBinPointer + (liIndex * 2) - 2,2)
      endFor
;
; Free Printer Bin ID memory
;
      liReturn = GlobalFree(liBinPointer)
;
; Allocate memory for Printer Bin Names's
;
      liBinPointer = GlobalAlloc(fromHex("0x40"),liNumberBins * 24)
;
; Fetch supported Bin Names
;
      liReturn = DeviceCapabilities(stPrinter,stPort,cnDCBinNames,liBinPointer,0)
;
; Save Bin Names and associated ID's in local DynArray
;
      for liIndex from 1 to liNumberBins
        stAny = cmSaveFixedString(liBinPointer + (liIndex * 24) - 24,24)
        dyPrinterBins[arBinNumbers[liIndex]] = stAny
      endFor
;
; Free Printer Bin ID memory
;
      liReturn = GlobalFree(liBinPointer)
  endSwitch
;
; Lookup paper source
;
  switch
    case dyPrinterBins.contains(siPaperSource) = True :
      stAny = dyPrinterBins[siPaperSource]
    otherwise :
      stAny = "Unknown ("
              + strval(siPaperSource)
              + ")"
  endSwitch
  return stAny
endMethod
StartTime/UntilTime

Some print drivers support the specification of a window of time where the printer is available. Windows stores the times as the number of minutes since 12:00AM GMT. We will convert this value into local time. Two factors are needed to calculate local time. One is the local time offset from GMT and the other is any adjustment for daylight savings. The Win32 API method below will provide both.
Uses "kernel32.dll"
  GetTimeZoneInformation(wTZI cLong) cLong [stdcall]
endUses
wTZI is the windows Time Zone Information structure returned.

Example:
method GetTimeFromMinutes(liMinutes LongInt) Time
;
; TimeZone Structure
;
; + 0   = Bias
; + 4   = Standard Name (32 SmallInt values)
; + 68  = Standard Date Structure
; + 84  = Standard Bias
; + 88  = Daylight Name (32 SmallInt values)
; + 152  = Daylight Date Structure
; + 168  = Daylight Bias
;
; Total Length = 172 bytes
;
; Date Structure (all type SmallInt)
;   Year,Month,Day of Week,Day,Hour,Minute,Second,Millisecond
;
var
  siAny          SmallInt
  liPointer      LongInt
  fromLocation   LongInt
  liReturn       LongInt
  liBias         LongInt
  liDaylightBias LongInt
endVar
;
; Initialize local variables
;
  liBias = 0
  liDaylightBias = 0
;
; Get Memory Block for API call
;
  liPointer = GlobalAlloc(fromHex("0x40"),172)
;
; Retrieve Time Zone Structure
;
  liReturn = GetTimeZoneInformation(liPointer)
;
; Save Bias, Standard Bias and Daylight Bias
;
  liReturn = MoveFromMemory(liBias,liPointer,4)
  liReturn = MoveFromMemory(liDaylightBias,liPointer + 168,4)
;
; Release memory allocated to Time Zone Structure
;
  liReturn = GlobalFree(liPointer)
  return cmTimeFromMinutes(liMinutes - liBias - liDaylightBias)
endMethod
Proc cmTimeFromMinutes(liMinute LongInt) Time
;
; Given minutes return a Time Type
;
var
  dtTotalTime   DateTime
endVar
  dtTotalTime = dateTime(cmMod(1440,liMinute) * 60000)
  return time(dtTotalTime)
endProc
Proc cmMod(nuBaseMultiple Number,nuBaseNumber Number) Number
;
; Variation of normal mod to handle numbers less than zero
;
var
  nuNormalizedNumber  Number
endVar
  nuNormalizedNumber = nuBaseNumber.mod(nuBaseMultiple)
  switch
    case nuNormalizedNumber < 0 :
      nuNormalizedNumber = nuNormalizedNumber + nuBaseMultiple
  endSwitch
  return nuNormalizedNumber
endProc

Tidak ada komentar:

Posting Komentar