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 classis 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
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
Custom paper with HP LaserJet
How to create Custom Paper with "HP LaserJet 1020" ?
Warm regards,
mk.
dot matrix printer
dot matrix printer