虚拟打印机开发报告
第一章 打印机体系结构和虚拟打印机
-
-
Windows打印体系结构
windows xp 的打印体系结构是由打印假脱机和一系列打印驱动程序组成。应用程序通过设备无关的Win32打印和GDI(图形设备接口)函数,创建打印作业并将其发送到不同的设备,如激光打印机、向量绘图仪、光栅打印机以及传真机。打印驱动程序包括一个允许用户控制可选属性的用户接口。
整个的打印流程如下: 应用程序通过调用GDI创建打印作业,应用程序对GDI的调用传递到GDI图形引擎,该引擎将绘制指令脱机处理为EMF文件(增强型图元文件)或者直接连接到打印机驱动程序上,然后将打印图像传递到假脱机系统。此时应用程序完成了打印文档的任务,用户可以自由使用应用程序完成其他任务,而打印假脱机则保证文档被打印。
打印假脱机将页面布局信息和作业控制指令加入数据流,然后将数据流发送到打印处理器,打印处理器检查打印假脱机文件的格式,对于EMF文件,它将内容的每一页回放给GDI,GDI把GDI命令分解成DDI(图形设备接口)定义的绘制图元,并把绘制图元送到打印驱动程序,打印机驱动程序将绘制图元绘制成打印机语言格式的原始数据,例如PCL等,接着原始数据被送到打印假脱机系统。接着打印假脱机将数据发送到语言监视器,语言监视器把数据发送到端口监视器,端口监视器用OS文件系统API往硬件端口写数据,最后数据被发送到打印机。
假脱机和打印机组件式可以替换的,这使得硬件供应商可以很容易对新硬件进行支持。整个打印体系结构如图1-1所示:
-
图1-1 打印体系结构图
在进行打印机驱动程序开发之前,首先需要明确的是:打印机驱动程序仅仅是Windows打印流程中的一个中间环节。一个打印机驱动程序是对一种特定打印机提供了一个图形函数集的动态链接库(DLL),它所提供的接口函数可以将与设备无关的输出信息转变为与设备相关的输出信息(指令和数据流)。为了能够充分理解打印机驱动程序在一个实际打印作业中的地位与功能,从而很好的完成其设计与开发,对Windows整个打印流程有一个系统的分析是非常必要的。下面的流程图(图1-2)给出的便是从一个应用程序发出打印请求开始,到本地的打印提供者将假脱机文件写到磁盘,然后该假脱机文件将在合适的时间被解析成具体的指令和数据,最后由本地端口监视器将解析过的数据流通过它所控制的端口发送到与之相连的打印机并最终完成打印的完整过程。
图1-2 windows打印流程图
下面步骤详细描述整个打印流程:
1:应用程序创建一个设备上下文并在其上绘制一个对象,然后调用GDI
中相应的函数接口向与此设备上下文相对应的打印机发出一个打印请求。
2:GDI调用相应的打印机驱动程序来处理打印请求。
3:打印驱动程序创建一个打印作业,并调用GDI函数将处理结果返回GDI。
4:GDI调用打印假脱机系统。
5:打印假脱机调用打印请求处理器以便将打印作业发送到应用程序指定的打印机上。
6:打印请求处理器将打印作业发送到本地打印提供者(目标打印机在本地)
或者网络打印打印提供者(目标打印机在网络中)。
7:如果打印作业的类型是非直接打印,那么本地打印提供者就将打印作业以
原始假脱机文件的格式存放在磁盘上,并且将后来的打印片段不断的附加到假脱机文件中,直到应用程序调用了EndDoc函数终止一个打印作业为止。以上第一到第七步可能是重复多次来产生一个完整的假脱机文件。
8:本地打印提供者启动一个后台线程,打印主线程根据对打印假脱机子系统
资源的监视情况,选定一个最佳的时刻触发假脱机文件的解析过程。此时,打印主线程将调用StartDoc函数启动打印处理器中的一个线程来开始解析工作。
9:打印机处理器线程调用ReadPrinter调用来激活本地打印提供者以便从磁盘读取前面所生成的打印假脱机文件。
10:打印处理器同时还调用了WritePrinter函数来激活打印机作业的语言监视器以便将数据通过物理端口发送到相应的打印机上。
11:打印语言监视器调用打印端口监视器的功能来给打印机发送数据。
12:打印端口监视器监测物理端口,通过物理端口给打印机发送数据。
13:端口监视器调用内核端口驱动程序完成物理端口与打印机间的通信。
14:完成打印作业后解析线程终止。
-
-
虚拟打印机
虚拟打印机同真实打印机一样,安装完毕,打开“控制面板”中的“打印机
-
和传真”,会看到所安装的虚拟打印机,可以像使用一台打印机一样使用它们。鼠标双击将其打开,可以对其“打印首选项”和“属性”进行修改,从而设定是否共享、可使用时间、是否后台打印和优先级,以及纸张大小、版式安排等。它们同样能截获所有Windows程序的打印操作,或模拟打印效果,或完成某一特殊功能。有些软件自带虚拟打印机,有些则是专门的虚拟打印机,利用这些虚拟打印机,可以帮助我们完成很多特殊的任务。虚拟打印机的打印文件是以某种特定的格式保存在电脑上。
打印机是比较重要的输出设备,但有些时候,我们并不需要把东西真实地打印出来,而只是想通过打印预览功能来看看输出的效果。但如果计算机中没有安装打印机,那么打印预览也不能实现,就不能够观看到打印的效果,这给我们这些没有打印机的朋友们带来了很多不便。
但在实际应用中我们更经常的可能会遇到这样的问题:我们有打印机,但是我们所使用的软件只提供给我们“打印”的功能,我们在打印之前不能预览。如果我们不想浪费纸、墨,一次又一次试验调用效果,那么解决办法只有一个:安装一个虚拟打印机。
简单地说,虚拟打印机就是在你的机器中添加一个虚拟的打印机让你可以使用它来打印。我说的这个虚拟打印机可不同于你以往添加的像hp LaserJet 2000那些只能用于打印预览的“虚拟打印机”:你可以用它来打印文件,即使软件并不支持打印预览的功能!使用的方法就和你使用正常的打印机一样.
当然,你不可能用虚拟打印机把文件直接打印到纸上(否则打印机卖给谁),用虚拟打印机打印的结果是硬盘上的一个文件,你可以用专门的阅读器打开那个文件以查看打印的效果。
虚拟打印机实际上并不存在的,只是为了工作需要而安装的打印机。
虚拟打印机有三种定制方法:
1、驱动层(Driver)一种。修改Render plug-in,对渲染绘制过程进行特殊的处理。
2、打印假脱机(splooer)层两种。
(1)自定义打印处理器(PrintProcessor),一般是修改DDK中genprint的例子。将自定义的代码加入到PrintDocumentOnPrintProcessor中。
(2)在监视(Monitor)层。作者采用的方法,在后面介绍中读者会看到。
打印假脱机(splooer)层两种的两种实现方法驱动层一般都用微软统一驱动程序(UniDrv)。通过打印测试页可以了解安装的打印驱动的各个方面:驱动程序、端口等。也可以通过打印机的属性查看。作者就是在监视器层进行的。
第二章 准备工作
工欲善其事,必先利其器。同样在开发时也要选择合适的集成环境。作者在虚拟打印开发任务中先后用过多款开发工具。如何选择合适的集成环境,相信没有唯一的答案。每个人都有自己偏好和标准。在这里作者会介绍自己的开发工具。希望大家在以后的开发中能得到启发和帮助。下面,作者一一介绍下工具。
2.1 安装软件
NSIS(Nullsoft Scriptable Install System)是一个开源的 Windows 系统下安装程序制作程序。它提供了安装、卸载、系统设置、文件解压缩等功能。这如其名字所指出的那样,NSIS 是通过它的脚本语言来描述安装程序的行为和逻辑的。NSIS 的脚本语言和通常的编程语言有类似的结构和语法,但它是为安装程序这类应用所设计的。在虚拟打印开发中,它创建安装包,它里面调用了windows自带的功能安装INF,完成虚拟打印机的安装任务。NSIS软件下载地址如下: http://www.flighty.cn/html/book/20100207_18.html )
2.2 集成开发工具
以前很多人一直都是用 VS2008 + DDKWizard + WinDbg 来进行驱动程序的开发调试的,使用 DDKWizard 来搭配调试环境,在 VS2008 下还算方便,因为不需要自己去设置一些什么包含文件和源代码路径之类的就可以直接编译驱动程序源码,安装好 DDKWizard 后在 VS2008 中就会自动出现一个开发驱动程序的项目框架,就跟选择创建 WinForm 应用程序一样的,创建好 DDK 项目后也是可以直接在 VS2008 下编译的,所以在开发上方便,但是在调试上的话,就麻烦了,在 WinDbg 中有一大堆东西要设置,在虚拟机上也要设置一些命令。
但是在使用 DDKWizard + VS2008 进行驱动开发时,是不能够直接通过 VS2008 来调试的,而一般都是在 WinDbg 中设置好符号文件的路径以及源代码的路径,然后再驱动程序的源代码中嵌入一些汇编代码,当然这些汇编代码只是简单的用来实现一个中断,从而在 WinDbg 调试器中会生成一个断点。这样调试起来速度慢,也不好控制,所以有时候觉得在做开发的时候,在调试上花去的时间太多了,会不爽。
在本项目中,将要介绍的是一个开源项目 VirtualDDK,通过这个开源项目即可以很好的实现在 VS2010 以及 VS2008 下直接调试驱动程序,同时对于 VirtualDDK 的环境搭配也是很简单的,下面提供的链接直接上一些图片以及一些注解来说明,由于文章存在大量的截图,所以读者只要按着截图来做基本上都可以成功的。VS2010 + VMware + DDK (安装参见: http://techird.blog.163.com/blog/static/1215640362011112385241568/)
Dev-c++开发工具不是必须的,读者可自行选择。下载地址:http://pan.baidu.com/share/link?shareid=524654&uk=3241605080)
2.3 端口监视器
由于作者采用第三种方法来进行虚拟打印开发,因此作者要重点介绍下Monitor层方面的知识点。
2.3.1打印监视器介绍
在第一章曾经提到了打印监视器的概念。打印监视器主要负责把打印数据流
从打印假脱机传送到合适的端口驱动程序。打印监视器分为语言监视器和端口监
视器两种。其中语言监视器的主要作用是支持打印机的双向通讯,监视打印机的
状态,获取并处理一些事件;端口监视器的主要作用是管理配置打印端口并控制
打印机和物理端口之间的通讯。在打印机驱动系统体系中,这两个监视器都是可
替换的,即开发人员可以根据需要编写自己的语言和端口监视器。比如编写语言
监视器提供对打印机双向通讯的支持等。
在基于NT的操作系统下,用户所看到的打印机视图其实是一个打印队列,一个或者多个物理打印机设备可以与该队列相连。端口就是打印队列和一个打印
设备之间的物理连接。打印机驱动程序中端口监视器模块负责和端口相关的操作。
每一个端口监视器支持一种或者多种端口类型的实例。当需要给打印机添加新类
型的端口实例时,就需要提供新的端口监视器。打印假脱机通过调用AddPrinier
函数来把一个端口指派到端口监视器中。在打印过程中,各种类型的打印设备都
被列在打印队列中,打印假脱机把打印作业发送到第一个可用的端口。如果端口
监视器指示这个端口正忙或者发生错误,打印假脱机把打印作业重新提交到打印
队列,并指定该端口监视器支持的另外一个端口发送打印数据。
2.3.2端口监视器构成
一个端口监视器由一些用户模式下的动态链接库组成。它的职责有两方面,
一是负责管理和配置服务器端的和打印机硬件连接的打印端口;二是为运行在用
户模式下的打印假脱机和运行在内核模式下的可以直接访问FO硬件端口的端口
驱动之间建立通信链接。
对Windows2000和以后的操作系统而言,每一个端口监视器都被分为两个
动态链接库:
l:Port Monitor Ul DLL
端口监视器的接口动态链接库包括功能性的用户接口。这个动态链接库存放
在客户系统的System32文件夹中并且在打印机的客户端系统上执行。
2:PortMonitorServerDLL
端口监视器的服务动态链接库包括端口的通信功能并且在打印机的服务器端系统上执行。
以上两个动态链接库之间通过打印假脱机的XcvData函数通信。需要说明的是:虽然端口监视器被分成了两个动态链接库,但是在具体编写端口监视器的过程中,却可以有两种方法:一是为新编写的端口服务动态链接库编写全新的接口动态链接库,而这样的接口动态链接库提供给用户的管理界面只能对新添加的端口类型进行管理;第二种方法是使用原来的接口动态链接库,这种情况下,新添加的端口类型会直接添加到原来的接口动态链接库的打印队列中,只需要为新添加的端口提供一个可以修改配置信息的弹出对话框即可。这两种方法在功能的实现方面没有区别。第一种方法可能复杂一些,但是界面简单整洁。下面两幅图描述了打印监视器在整个打印流程中所处的位置。其中图2-1左侧的是有语言监视器的情况,图右侧的是没有语言监视器的情况。
图2-1 打印处理机到打印机的数据流程图
2.3.3 数据通信模块
当一个打印监视器进行了正确的初始化并且正确添加了打印端口之后,监视器就可以开始与打印机物理端口的数据通信工作。下面介绍数据通信模块的核心工作。
每一个打印作业都会首先被打印假脱机划归到某个语言或者端口监视器的
StartDocPort和EndDocPort函数中。这两个函数也就成了完成一个打印作业的开始和结束函数。这里的开始和结束,仅仅是针对一个打印作业的,并不是指整个打印流程。所以在StartDocPort函数之前和EndDocP0rt之后,假脱机还会调用其他的函数来完成整个打印流程。下面图2-2展示了完成一个打印作业的基本流程.如图所示,要完成一个打印作业需要经过以下几个步骤:
1:打开端口
打印假脱机首先要调用端口监视器的OpenP0rt函数,该函数的主要功能是打
开一个端口并返回一个标识该端口的句柄。在假脱机的后续调用中,该句柄将作为其他处理函数的输入参数使用。针对某些端口,OpenP0rt函数还可以在打印假
图2-2 端口监视器数据通信流程图
脱机读端口或写端口之前完成一些初始化的工作,比如对那些支持可修改超时值的端口而言,每次端口超时值的设定就由OpenPort函数完成。当一个端口被正确打开之后,该端口就可以被写入信息。打印假脱机在同一时间只允许一条可用的通信链路和一个打印端口连接。因此当假脱机在端口监视器中调用了OpenPort函数之后,在关闭这个端口之前,它不会再次尝试去打开这个端口。而在端口被关闭之前,假脱机可以发送多个打印作业到这个端口。
2:开始打印作业
一个端口被打开之后,打印假脱机接着调用startDocPort函数来开始一个打
印作业。StartDocPort函数主要通过调用WindowsAPI提供的CreateFile函数来建立一条和内核端口驱动之间的数据链接。
3:读/写端口
当数据链接建立之后,打印假脱机可以根据需要进行双向操作,一种是向打
印机写数据,这种情况下打印假脱机先调用端口监视器的WritePort函数然后会多次调用WindowsAPI提供的WriteFlle函数来向内核端口驱动发送数据;另一种
是从打印机读数据,此时打印假脱机先调用端口监视器的ReadPort函数然后再调用WindowsAPI提供的ReadFile函数从内核端口驱动那里读取硬件信息。
4:数据传送
不管是向打印机写数据还是从打印机读数据,打印假脱机后面都会调用
GetPrinterDataFromPort函数,该函数的主要功能是通过调用WindowsAPI提供的DeviceIoControl函数来最终实现打印驱动程序和内核端口驱动之间的数据传输。
5:结束打印作业
以上操作完成之后,打印假脱机调用EndDocPort函数来结束一个打印作业。
该函数的主要作用是释放CreateFile函数建立的数据链接和StartDocPort函数中申请的所有资源。EndDocPort函数和StartDocPort函数总是成对出现的。
6:循环检测
当按照上述过程完成了一个打印作业之后,打印假脱机查看和这个端口相连
的打印队列中还有没有其他的文件需要打印。如果有,则程序返回到StartDocPort函数重复执行上述过程,如果没有,打印假脱机就关闭这个端口。
7:关闭端口
端口监视器的ClosePort函数用来关闭端口。该函数的主要作用是释放在
OpenPort函数中申请的所有系统资源。ClosePort函数和OpenPort函数也总是成对出现。当与一个端口相关的打印队列中所有的打印作业都完成之后,或者由于其他情况需要退出该端口的时候,ClosePort函数就被打印假脱机调用
第三章 开发
3.1 项目源代码
项目源代码可自行选择,网上有很多代码可供下载,比如大家常用的就是在DDK(WIN2000)里面有一个例子genprint,它是一个打印机驱动,只要我们在这个例子的代码中进行修改就可以实现自己想要的功能。但是本人采用的是ImagePrinter的源代码。下载虚拟打印源代码Imageprinter, 下载地址: http://code-industry.net/downloads.php
3.2 配置开发环境
3.2.1 若选用VS2010开发,参数配置较繁琐,可参见:http://jingyan.baidu.com/article/b2c186c8eae112c46ef6ffb8.html
3.2.2 若选用Dev-c++配置简单,在此不复述。
3.3 编译调试
开发并编译源代码,生成imgport.dll文件。
开发时候会出现各种bug,希望大家有效地利用MSDN和论坛来解决各种问题。
第四章 安装虚拟打印机
4.1 安装厂商和打印机
4.1.1 安装前系统配置
安装之前,电脑里面没有 端口 Imageprinter Port 和 厂商code-industry 及其打印机ImagePrinter的驱动程序,如下图4-1到图4-2:
图4-1
图4-2
图4-3
3.1.2 安装
新建目录,将项目编译过的项目源代码及其DLL文件集中一起,imgport.dll文件放入 ..\imgport\install\i386文件夹中。进入目录:..\imgport\install
右击鼠标右键--》编译NSIS脚本文件(注意: 编译之前务必将项目编译产生的imgport.dll拷贝到..\imgport\install\i386文件夹下,否则无法进行后续任务,因为NSIS软件打包生成安装包时会利用所有的DLL、INF和GPD等文件),生成可执行安装文件setup.exe,如图4-4:
图4-4
执行setup.exe文件,安装端口Imageprinter Port、厂商code-Industry和打印机ImagePrinter。安装后系统配置,如下:4-5:
在“添加或删除程序“中可以看到:
图4-5
端口、厂商及打印机驱动程序均已经安装上,如下图4-6、图4-7:
图4-6
图4-7
去“控制面板”-> “打印机和传真”,可以看到刚刚安装的打印机 ImagePrinter。
如图4-8:
图4-8
可能会有人会问:没有安装INF文件,怎么就把虚拟打印机安装好? 细心的读者应该能发现,在虚拟打印开发中,NSIS软件创建安装包,它调用的各种文件中包含了windows自带的功能安装INF,从而完成虚拟打印机的安装任务。
4.2 测试打印机
4.2.1 新建word文档,输入要打印的文字,然后选择”打印”-》“ImagePrinter”打印机-》确定,如图4-9:
4.2.1 虚拟打印成功,如图4-10
图4-10
至此虚拟打印成功。
第五章 总结
本文对Windows (XP)平台下打印机设备驱动程序的开发进行简要的论述。以在Windows环境下完成一次打印任务的完整流程为线索,介绍了其中的各个功能模块,并深入分析了一个端口监视器实例的设计和实现,最后根据源代码ImagePrinter应用实际设计完成了一个打印机驱程程序。一般来说,编写一个完整的打印机驱动程序所要做的工作如下:
第一步:熟悉Windows操作的打印流程,明确驱动程序在这个流程中所处的位置。
第二步:阅读WindowsDDK和其他相关文档,明确驱动程序各组件的功能划分及相互间的关系。
第三步:明确驱动程序各组件的功能模块和设计方法,确定各模块间的接口函数。
第四步:分别编写所需的功能模块和各个接口函数。
第五步:编译、调试驱动程序,生成可用的动态链接库。
第六步:为所生成的驱动程序以及其他相关文件生产一个安装文件(.inf)。或者选用其他工具安装,作者就是采用NSIS来安装的。
第七步:安装,测试、修改、再重新编译链接,重复这几个步骤直到达到要求为止。
目前,打印机驱动程序的开发主要还是由各打印机生产厂家完成,出于商业保护的考虑,他们都隐藏了自己的技术细节。微软虽然提供了一些打印机驱动方面的开发文档和源程序,但是都是一些最基本功能的实现,其它的参考文献也大都是从Windows DDK翻译而来,参差不齐。
由于本人的工作是从零开始的,以前没有window底层的开发经验,同时缺少很多资料,因而实现的打印机驱动程序只具有基本的虚拟打印功能,还需要继续的补充和改进。
由于本人水平有限,对一些问题的理解还不够深入,如有错误与不妥之处,敬请大家批评指正。
第六章 参考文献
[1]张帆、史彩程, Windows驱动开发技术详解,电子工业出版社,2008-7-1
[2]张佩、马勇、董鉴源, 竹林蹊径 深入浅出Windows驱动开发,电子工业出版社,2011-2
[3]MSDN 参考资料, http://msdn.microsoft.com/en-us/library/windows
/hardware/ff563806(v=vs.85).aspx
[4]孙守阁、徐勇,Windows设备驱动程序技术内幕,清华大学出版社,2000
[5]何斌,黄进,陈其昌,Windows环境下打印机驱动程序设计,电子计算机与
外部设备,2000年3月
[6]田玉敏,燕红锁,Windows2000下打印机驱动程序开发,计算机工程,2002
年3月
[7]尤晋元,史美林,陈向群等,Windows操作系统原理,机械工业出版社,2001
[8]谭文,杨潇等,寒江独钓Windows内核安全编程,电子工业出版社,2009-6
[9]Johnson M.Hart,Windows系统编程(第三版),机械工业出版社,2010-10
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/26435490/viewspace-1629211/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/26435490/viewspace-1629211/