Retrieving Printer Capabilities

In Enumerating printer forms article the DeviceCapabilities

function is used to mark print forms supported by particular printer. It can be used to retrieve other printer capabilities as well.

$SAMPLECODE$

Requires Windows 2000 or later. The WinApiErrMsg function in the code below is from Retrieving Windows system error message. The Windows API support class

is used to handle Windows API structures.

The code will run 'as is' in VFP8 or later because it uses Collection class to store the list of forms and bins. It can be easily adapted to run in earlier VFP versions.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
CLEAR

loPC = NEWOBJECT("PrinterCapabilities", "PrinterCapabilities.fxp", "", "", "English")

* Enumerate forms for specified Windows printer
*loPC.cPrinterName = "Adobe PDF"

loPC.cPrinterName = GETPRINTER()

IF NOT loPC.GetOtherCapabilities()
	? loPC.cErrorMessage
	? loPC.cApiErrorMessage
	* Error
	RETURN .F.
ENDIF

? "Printer: " + loPC.cPrintername

IF loPC.lPrnColor
	? "Color is supported"
ENDIF	

IF loPC.lPrnDuplex 
	? "Duplex is supported"
ENDIF	

? "Max Paper Size: " + TRANSFORM(loPC.nPrnMaxPaperWidth) + " x " + TRANSFORM(loPC.nPrnMaxPaperLength) 
? "Min Paper Size: " + TRANSFORM(loPC.nPrnMinPaperWidth) + " x " + TRANSFORM(loPC.nPrnMinPaperLength) 

? "Max # of copies: " + TRANSFORM(loPC.nPrnMaxCopies)

IF loPC.nPrnOrientation > 0
	? "Landscape is supported. Rotatated " + TRANSFORM(loPC.nPrnOrientation) + " degrees"
ENDIF	

?
* Bins
? "Bins"
IF NOT loPC.GetBinList()
	? loPC.cErrorMessage
	? loPC.cApiErrorMessage
	* Error
	RETURN .F.
ENDIF

FOR i=1 TO loPC.oPrnBinList.Count
	loObject = loPC.oPrnBinList.Item(i)
	? i, loObject.BinID, loObject.BinName
ENDFOR

? 


* Forms
? "Print Forms"
IF NOT loPC.GetFormList()
	? loPC.cErrorMessage
	? loPC.cApiErrorMessage
	* Error
	RETURN .F.
ENDIF

FOR i=1 TO loPC.oPrnFormList.Count
	loObject = loPC.oPrnFormList.Item(i)
	? i, loObject.FormID, loObject.FormName, TRANSFORM(loObject.Width) + " x " + TRANSFORM(loObject.Height)
ENDFOR

loPC = Null
RETURN

The PrinterCapabilities

class code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

* PrinterCapabilities.prg
#INCLUDE PrinterCapabilities.h
DEFINE CLASS PrinterCapabilities AS Custom
	HIDDEN nInch2mm, nCm2mm, nCoefficient, aList[1]

	* Printer name
	cPrinterName = ""
	* The form attributes are stored in tenths of millimeters
	* It can be converted by class to inches ("English") or centimeters ("Metric")
	cUnit = "Internal"
	* Specified how to round result of conversion
	nRound = 0
	* Conversion Coefficients
	nInch2mm = 25.4
	nCm2mm = 10
	nCoefficient = 1

	* Error code and Error message returned by Win API
	cApiErrorMessage = ""
	* Error message returned by class itself (none-API error)
	cErrorMessage = ""
	* Win API support class
	oWas = NULL
	* For internal use
	DIMENSION aList[1]

	* Collection of Print Forms retrieved
	oPrnFormList = Null
	* Bin list
	oPrnBinList = Null
	* Color support
	lPrnColor  = .F.
	* Duplex support
	lPrnDuplex  = .F.
	* Max paper size printer supports
    nPrnMaxPaperLength = NULL
    nPrnMaxPaperWidth  = NULL
	* Min paper size printer supports
    nPrnMinPaperLength = NULL
    nPrnMinPaperWidth  = NULL
	* Max Copies supportd
	nPrnMaxCopies = 1
	* Orientation: 0-Lanscape not supported
	nPrnOrientation = 0
	
	PROCEDURE Init(tcPrinterName, tcUnit, tnRound)

		IF PCOUNT() >= 1 AND NOT EMPTY(tcPrinterName)
			This.cPrinterName = tcPrinterName
		ELSE
			* Default VFP printer
			This.cPrinterName = SET("Printer",3)
		ENDIF	
		IF PCOUNT() >= 2
			This.cUnit = PROPER(tcUnit)
		ELSE	
			This.cUnit = "English"
		ENDIF
		IF PCOUNT() = 3
			This.nRound = tnRound
		ENDIF
		This.oPrnFormList = CREATEOBJECT("Collection")
		This.oPrnBinList  = CREATEOBJECT("Collection")

		This.oWas = NEWOBJECT("WinApiSupport", "WinApiSupport.fxp")

		DECLARE Long GetLastError IN kernel32

	ENDPROC

	PROCEDURE cUnit_Assign(tcUnit)
		IF INLIST(tcUnit, "English", "Metric", "Internal")
			This.cUnit = PROPER(tcUnit)
		ELSE
			RETURN
		ENDIF
		* Calculate conversion coefficient
		DO CASE
		CASE PROPER(This.cUnit) = "English"
			This.nCoefficient = This.nInch2mm * 10
			IF This.nRound = 0
				This.nRound = 2
			ENDIF
		CASE PROPER(This.cUnit) = "Metric"
			This.nCoefficient = This.nCm2mm * 10
		OTHERWISE
			This.cUnit = "Internal"
			This.nCoefficient = 1
		ENDCASE
	ENDPROC

	PROCEDURE GetOtherCapabilities()
		LOCAL lnRetVal
		* Color 
		lnRetVal = This.GetCapability(DC_COLORDEVICE)
		IF (lnRetVal < 0)
			RETURN .F.
		ENDIF
		This.lPrnColor = (lnRetVal > 0)
		* Duplex
		lnRetVal = This.GetCapability(DC_DUPLEX)
		IF (lnRetVal < 0)
			RETURN .F.
		ENDIF
		This.lPrnDuplex = (lnRetVal > 0)
		* Max Paper Size
		lnRetVal = This.GetCapability(DC_MAXEXTENT)
		IF (lnRetVal <= 0)
			RETURN .F.
		ENDIF
		This.nPrnMaxPaperLength = This.ConvertDimension(BITRSHIFT(lnRetVal,16))
		This.nPrnMaxPaperWidth  = This.ConvertDimension(BITAND(lnRetVal, 0xFFFF))
		* Min Paper Size
		lnRetVal = This.GetCapability(DC_MINEXTENT)
		IF (lnRetVal <= 0)
			RETURN .F.
		ENDIF
		This.nPrnMinPaperLength = This.ConvertDimension(BITRSHIFT(lnRetVal,16))
		This.nPrnMinPaperWidth  = This.ConvertDimension(BITAND(lnRetVal, 0xFFFF))
		* Max # of copies
		lnRetVal = This.GetCapability(DC_COPIES)
		IF (lnRetVal < 0)
			RETURN .F.
		ENDIF
		This.nPrnMaxCopies = lnRetVal 
		* Orientation
		lnRetVal = This.GetCapability(DC_ORIENTATION)
		IF (lnRetVal < 0)
			RETURN .F.
		ENDIF
		This.nPrnOrientation = lnRetVal 

		RETURN .T.	
	ENDPROC

	PROCEDURE GetBinList()
		LOCAL llSuccess, lnCount, laBins[1], laBinNames[1]
		
		lnCount = This.GetCapability(DC_BINS, 2)
		IF (lnCount <= 0)
			RETURN .F.
		ENDIF
		ACOPY(This.aList, laBins)
		
		lnCount = This.GetCapability(DC_BINNAMES, 24, .T.)
		IF (lnCount <= 0)
			RETURN .F.
		ENDIF
		ACOPY(This.aList, laBinNames)

		FOR lnIndex = 1 TO lnCount
			loBin 			= This.GetOneObj("BIN")
			loBin.BinID   	= This.oWas.Short2Num(laBins[lnIndex])
			loBin.BinName 	= laBinNames[lnIndex]
			This.oPrnBinList.Add(loBin, TRANSFORM(loBin.BinID))
		ENDFOR
			
		RETURN .T.	
	ENDPROC

	PROCEDURE GetFormList()
		LOCAL llSuccess, lnCount, laForms[1], laFormNames[1], laFormSizes[1]
		
		lnCount = This.GetCapability(DC_PAPERS, 2)
		IF (lnCount <= 0)
			RETURN .F.
		ENDIF
		ACOPY(This.aList, laForms)
		
		lnCount = This.GetCapability(DC_PAPERNAMES, 64, .T.)
		IF (lnCount <= 0)
			RETURN .F.
		ENDIF
		ACOPY(This.aList, laFormNames)

		lnCount = This.GetCapability(DC_PAPERSIZE, 8)
		IF (lnCount <= 0)
			RETURN .F.
		ENDIF
		ACOPY(This.aList, laFormSizes)

		FOR lnIndex = 1 TO lnCount
			loForm 			= This.GetOneObj("FORM")
			loForm.FormID   = This.oWas.Short2Num(laForms[lnIndex])
			loForm.FormName = laFormNames[lnIndex]
			loForm.Width 	= This.ConvertDimension(This.oWas.Long2Num(LEFT(laFormSizes[lnIndex],4)))
			loForm.Height	= This.ConvertDimension(This.oWas.Long2Num(RIGHT(laFormSizes[lnIndex],4)))
			
			This.oPrnFormList.Add(loForm, TRANSFORM(loForm.FormID))
		ENDFOR
			
		RETURN .T.	
	ENDPROC

	HIDDEN PROCEDURE GetCapability(tnCapability, tnItemSize, tlNts)
		LOCAL lnResult, lcBuffer, lnIndex, llListReturned, lnItemCount
		* Is list returned for this capability
		llListReturned = (PCOUNT() >= 2)
		* Get a result or # of items in the list
		lnResult = DeviceCapabilitiesResult(This.cPrinterName, "", tnCapability, 0, 0)
		DO CASE
		CASE lnResult < 0
			* Call to DeviceCapabilities failed
			This.cErrorMessage = "Capability requested: " + TRANSFORM(tnCapability) + ;
									". DeviceCapabilities() failed."
			This.cApiErrorMessage = WinApiErrMsg(GetLastError())
			RETURN lnResult 
		CASE NOT llListReturned 	
			* The capabilty is returned in lnResult
			RETURN lnResult 
		OTHERWISE
			lnItemCount = lnResult
		ENDCASE

		* Allocate buffer for the item list
		lcBuffer = REPLICATE(CHR(0), tnItemSize * lnItemCount)
		* Get the list
		lnResult = DeviceCapabilitiesList(This.cPrinterName, "", tnCapability, @lcBuffer, 0)


		IF lnResult < 0
			* Call to DeviceCapabilities failed
			This.cErrorMessage = "Capability requested: " + TRANSFORM(tnCapability) + ;
									". DeviceCapabilities() failed."
			This.cApiErrorMessage = WinApiErrMsg(GetLastError())
			RETURN 0
		ENDIF

		* The capabilty is returned as alist in lcBuffer. Stuff it into array property aList
		FOR lnIndex=1 To lnResult
			DIMENSION This.aList[lnIndex]
			This.aList[lnIndex] = SUBSTR(lcBuffer, (lnIndex-1) * tnItemSize + 1, tnItemSize)
			IF tlNts
				* An item is null terminated string.
				This.aList[lnIndex] = LEFT(This.aList[lnIndex], AT(CHR(0), This.aList[lnIndex] + CHR(0))-1)
			ENDIF
		ENDFOR
		RETURN lnResult
	ENDPROC

	FUNCTION ConvertDimension(tnValue)
		RETURN ROUND(tnValue / This.nCoefficient, This.nRound)
	ENDFUNC

	* Create an object with attributes
	PROCEDURE GetOneObj(tcType)
		LOCAL loObject
		loObject = NEWOBJECT("Empty")
		DO CASE
		CASE UPPER(tcType) == "FORM"
			ADDPROPERTY(loObject, "FormId", 0)
			ADDPROPERTY(loObject, "FormName", "")
			ADDPROPERTY(loObject, "Width", 0)
			ADDPROPERTY(loObject, "Height", 0)
		CASE UPPER(tcType) == "BIN"
			ADDPROPERTY(loObject, "BinId", 0)
			ADDPROPERTY(loObject, "BinName", "")
		ENDCASE	
		RETURN loObject
	ENDPROC

	PROCEDURE ClearErrors
		This.cErrorMessage = ""
		This.cApiErrorMessage = ""
	ENDPROC

ENDDEFINE
*----------------------------------------------------------------------------------------------

* Can be called with pOutput=0 (Null) to get item count
FUNCTION DeviceCapabilitiesResult(pDevice, pPort, fwCapability, pOutput, pDevMode)
	DECLARE Long DeviceCapabilities IN winspool.drv AS DeviceCapabilitiesResult ;
		String pDevice, String pPort, Long fwCapability, ;
		long pOutput, Long pDevMode
	RETURN DeviceCapabilitiesResult(pDevice, pPort, fwCapability, pOutput, pDevMode)
ENDFUNC

* Retirns Item list into pOutput
FUNCTION DeviceCapabilitiesList(pDevice, pPort, fwCapability, pOutput, pDevMode)
	DECLARE Long DeviceCapabilities IN winspool.drv AS DeviceCapabilitiesList ;
		String pDevice, String pPort, Long fwCapability, ;
		String @pOutput, Long pDevMode
	RETURN DeviceCapabilitiesList(pDevice, pPort, fwCapability, @pOutput, pDevMode)
ENDFUNC

The PrinterCapabilities.h

header file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

* PrinterCapabilities.h

* Device capabilities indices 
#DEFINE DC_FIELDS           1
#DEFINE DC_PAPERS           2
#DEFINE DC_PAPERSIZE        3
#DEFINE DC_MINEXTENT        4
#DEFINE DC_MAXEXTENT        5
#DEFINE DC_BINS             6
#DEFINE DC_DUPLEX           7
#DEFINE DC_SIZE             8
#DEFINE DC_EXTRA            9
#DEFINE DC_VERSION          10
#DEFINE DC_DRIVER           11
#DEFINE DC_BINNAMES         12
#DEFINE DC_ENUMRESOLUTIONS  13
#DEFINE DC_FILEDEPENDENCIES 14
#DEFINE DC_TRUETYPE         15
#DEFINE DC_PAPERNAMES       16
#DEFINE DC_ORIENTATION      17
#DEFINE DC_COPIES           18

*!*	#DEFINE DC_BINADJUST            19
*!*	#DEFINE DC_EMF_COMPLIANT        20
*!*	#DEFINE DC_DATATYPE_PRODUCED    21
*!*	#DEFINE DC_COLLATE              22
*!*	#DEFINE DC_MANUFACTURER         23
*!*	#DEFINE DC_MODEL                24

#DEFINE DC_PERSONALITY          25
#DEFINE DC_PRINTRATE            26
#DEFINE DC_PRINTRATEUNIT        27
#DEFINE   PRINTRATEUNIT_PPM     1
#DEFINE   PRINTRATEUNIT_CPS     2
#DEFINE   PRINTRATEUNIT_LPM     3
#DEFINE   PRINTRATEUNIT_IPM     4
#DEFINE DC_PRINTERMEM           28
#DEFINE DC_MEDIAREADY           29
#DEFINE DC_STAPLE               30
#DEFINE DC_PRINTRATEPPM         31
#DEFINE DC_COLORDEVICE          32
#DEFINE DC_NUP                  33
#DEFINE DC_MEDIATYPENAMES       34
#DEFINE DC_MEDIATYPES           35

Comments