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 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"] EndUsesGlobalAlloc 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 endForPlease 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"] endUsespFlags = 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) endSwitchThe 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 stAnyThe 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"] endUsesstPrinter = 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 endSwitchThe 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 endTypeThe 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 endTypeThe 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"] endUseshPrn - 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 endMethodThe 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 endMethodThe 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 endMethodThere 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"] endUseslpDeviceName = 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 endMethodStartTime/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 |
Senin, 18 Oktober 2010
Win32 API and Printers: Get Default Printer and Printer Properties
Langganan:
Posting Komentar (Atom)
Tidak ada komentar:
Posting Komentar