Add and delete custom printer forms

To distribute applications that use reports with custom paper size you have to create custom printer forms on every destination PC that runs Windows 2000 or later. The MS KB article Q157172

explains how it can be done manually. However there's a way to do that programmatically using Windows API.


The code is based on the working code posted by Jim Livermore on UT and work of George Tasker and late Ed Rauh.

$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.

 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

lcPrinterName = "Adobe PDF"

* All sizes in inches
ooo = NEWOBJECT("AddPrinterForm", "AddPrinterForm.fxp")
IF NOT ooo.AddForm("MyCustomForm1", 5,7, lcPrinterName)
	? ooo.cErrorMessage
	? ooo.cApiErrorMessage 
  * Error
ENDIF

*!*	* Delete just created form
*!*	ooo = NEWOBJECT("AddPrinterForm", "AddPrinterForm.fxp")
*!*	IF NOT ooo.DeleteForm("MyCustomForm1", lcPrinterName)
*!*		? ooo.cErrorMessage
*!*		? ooo.cApiErrorMessage 
*!*	  * Error
*!*	ENDIF


* All sizes in cm
ooo = NEWOBJECT("AddPrinterForm", "AddPrinterForm.fxp", "", "Metric")
IF NOT ooo.AddForm("MyCustomForm2", 15,17, lcPrinterName)
  * Error
ENDIF

*!*	* Delete just created form
*!*	ooo = NEWOBJECT("AddPrinterForm", "AddPrinterForm.fxp")
*!*	IF NOT ooo.DeleteForm("MyCustomForm2", lcPrinterName)
*!*		? ooo.cErrorMessage
*!*		? ooo.cApiErrorMessage 
*!*	  * Error
*!*	ENDIF

The AddPrinterForm

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

* AddPrinterForm.prg
DEFINE CLASS AddPrinterForm AS Custom

	HIDDEN cUnit, cPrinterName, nFormHeight, nFormWidth, nLeftMargin, ;
              nTopMargin, nRightMargin, nBottomMargin, nInch2mm, nCm2mm, nCoefficient, hHeap

	cUnit = "English"		&& inches or Metric - cm's
	cPrinterName = ""
	nFormHeight = 0
	nFormWidth = 0
	nLeftMargin = 0
	nTopMargin = 0
	nRightMargin = 0
	nBottomMargin = 0

	cApiErrorMessage = ""
	cErrorMessage = ""

	nInch2mm = 25.4
	nCm2mm = 10
	nCoefficient = This.nInch2mm * 1000

	hHeap = 0

	* Win API support class
	oWas = NULL

	PROCEDURE Init(tcUnit)
	This.oWas = NEWOBJECT("WinApiSupport", "WinApiSupport.fxp")
	IF PCOUNT() = 1 
		This.cUnit = PROPER(tcUnit)
	ENDIF
	This.LoadApiDlls()
	This.hHeap = HeapCreate(0, 4096, 0)
	* Use Windows default printer
	This.cPrinterName = SET("Printer",2)
	ENDPROC

	PROCEDURE cUnit_Assign(tcUnit)
	IF INLIST(tcUnit, "English", "Metric")
		This.cUnit = PROPER(tcUnit)
	ELSE
		RETURN	
	ENDIF
	* Calculate conversion coefficient
	This.nCoefficient = IIF(PROPER(This.cUnit) = "English", ;
						This.nInch2mm, This.nCm2mm) * 1000
	ENDPROC

	PROCEDURE Destroy
	IF This.hHeap <> 0
		HeapDestroy(This.hHeap)
	ENDIF

	ENDPROC

	PROCEDURE SetFormMargins(tnLeft, tnTop, tnRight, tnBottom)
	WITH This
		.nLeftMargin 	= tnLeft   * .nCoefficient
		.nTopMargin 	= tnTop    * .nCoefficient
		.nRightMargin 	= tnRight  * .nCoefficient
		.nBottomMargin 	= tnBottom * .nCoefficient
	ENDWITH
	ENDPROC

	PROCEDURE AddForm(tcFormName, tnWidth, tnHeight, tcPrinterName)
	LOCAL lhPrinter, llOK, lcForm

	This.nFormWidth  = tnWidth  * This.nCoefficient
	This.nFormHeight = tnHeight * This.nCoefficient
	IF PCOUNT() > 3
		This.cPrinterName = tcPrinterName
	ENDIF

	This.ClearErrors()
	lhPrinter = 0
	IF OpenPrinter(This.cPrinterName, @lhPrinter, 0) = 0
		This.cErrorMessage = "Unable to get printer handle for " + This.cPrinterName 
		This.cApiErrorMessage = WinApiErrMsg(GetLastError())
		RETURN .F.
	ENDIF

	lnFormName = HeapAlloc(This.hHeap, 0, LEN(tcFormName) + 1)
	= SYS(2600, lnFormName, LEN(tcFormName) + 1, tcFormName + CHR(0))

	* Build FORM_INFO_1 structure
	WITH This.oWas
		lcForm = .Num2Long(0) + .Num2Long(lnFormName) + ;
			.Num2Long(This.nFormWidth) + .Num2Long(This.nFormHeight) + ;
			.Num2Long(This.nLeftMargin) + .Num2Long(This.nTopMargin) + ;
			.Num2Long(This.nFormWidth - This.nRightMargin) + ;
			.Num2Long(This.nFormHeight - This.nBottomMargin)
	ENDWITH

	* Finally, call the API
	IF AddForm(lhPrinter, 1, @lcForm) = 0
		This.cErrorMessage = "Unable to Add Form " + tcFormName 
		This.cApiErrorMessage = STRTRAN(WinApiErrMsg(GetLastError()), "file", "form", -1, -1, 3)
		llOK = .F.
	ELSE
		llOK = .T.
	ENDIF
	= HeapFree(This.hHeap, 0, lnFormName)
	= ClosePrinter(lhPrinter)

	RETURN llOK

	PROCEDURE ClearErrors
	This.cErrorMessage = ""
	This.cApiErrorMessage = ""
	ENDPROC
	
	PROCEDURE DeleteForm(tcFormName, tcPrinterName)
	LOCAL lhPrinter, llOK

	IF PCOUNT() > 1
		This.cPrinterName = tcPrinterName
	ENDIF

	This.ClearErrors()
	lhPrinter = 0
	IF OpenPrinter(This.cPrinterName, @lhPrinter, 0) = 0
		This.cErrorMessage = "Unable to get printer handle for " + This.cPrinterName + "."
		This.cApiErrorMessage = WinApiErrMsg(GetLastError())
		RETURN .F.
	ENDIF

	* Finally, call the API
	llOK = ( DeleteForm(lhPrinter, tcFormName) <> 0 )
	IF NOT llOK 
		This.cErrorMessage = "Unable to delete Form " + tcFormName 
		This.cApiErrorMessage = STRTRAN(WinApiErrMsg(GetLastError()), "file", "form", -1, -1, 3)
	ENDIF
	= ClosePrinter(lhPrinter)
	RETURN llOK
	
	HIDDEN PROCEDURE LoadApiDlls
		DECLARE Long HeapCreate IN WIN32API Long dwOptions, Long dwInitialSize, Long dwMaxSize
		DECLARE Long HeapAlloc IN WIN32API Long hHeap, Long dwFlags, Long dwBytes
		DECLARE Long HeapFree IN WIN32API Long hHeap, Long dwFlags, Long lpMem
		DECLARE HeapDestroy IN WIN32API Long hHeap
		DECLARE Long GetLastError IN kernel32
	ENDPROC

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

FUNCTION OpenPrinter(tcPrinterName, thPrinter, tcDefault)
DECLARE Long OpenPrinter IN WinSpool.Drv ;
	String pPrinterName, Long @ phPrinter, String pDefault
RETURN 	OpenPrinter(tcPrinterName, @thPrinter, tcDefault)

FUNCTION ClosePrinter (thPrinter)
DECLARE Long ClosePrinter IN WinSpool.Drv Long hPrinter
RETURN ClosePrinter(thPrinter)


FUNCTION AddForm(thPrinter, tnLevel, tcForm)
DECLARE Long AddForm IN winspool.drv Long hPrinter, Long Level, String @pForm
RETURN AddForm(thPrinter, tnLevel, tcForm)

FUNCTION DeleteForm(thPrinter, tcForm)
DECLARE Long DeleteForm IN winspool.drv Long hPrinter, String  pFormName 
RETURN DeleteForm(thPrinter, tcForm)

Comments

I think this code not working with "HP LaserJet"

How to create Custom Paper with "HP LaserJet 1020" ?

Warm regards,
mk.

how to set paper size for
dot matrix printer