| Win32 API and Printers: Get Default Printer and Printer Properties |
|
|
|
| Contributed by Rick Kelly | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 22 July 2002 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Learn how the Win32 API can give you more printing control than ever before!Win32 API and Printers Get Default Printer and Printer Properties © 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. 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 servicesIn 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"] endUsesOSVersionInfo 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.
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.aspWin95/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 = 1pNull = 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 nameliBufferSize = 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 printerpName = 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
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 NamelpPort = 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/UntilTimeSome 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] endUseswTZI 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
Conclusion We now have methods that provide access to the Win32 API for some basic printer information. Acknowledgements Liz Woodhouse Tony McGuire Pascal Hutton Tom Krieg Next: Setting the Default Windows Printer |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| < Prev | Next > |
|---|





