Windows打印体系结构之Print Spooler概念与架构

    本文地址:http://tongxinmao.com/Article/Detail/id/318

    1、Windows打印体系结构

    首先附上查找Windows打印相关内容的链接,这个分类下包含了Windows打印的方方面面:
    https://msdn.microsoft.com/en-us/library/windows/hardware/ff551767(v=vs.85).aspx

    如上图所示,Windows的打印体系结构是由一个打印机假脱机程序(Spooler)和一系列的打印驱动组成。其中带方框的部分都是可以编写安装的。其中的打印驱动程序是指微软的统一驱动,也就是系统默认的驱动,而迷你驱动就是在此基础上进行个性化定制。
    迷你驱动包括GPD文件和资源DLL,GPD文件就是一个脚本,定义了“打印机首选项”中将出现那些设置,有哪些选项可选,资源DLL就是一个仅仅包含Resource的DLL,提供给GPD使用其中的资源。
    而在打印假脱机中:
    打印处理器(也就是打印机驱动程序)负责SPL文件操作 EMF/RAW文件操作等;
    Render-Plugin则可以对渲染绘制过程进行特殊的处理;
    UI-Plugin 就是在“打印机首选项”中定义一些特殊的设置页面。
    语言监视器以及端口监视器和网络打印提供者我们在打印假脱机中进行介绍。
    1.1、打印机假脱机程序(Spooler)

    1.1.1、打印假脱机的概念

    打印假脱机程序在Windows系统中主要表现就是Windows后台打印程序Print Spooler (Spoolsv.exe)。那么什么是打印假脱机程序(Spooler)呢?顾名思义,我们只需要明白“假脱机”的概念,基本上就明白了什么是打印假脱机程序(Spooler)。
    在几乎所有的操作系统中,外部设备与系统交互都存在两种模式:联机模式和脱机模式。
    所谓联机模式是指系统完全按照外部设备的执行顺序执行设备操作,再者外部设备往往都是抢占式设备,当一个进程需要使用外部设备时往往要等其它进程使用完毕才可以进行使用。例如:联机模式下当进程有输入/输出需求时就只能在输入/输出设备空闲时才能使用设备,如果当前该设备正在被占用,则该进程就只能等待调用者调用结束才能获取设备使用权,所以联机模式最大的缺点就是进程之间不能"并发"地使用设备。
    而脱机模式正好与联机模式相反,脱机模式中提出一个概念就是缓冲区的概念,例如:在脱机模式下,当进程需要输出数据到外部设备时,首先将数据输出到缓冲区内,然后外部设备再从缓冲区获取需要输出的数据。这样做很好遥免了联机模式下进程间不能并发的问题。但是脱机模式很大的一个弊端也凸显出来,那就是当有数据输出到缓冲区内时,设备并不是立马对数据进行输出处理,而是等待缓冲区满或者进程需要输出设备输出的时候,设备才能启动并将数据输出至设备进行处理。这样做会降低设备处理能力,并且频繁的启动设备很容易造成设备损坏。
    假脱机系统,顾名思义不是真的脱机,它是假的脱机模式。它的真实含义是在联机的模式下得到脱机效果。假脱机的最终目标是沿用联机和脱机的所有优点同时避免联机和脱机的所有缺点。所以打印机是一个相对来说比较特殊的硬件。不但不同于显卡这种机箱内的硬件,也不同于鼠标键盘等外设。鼠标键盘显示器等都是即时反应的,比如移动一下鼠标操作系统都会立即处理并反应在显示器上。但打印机不是,打印一个文档快则几秒,慢则有数十分钟。操作系统不可能一直等到打印机处理结束再进行返回。所以,操作系统提供了一个叫Spooler的服务来对各种打印机的任务进行管理。Spooler的服务所提供的这个服务有人也将其称之为打印池。不管称呼如何,其技术在操作系统领域中给它的定义是:Spooling技术是一种在多道程序环境下模拟脱机方式控制I/O设备进行输入输出的技术。Spooling技术的最终目的是通过系统软件的方式把只能让一个用户独占的设备变成多用户多进程共享的设备,这种做法不仅可实现进程可以随时调用设备也可以提高设备的利用率。因为只有当缓冲区没有任务的时候设备才会进入休眠状态,同时还降低了对设备进行休眠和唤醒的操作频率,延长了设备寿命。
    因此在这里我们总结一下打印假脱机程序(Spooler)的作用。
    打印假脱机程序由一系列的微软提供的和可选的渲染组件组成,他们的作用包括:
    1、检测打印任务是在本地打印还是网络打印。
    2、接受GDI和打印驱动为特定类型的打印机所提供的数据流。
    3、缓冲绘制数据到文件中。
    4、从逻辑打印队列中选出第一个有效的物理打印机。
    5、 将缓冲的数据流(如EMF)转换成能被打印机硬件所识别的格式(如PCL)。
    6、发送打印数据流到打印机硬件中。
    7、为假脱机组件和打印机的相关信息维护一个基于注册表的数据库,这个数据库的我们在后面会讲到。
     
    1.1.2、打印假脱机的架构

    说道打印假脱机(Spooler)的架构,我们就不得不提一下Windows的通用图形接口,否则没法继续介绍打印假脱机(Spooler)的架构。
    对于现在的主流Windows操作系统,存在着两个统一的图形接口:GDI和XPS。GDI是从Windows 95开始就一直支持的通用图形接口,包括最新的Windows 10。而XPS是从Windows Vista开始,微软开发的另一个通用图形接口,其目的是为了替换已经老旧的GDI。但肩负着代替GDI使命的XPS过了多年依然没达成这一任务,也就造成了今天Windows系统里面GDI和XPS共存并相互兼容的状态。我们会在后面详细讲解GDI的架构以及XPS的架构。在这里点出主要是便于帮助我们理解打印假脱机(Spooler)的架构。
    因为GDI和XPS共存且相互兼容,所以在Windwos的现有模式下,Windows打印假脱机(Spooler)的架构有两种:基于GDI的打印假脱机(Spooler)的架构和基于XPS的打印假脱机(Spooler)的架构。
    我们首先来看基于GDI的打印假脱机(Spooler)的架构。
    GDI的打印假脱机(Spooler)主要组成结构如下图所示:【https://msdn.microsoft.com/en-us/library/windows/hardware/ff551781(v=vs.85).aspx】

    应用通过调用GDI函数来创建打印任务,通过调用Winspool.drv提供的API接口,将打印内容路由到PrintProvider中。PrintProvider负责管理本地打印和远程打印,同时要管理打印任务堆里的启动、停止和枚举打印队列。
    下面分别介绍这些组件:
    应用
    打印应用程序通过调用GDI函数创建一个打印作业。
    GDI
    GDI是Graphics Device Interface的缩写,含义是图形驱动程序接口,是Microsoft设计的一套API,是图形用户界面GUI系统的一个重要组成部分:为 Win32应用程序提供与设备无关的图形编程界面,包括:视频显示、打印机、画图仪和传真机。图形设备接口(GDI)包括用户模式和内核模式组件。微软把工作于用户模式的 GDI 称为 Win32 GDIAPI,工作于内核模式的GDI称为GDI 图形引擎。
    在 Windows 操作系统下,绝大多数具备GUI(Graphics User Interface)的应用程序都离不开GDI。利用GDI所提供的众多函数可以方便地在屏幕、打印机及其它输出设备上输出图形、文本等。GDI的出现使程序员无需关心硬件设备及设备驱动就可以将应用程序的输出转化为硬件设备上的输出,实现了程序开发者与硬件设备的隔离,大大方便了开发工作。
    WINSPOOL.DRV
    Windows的打印客户端 (winspool.drv)把打印的APl暴露给用户应用程序, 用户应用程序用打印API来查询打印机、打印任务、改变打印机设置、查询打印机设置、加载打印机驱动程序用户界面DLL来显示打印机具体设置属性页和做一些其他的事情。Windows的打印客户端 (winspool.drv)帮助GDI决定打印任务应该如何处理。对于一般的打印任务,GDI生成EMF文件并把它送到打印池客户,然后打印客户用远程进程调用把打印任务发送给打印系统服务进程spoolsv.exe。
    SPOOLSV.EXE
    spoolsv.exe是后台打印程序的API服务器。spoolsv.exe打印服务向打印客户DLL导出RPC (远程过程调用)接口,用户应用程序可以用Windows的打印客户端 (winspool.drv)管理打印机、打印机驱动程序和打印任务。spoolsv.exe打印服务本身是一个小的EXE文件(Spoolsv.exe)。它通过打印路由把大多数调用送到打印机提供者。
    Spoolsv.exe打印服务随着操作系统启动而启动。该模块输出一个RPC接口到后台处理程序的Win32 API中的服务器端。Spoolsv.exe中的客户包括WINSPOOL.DRV(本地)和Win32spl.dll(远程)。该模块实现一些API函数,但大多数功能的调用由所述路由(SPOOLSS.DLL)的装置传递到打印提供着。
    打印路由
    打印路由SPOOLSS.DLL,顾名思义就是确定打印提供者在哪儿以及这么到它哪儿!我们使用的打印机有可能是本地打印机,也可能是在网络中的网络打印机。Spoolsv.exe打印系统服务使用打印路由(spoolss.dlI)把打印任务提交给打印机提供者,打印机提供者知道该把打印任务送到哪里。
    打印路由(spoolss. dlI)的任务非常简单:就是找出正确的打印机提供者,然后把信息发送过去。它借助系统注册表中打印机相关的设置,通过打印任务所带的打印机名或者打印机句柄信息完成这一任务。
    打印提供者(Print Provider)
    打印提供者负责把打印任务分配给本地或远程计算机。它也管理打印任务队列操作,如启动、停止和任务枚举。与打印服务进程和打印路由不同,系统中可以有很多打印提供者。
    Windows操作系统本身内置有几个打印提供者:
    本地打印机提供者(localspl.dll)。处理本地打印任务或远程客户发送到本地机的打印任务。每项打印任务最终都由本地打印机提供者处理,它把打印任务送到打印处理器(后面解释)。
    网络打印机提供者(win32spl.dll)。把打印任务发送到远程的网络打印服务器。
    HTTP打印机提供者(inetpp.dll)。把打印任务通过HTTP发送到URL地址进行打印。
    其中,硬件打印机的厂商比如HP等,能够用Windows NT的DDK来编写它们自己的打印机提供者。
    打印处理器【打印驱动程序】
    在上述打印提供者里面出现了一个新名称:打印处理器!打印处理器是专门负责把打印任务的打印文件转换为打印机可以识别的原数据格式。在这里我们又需要引申出另外的一个话题:打印机控制语言或者说打印机指令集。就如同计算机的硬盘一样,应用程序需要对硬盘读写一个数据,需要将其请求发送给操作系统,操作系统会将其转发给硬盘的SCSI控制器,硬盘的SCSI控制器将应用程序锁请求的数据转换为硬盘能够理解的SCSI语言或者SCSI指令,然后发送到硬盘执行。
    同理,在这里这个打印处理器就类似于硬盘的SCSI控制器。所不同的是,硬盘的SCSI控制器是驱动程序加硬件芯片,也就是软硬一体,而打印机的这个打印处理器,则是通过Windows的驱动程序来完成的,是一个纯软件的操作,因此在纯粹的Windows的EMF格式下的光栅打印,会对系统的资源占用较高。
    在打印机市场,使用最广的打印机指令有:ESC、PCL、PostScdpt指令。其中PCL、PostScdpt指令也被称为页面描述语言(PDL,PageDescriptional Language),ESC指令也被称之为嵌入式语言。这只是打印机语言其中的一种分类方法。这些都是打印机原始指令,大部分原始指令都来自于标准打印数据"EMF"的转换,但是PCL6标准指令和PostScript指令不能通过转换标准打印数据获取,而是直接由打印驱动程序生成,故此PCL6指令打印和PostScript指令无需转换指令而是直接将指令发送到打印设备,所以这两种打印指令被为原始打印也叫直接打印。其打印的数据不经过驱动程序的打印处理器进行转换,而是直接发送到打印机,由打印机设备自己解析这些数据并打印。显而易见这来两种打印机语言打印效率就比较高。针对这些打印机语言的介绍我们后面再说。
    打印监视器
    Windows支持两种类型的打印监视器:语言监视器和端口监视器。在上述Windows的架构体系中,我们可以看到,打印假脱机程序还包含有一些打印的监视器。打印监视器负责把原始打印数据从打印服务引到正确的端口驱动程序上。
    语言监视器,这里的语言是指打印机固件能理解的各种打印机任务语言,如PCL。语言监视器的主要目的是通过双向通信电缆,在打印服务进程和打印机之间提供全双工的通信信道。计算机到打印机的数据通道主要用于把打印数据送到打印机,打印机到计算机的返回通道用于提供反馈信息。语言监视器的第二个目的是在打印机数据流中插入控制命令。
    而端口监视器则是在打印服务进程和内核模式端口驱动程序之间提供通信途径,该驱动程序直接存取打印机链接的硬件的I/O端口。端口监视器是不能直接对硬件操作。它使用常用的文件 API同内核中的驱动程序通信。端口监视器也负责管理逻辑打印机端口,例如本地计算机的所有COM 和LPT端口。
    上述大概说明了基于GDI的打印假脱机(Spooler)主要组成结构。而基于XPS的打印假脱机(Spooler)又是如何的一个组成结构呢?
    其实对于XPS的打印假脱机来说,和GDI的唯一区别就是GDI图形接口换成了XPS图形接口。在Windows中,程序员所开发的图形应用程序,要么采用GDI的图形接口开进行开发,要么采用WPF开发框架,WPF(Windows Presentation Foundation)是 Windows  Vista中引入的新的用户界面开发框架, 可提供更加绚丽的用户交互界面.,WPF应用程序仅支持基于 XPS 的打印接口。也就是说,其实是Windows 7以及之后的版本中,大部分新开发的应用程序都是基于WPF开发的,其调用的也是XPS接口。因此对于GDI或XPS来说,是GDI的应用程序就调用GDI的打印接口,是XPS的应用程序就调用XPS的打印接口。同时二者又是可以相互兼容和相互转换的。


    上一篇:Windows打印机驱动开发
    下一篇:打印相关Api函数