|
在Visual FoxPro 6中动态修改打印纸张大小
出处:网络
当FoxPro从2.6升级到Visual FoxPro的时候,使用FoxPro的程序员也从面向过程的时代跨入了面向对象的时代。Visual
FoxPro可借助工具条、对象、可视控件来自动完成界面的设计并执行各种任务,同时不牺牲任何数据库性能。不再只通过代码来完成任务,新环境让我们直接图形化的操作文件、表、对象和类。这是一场多么伟大的革命啊!但是我们很快发现,它革掉了一个很重要的东西,那就是对打印机的控制。我们失去了对打印机的直接控制能力,只能通过打印机驱动程序间接的使用打印机,就连FoxPro的低级函数Fopen()也只能打开文件和串口,失去了打开并口的能力,更要命的是通过打印机驱动程序在Windows中打印是以页为单位,打一行字也要走一页,实在不爽。
怎么办呢,我到网上转了一圈,在Visual FoxPro编程论坛中发现提问最多的问题是打印的问题,其中最多的又是在Windows中打印机走纸以页为单位的问题,很多用Visual FoxPro编写超市POS系统的网友,都为无法控制小票的走纸长度而苦恼。虽然在网上没有找到答案,但也发现了一些线索,有人在以100元的价格兜售一个解决这个问题的软件,我当了试用版看了一下,发现这个软件是用Visual FoxPro编写的,这说明Visual FoxPro有能力解决这个问题。但是100元太贵了如果20元的话我会考虑买一套。通过研究试用版我发现该软件修改的是打印机的自定义纸的大小,打印机的设置都存在Windows的注册表里,虽然修改注册表是一条比较危险的路,但我必须走下去。
注册表太大了,我仔细找了很长时间,最后锁定在“HKEY_LOCAL_MACHINE\ System\CurrentControlSet\Control\Print\Printers\”,在这个键下面有我安装的所有打印机的名字。我试这修改“Epson LQ-1600KII”自定义纸的大小,只有“Default DevMode”的键值发生了变化。这个键的值很长,如果大家感兴趣可以打印出来看,我这里就省略了。这个键的值是以二进制方式存放的,打印出来的是16进制的形式,经过仔细对照发现第48对至第52对发生了变化。进一步研究发现第48对为“01”时打印机是自定义纸,第49对和第50对记录的是打印纸的长度,第51对和第52对记录的是打印纸的宽度。如果设置长度为1000时(0.1毫米)第49对为“E8”、第50对为“03”,实际上1000转换为16进制应该为3E8,这里反了个(这是干吗,不爽!)。我又试了其它几种又自定义纸的打印机结果相同。好了,现在我们知道了打印纸大小的存放地点,下面可以开始研究如何修改它们了。
Visual FoxPro本身没有修改注册表的能力,需要调用Windows的API函数来修改。修改的步骤是这样的:首先读取“HKEY_LOCAL_MACHINE\ System\CurrentControlSet\Control\Print\Printers\Default DevMode”的键值,由于得到的字符串有很多不可显示字符,所以需要由二进制转换为16进制,转换后得到的字符串应与在注册表编辑器(RegEdit.exe)中打印的键值一致,然后用16进制表示的打印纸的长度和宽度替换相应位置的字符,最后再由16进制转换到10进制,转换为10进制时得到的是应回写注册表的字符的ASCII码,再用CHR()函数转换后即可回写注册表。到了这里还不算完,在修改注册表以后还要修改报表文件。Visual FoxPro 的报表文件实际也是一个表,可以用Use命令打开,字段Tag2和expr需要做相应更改。在这里有一点要提醒大家,既然要修改报表文件,那么报表文件就不能编译进可执行文件,必须单独与编译后的程序一起打包发布。
我将该程序做成一个函数供大家调用,如果修改成功函数返回.T.,否则是.F.。最困难的是寻找读取和回写注册表二进制键值的函数,我在网上查阅了很多资料,没有发现如何读取二进制数据的文章,最后是从微软网站上当了一个读取文本类型键值的函数,经试验发现当REG_BINARY=1时该函数可以读取与回写文本类型的键值,REG_BINARY=3时就可以读取与回写二进制的键值。为了避免不必要的麻烦,我在这个函数中没有加入设置默认打印纸为自定义纸的功能,请大家创建报表文件时将报表文件设置为自定义纸。我只是在Win98和WinMe中做了测试,请不要在其它操作系统中使用。我没有尝试修改没有自定义纸张设定的打印机,请大家使用之前确认你使用的打印机可以设置自定义纸张。如何调用本文所附的函数有说明,这里不再赘述。
附源程序:
*调用格式:Print_Page(Print_name,Hight,Width)
*Print_name:在注册表中打印机的名称,如"Epson LQ-1600KII"、"Epson LQ-300K",必须是字符串
*Hight:打印纸的长度,取值范围依据打印机的范围,必须是数字
* Epson LQ-1600KII的取值范围:127--23119(单位:0.1毫米)
*Width:打印纸的宽度,取值范围依据打印机的范围,必须是数字
* Epson LQ-1600KII的取值范围:127--4191(单位:0.1毫米)
FUNCTION Print_Page
PARAMETERS Print_Name, _Hight, _Width
#DEFINE HKEY_LOCAL_MACHINE -2147483646
Local nKey, cSubKey, cValue, cValueRead, lSuccess, cValueToWrite, H_Width, H_Hight
nKey = HKEY_LOCAL_MACHINE
cValue = "Default DevMode"
cSubKey = "System\CurrentControlSet\Control\Print\Printers\"+Print_name
H_Width = Iif(Len(Dec10To16Hex(_Width))=3,"0"+Dec10To16Hex(_Width),Dec10To16Hex(_Width))
H_Width = Substr(H_Width,3,2)+Substr(H_Width,1,2) &&取得16进制的宽度值
H_Hight = Iif(Len(Dec10To16Hex(_Hight))=3,"0"+Dec10To16Hex(_Hight),Dec10To16Hex(_Hight))
H_Hight = Substr(H_Hight,3,2)+Substr(H_Hight,1,2) &&取得16进制的长度值
cValueRead = ReadREG_SZ(nKey, cSubKey, cValue)
IF (EMPTY(cValueRead)) THEN
Return(.F.) &&读注册表错误
ELSE
lenght = Len(cValueRead)
cRetValue = Space(0)
For i=1 To lenght
cRetValue = cRetValue+BinaryToChar(Substr(cValueRead,i,1))
EndFor
ENDIF
cRetValue = Stuff(cRetValue,93,4,"0001") &&设置默认纸张为自定义纸
cRetValue = Stuff(cRetValue,97,8,H_Hight+H_Width) &&设置纸张的长度为2000,宽度为1000
lenght = Len(cRetValue)/2
Binarychar = Space(0)
For k=1 To lenght
Binarychar = Binarychar+Chr(Hex16To10Dec(Substr(cRetValue,(k-1)*2+1,2)))
EndFor
cValueToWrite = Binarychar
lSuccess = WriteREG_SZ(nKey, cSubKey, cValue, cValueToWrite)
IF (lSuccess) THEN
Return(.T.) &&设置成功
ELSE
Return(.F.) &&设置失败
ENDIF
Return
FUNCTION ReadREG_SZ
* This function reads a REG_SZ value from the registry. If successful,
* it will return the value read. If not successful, it will return an empty string.
PARAMETERS nKey, cSubKey, cValue
* nKey The root key to open. It can be any of the constants defined below.
* #DEFINE HKEY_CLASSES_ROOT -2147483648
* #DEFINE HKEY_CURRENT_USER -2147483647
* #DEFINE HKEY_LOCAL_MACHINE -2147483646
* #DEFINE HKEY_USERS -2147483645
* cSubKey The SubKey to open.
* cValue The value that is going to be read.
* Constants that are needed for Registry functions
#DEFINE REG_BINARY 3
* WIN 32 API functions that are used
DECLARE Integer RegOpenKey IN Win32API ;
Integer nHKey, String @cSubKey, Integer @nResult
DECLARE Integer RegQueryValueEx IN Win32API ;
Integer nHKey, String lpszValueName, Integer dwReserved,;
Integer @lpdwType, String @lpbData, Integer @lpcbData
DECLARE Integer RegCloseKey IN Win32API Integer nHKey
* Local variables used
Local nErrCode && Error Code returned from Registry functions
Local nKeyHandle && Handle to Key that is opened in the Registry
Local lpdwValueType && Type of Value that we are looking for
Local lpbValue && The data stored in the value
Local lpcbValueSize && Size of the variable
Local lpdwReserved && Reserved Must be 0
* Initialize the variables
nKeyHandle = 0
lpdwReserved = 0
lpdValueType = REG_BINARY
lpbValue = ""
nErrCode = RegOpenKey(nKey, cSubKey, @nKeyHandle)
* If the error code isn't 0, then the key doesn't exist or can't be opened.
IF (nErrCode # 0) THEN
RETURN ""
ENDIF
lpcbValueSize = 1
* Get the size of the data in the value
nErrCode=RegQueryValueEx(nKeyHandle, cValue, lpdwReserved, @lpdwValueType, @lpbValue, @lpcbValueSize)
* Make the buffer big enough
lpbValue = SPACE(lpcbValueSize)
nErrCode=RegQueryValueEx(nKeyHandle, cValue, lpdwReserved, @lpdwValueType, @lpbValue, @lpcbValueSize)
=RegCloseKey(nKeyHandle)
IF (nErrCode # 0) THEN
RETURN ""
ENDIF
lpbValue = LEFT(lpbValue, lpcbValueSize - 1)
RETURN lpbValue
* End of Code
FUNCTION WriteREG_SZ
* This function writes a REG_SZ value to the registry. If successful,
* its will return .T.. If not successful, it will return .F..
PARAMETERS nKey, cSubKey, cValue, cValueToWrite
* nKey The root key to open. It can be any of the constants defined below
*#DEFINE HKEY_CLASSES_ROOT -2147483648
*#DEFINE HKEY_CURRENT_USER -2147483647
*#DEFINE HKEY_LOCAL_MACHINE -2147483646
*#DEFINE HKEY_USERS -2147483645
* cSubKey The SubKey to open.
* cValueToWrite The value being written to the registry.
* Constants that are needed for Registry functions
#DEFINE REG_BINARY 3
* WIN 32 API functions that are used
DECLARE Integer RegOpenKey IN Win32API ;
Integer nHKey, String @cSubKey, Integer @nResult
DECLARE Integer RegSetValueEx IN Win32API ;
Integer hKey, String lpszValueName, Integer dwReserved,;
Integer fdwType, String lpbData, Integer cbData
DECLARE Integer RegCloseKey IN Win32API Integer nHKey
* Local variables used
Local nErrCode && Error Code returned from Registry functions
Local nKeyHandle && Handle to Key that is opened in the Registry
Local lpdwValueType && Type of Value that we are looking for
Local lpbValue && The data stored in the value
Local lpcbValueSize && Size of the variable
Local lpdwReserved && Reserved Must be 0
* Initialize the variables
nKeyHandle = 0
lpdwReserved = 0
lpdwValueType = REG_BINARY
lpbValue = cValueToWrite
nErrCode = RegOpenKey(nKey, cSubKey, @nKeyHandle)
* If the error code isn't 0, then the key doesn't exist or can't be opened.
IF (nErrCode # 0) THEN
RETURN .F.
ENDIF
lpcbValueSize = LEN(lpbValue) && Store the length of the string
nErrCode=RegSetValueEx(nKeyHandle, cValue, lpdwReserved, lpdwValueType, lpbValue, lpcbValueSize)
=RegCloseKey(nKeyHandle)
IF (nErrCode # 0) THEN
RETURN .F.
ENDIF
RETURN .T.
* End of Code
FUNCTION BinaryToChar
PARAMETERS cchar
Local nint,nmod,char1,char2
ntemp = Int(Asc(cchar)/16)
Store "" To char1,char2
Do Case
Case ntemp = 10
char1 = "a"
Case ntemp = 11
char1 = "b"
Case ntemp = 12
char1 = "c"
Case ntemp = 13
char1 = "d"
Case ntemp = 14
char1 = "e"
Case ntemp = 15
char1="f"
OtherWise
char1 = Str(ntemp,1)
EndCase
nmod = Asc(cchar)-ntemp*16
Do Case
Case nmod = 10
char2 = "a"
Case nmod = 11
char2 = "b"
Case nmod = 12
char2 = "c"
Case nmod = 13
char2 = "d"
Case nmod = 14
char2 = "e"
Case nmod = 15
char2 = "f"
OtherWise
char2 = Str(nmod,1)
EndCase
Return char1+char2
FUNCTION Hex16To10Dec &&16到十进制
* Converts POSITIVE decimal integers to hex (Char).
* Input: NUMERIC
* Output: CHAR
PARAMETER InNum
OutStr = 0
For I = 1 To Len(InNum)
OutStr = FindHex(Right(InNum,1))*16^(i-1)+OutStr
InNum = Substr(InNum,1,Len(InNum)-1)
EndFor
RETURN(OutStr)
FUNCTION FindHex &&16进制到十进制的内部函数
* Lookup table for conversion of alpha hex chars.
* Input: NUMERIC
* Output: VAL
PARAMETERS InVal && Integer
Private All Like j*
Do Case
Case InVal = "a"
jOutStr = 10
Case InVal = "b"
jOutStr = 11
Case InVal = "c"
jOutStr = 12
Case InVal = "d"
jOutStr = 13
Case InVal = "e"
jOutStr = 14
Case InVal = "f"
jOutStr = 15
OtherWise
jOutStr = Val(InVal)
EndCase
Return(jOutStr)
FUNCTION Dec10To16Hex &&十到16进制
* Converts POSITIVE decimal integers to hex (Char).
* Input: NUMERIC
* Output: CHAR
PARAMETER InNum
OutStr = Space(0)
Do While InNum>0
OutStr = Find16Hex(Mod(InNum,16))+OutStr
InNum = Int(InNum/16)
Enddo
Return(OutStr)
FUNCTION Find16Hex &&十到16进制的内部函数
* Lookup table for conversion of alpha hex chars.
* Input: NUMERIC
*Output: CHAR
PARAMETERS InVal && Integer
Private All Like j*
Do Case
Case InVal = 10
jOutStr = "a"
Case InVal = 11
jOutStr = "b"
Case InVal = 12
jOutStr = "c"
Case InVal = 13
jOutStr = "d"
Case InVal = 14
jOutStr = "e"
Case InVal = 15
jOutStr = "f"
OtherWise
jOutStr = STR(InVal,1,0)
EndCase
Return(jOutStr)
[返回] |