http://www.foxsky.net/topic-79.aspx
Windows 中的打印,由于它通常是按页为单位和图形方式来打印的,所以让人觉得无法很好的控制它,很多人都很怀念 DOS 下控制打印机的便利,笔者最初在转入 Windows 编程时也被它困扰了很久。很多人都只知道用设置自定义纸张的方式来解决打印机走纸的问题,这种方式确实可以解决大部分需要,然后也存在一些难以解决的问题,毕竟每种打印机驱动都存在一个最大纸张尺寸的限制,而且有些驱动程序的输出时与你定义的纸张大小会有一些出入;另外,你可能在打印时根本就不知道会需要多长的页才能刚好容纳下你要打印的内容。
随着对 Windows API 的逐步了解,笔者摸索出了如何自如的控制打印机的方法。其实,在 Windows 下也同样可以像在 DOS 下一样按行来打印,而且你还可以做得更好,在利用 Windows 驱动来打印图形的同时,也控制它按字符方式来打印。
我将分几部分来介绍实现的方式,最后给出一个完整的类库,你只需将它拖入你的表单,或者用 NewObject 来实例化它,然后调用它提供方法来输出到打印机即可。今天先介绍类库中最重要的方法 RawDat2Prt,该方法将你的文本直接输出到打印机,可以认为是不通过打印驱动程序的加工,当然,打印机还是要安装的,呵呵!
请下载示例并运行它,输入一些测试字符来测试它,你应该可以看到你输入多少就打印了多少,绝不会有多余的走纸。
RawDat2Prt 方法接受两个参数。第一个是必须的,就是要打印的数据,它必须是一个字符串,如果你要打印的其它类型,请在传入前先用 Transform 转换它;第二个可选,代表打印任务名。
RawDat2Prt 方法先调用 OpenPrinter API 函数来打开打印机或打印服务器并得到其打印句柄,注意这个打印机或打印服务器的名称是在类的 PrinterName 中指定的,你可以在调用这个方法前先指定它,这在系统中同时安装了多个打印机或者网络上存在其它的共享打印机时会非常有用(关于网络打印机的安装方法,也是存在一些技巧的,很多人都知道自定义纸张的设置是需要管理员权限的,如何解决它?这将在后面涉及自定义纸张时再介绍),如果你没有指定它,则它会自动取系统定义的缺省打印机名。
在得到打印机句柄后,接着就调用 StartDocPrinter 来启动一个打印任务。这个 API 需要三个参数,其中第三个参数 pDocInfo 是我们所关心的,MSDN 中指出它是一个名为 DOC_INFO_1 的结构,它包含三个成员,其中的第一个成员是一个指向打印任务名的指针,第三个成员指定数据的打印方式,这里我们指定它按 RAW(原始数据)方式来打印。注意这个任务名你是可以设置的,一种方法就是前面说的调用方法的第二个可选参数,这样就可以从打印任务图标中清楚的区分当前打印的项目;如果你没有指定它,它使用类的 TaskName 属性中存放的值,你可以在设计或运行时改变它。
启动打印任务后,接着调用 StartPagePrinter 来开始一个 Windows 称为页的打印,不管你是否按页来打印,这个函数 MS 规定都是必须要调用的,它只需要打印句柄一个参数。
现在到关键的位置了,调用 WritePrinter 函数来实际输出。这个 API 需要四个参数:第一个是打印句柄,第二个是指向要打印数据的指针,第三个是要打印数据的长度,第四个用于返回实际输出到打印机的数据长度。
输出完成后调用 EndPagePrinter, EndDocPrinter 来结束打印页和打印任务,最后调用 ClosePrinter 关闭打印机并释放句柄。
注意这个类中使用了 HeapCreate 来申请一个内存堆用于临时存放一些用于 API 的数据,这个函数在 VISTA 下有时会导致异常,如果你要将这个类用于 VISTA,可以改为使用 GlobalAlloc 来申请堆,以前在测试另一个类 ToolTip 时出现过这种情况,这个类中使用它是否同样存在此问题还不清楚,我的机器太差,VISTA 安装后又删除了,无法测试。
还有一点就是表单中定义了一个名为 LineFeed 的属性,它存放的是打印机换行所需要的控制字符,缺省使用的是 LF,也就是 CHR(10),这个跟具体的打印机硬件设置有关,有些打印机可能需要 CR,也就是 CHR(13),有些则可能需要 LF+CR,这得你自己来确定。
如果你的打印机是与 EPSON 的 ESC 命令序列兼容的话,可以试试 '进纸 1/180 英寸' 命令,实际上是向打印机发送了 ESC+'J'+1 命令,以前在 DOS 下直接控制过过打印机的人应该很熟悉吧!你现在应该相信在 Windows 下也可以同样直接发送命令到打印机了。这种方法也同样可以用来解决自定义纸张长度不精确的问题,以后还会讲到。
最后要声明一点,这个类只适用于针打和用于 POS 的热敏打印机,如果是喷打激打则无法按行方式来打印,它们的物理结构注定了只能按页来打印。还有就是,我的代码都是在 vfp9 下编写的,如果你的版本低于这个版本,可能会有问题,要用就自己改吧。你可以随意修改它,也可以用于你自己的程序,如果要转载,别忘了标明出处就好。