本文基于Citrix的打印体系以及相关解决方案!
一、Windows打印体系以及Citrix嵌入的组件
Windows的打印体系主要由两大部分组成:
1、打印假脱机系统(Print Spooler)
2、打印机驱动程序
这两大部分是Window是打印系统的核心。同时,基于Windows打印体系所支持的打印图形接口以及打印机驱动程序的不同,Windows打印体系架构由有多个版本,分别为V1、V2和V3,其中V3是现目前最为常用的版本,而且Citrix UPD目前也是基于V3的版本构建,因此Citrix UPD的版本目前也止步于V3。但是在Windows 8以及Windows 10中,Windows系统推出了最新的Windows打印体系V4,由于V4比较新,需要各家打印机厂商首先熟悉了V4的架构以及相对于的SDK之后,才会根据该架构进行开发,因此V4还需要一段时间来进行普及。因而现目前Citrix的UPD技术还未推出基于Windows体系架构的V4版本。而V4不在本文讨论范围之内。
打印假脱机系统(Print Spooler)
首先打印假脱机系统由以下部分组成:
1、Windows打印客户端(Winspool.drv):把打印APl暴露给用户应用程序, 用户应用程序用打印API来查询打印机、打印任务、改变打印机设置、查询打印机设置、加载打印机驱动程序用户界面来显示打印机具体设置属性页和做一些其他的事情。
2、Windows打印服务器端(Print Spooler):服务器端保证文档被打印。该服务器端由几个组件组成:
1)Windows打印系统服务进程(Spoolsrv.exe):打印系统服务是系统中是具有特殊权限和责任的进程。打印服务在操作系统启动时启动。这就是为什么有时系统重启时,如果计算机有未完成打印作业,打印机会自动打印的原因。打印服务向打印客户端导出RPC (远程过程调用)接口, 用户应用程序可以用这个接口管理打印机、打印机驱动程序和打印任务。打印池服务本身是一个小的EXE文件。同时,打印客户端通过通过 RPC 方式将打印任务提交给打印系统服务进程。
2)打印路由器(spoolss. dll):我们在使用打印机的时候,可能是本地打印机,也可能是网打印机,或在Internet打印机,或在这个世界中的任何一个地方,但它只有一个URL地址。打印系统服务使用打印路由器(spoolss. dll)把打印任务提交给打印机提供者,打印机提供者知道该把打印任务送到哪里。路由器的任务非常简单:就是找出正确的打印机提供者,然后把信息发送过去。它借助系统注册表中打印机相关的设置,通过打印任务所带的打印机名或者打印机句柄信息完成这一任务。
3)打印提供者(Provider):打印提供者负责把打印任务分配给本地或远程计算机。它也管理打印任务队列操作,如启动、停止和任务枚举。打印提供者通常由打印机产商提供,Windows 系统自带几个Provider, 包括负责本地打印的 localspl.dll,,负责Windows 网络打印的 win32spl.dll,,和负责 http 打印的inetpp.dll。Citrix UPD技术提供了UPD Provider。
4)打印处理器:打印处理器提供最重要的一个功能:渲染。即将原本windows系统调用的命令将打印任务成像的指令转换为打印机能够识别的打印指令(Windows通过图形引擎成像,打印机则根据打印机种类不同,成像的方式也不同,比如喷墨打印机就是经过将墨水喷洒在纸张上成像)。在Windows当中,存在默认的两个打印处理器:
Window打印处理器支持几种打印池格式,包括EMF、XPS、RAW和TEXT。
Macintosh打印处理器支持自PostScript打印池格式。
EMF打印格式是基于Win32API开发的调用GDI图形接口程序的Windows应用程序的常用打印格式。EMF打印文件通常比打印机能接收的原始数据小很多。生成EMF打印池主要是由GDI完成的,打印机驱动程序介入得很少。如果通过网络打印的话,打印EMF数据在网络上将发送更少的数据。它还能让客户机有更多的时间完成正常的工作,而服务器则忙于把EMF转换成原始打印数据。在生成EMF的过程中,GDI会查询远程计算机可用的字体。如果远程计算机没有某种字体,那么这些字体就会被嵌在.spl打印缓存文件中并被传送到远程计算机并安装。
XPS打印格式是基于Windwos的新一代图形接口WPF开发的应用程序的常用打印格式。该打印格式并不会去调用GDI生产EMF文件,而是调用WPF的显示系统生产XPS文件。
5):打印监视器:打印监视器负责把原始打印数据从打印处理器中引到正确端口驱动程序。打印监视器有两类:语言监视器和端口监视器。
a) 语言监视器:它是用户模式下的 dll,提供两个功能:1. 为打印 spooler 和双向打印机(提供软件访问状态信息的打印机)提供全双工的沟通通道;2. 用
于添加打印控制信息。 如果所用的打印机是单向,即不需要和打印机通讯,那么就不需要语言监视器。
b) 端口监视器:它也由用户模式的 dll 组成,他们负责为用户模式的 print spooler和内核模式的端口驱动(用于访问硬件 IO 端口)提供沟通通道。它也负责管理和配置服务器的打印机端口。
打印机驱动程序
一般来说,打印机驱动程序包括以下组件:
1、打印机图形组件:它像显示驱动程序那样接收DDI绘图命令或WPF显示命令,把它们转换成打印机语言,并把数据送到打印系统中。
2、打印机接口组件:它为打印机提供可配置参数用户界面,并为打印系统提供打印机安装、配置和事件报告。
3、可选的打印机处理器:它帮助打印进程将打印任务中的打印数据转为打印机能够识别的打印数据。
4、可选的语言监视器:它提供打印池和用户双之间的方向反馈。
5、可选的端口监视器,它把为打印机准备好的数据发送到硬件端口驱动程序中或特定通道中,Citrix打印环境下,Citrix自研的端口监视器会将数据发送到ICA/HDX协议的打印虚拟通道中。
一般而言的打印机驱动程序不仅仅包括了打印驱动部分,还包括了假脱机体系中的打印处理器甚至是端口监视器,这些是由一堆DLL文件、GPD文件、Inf文件等等所组成的驱动程序。而严格意义上来说,打印驱动仅仅包括图形渲染和用户界面两个部分。
Windwos打印过程
诚如每位Citrix系统工程师入门Citrix的时候一样,我们了解一个产品,明白了这个产品的组件以及组件的作用之后,我们接下来就需要了解这些组件是如何相互协同工作的,整体的数据流程是怎样的?
打印过程包含三部分:客户端过程、Spooler 过程和打印过程。每个过程将对打印作业进行一些操作,再把作业转发到下一个过程。
客户端过程
用户从应用程序(如 Office Word)中选择打印,这个时候应用程序需要向打印客户端查询打印机信息、打印任务、打印设置信息等。这个时候打印机驱动程序用户接口组件就会创建一个设备上下文,作用是让应用程序了解正在使用的打印机,以进行对应的处理。应答的信息包括用纸信息、 印刷能力等等。这些信息会提供给应用程序,用户在这时候就会看见打印设置的页面,这些信息是由打印机驱动所支持的用纸和进纸方式被列在了纸张大小和来源里。换成其它驱动后这个列表也会变化。打印设置协商好后,这里就得看我们的应用程序是什么应用程序了:
1)Win32API开发的GDI的程序:应用程序用GDI命令向系统发送打印任务。几个特殊GDI命令表示打印任务的开始和结束,及将文档分成多页。GDI接收应用程序传来的GDI绘制命令,将它写到EMF打印池文件并转发给Print Spooler。EMF文件不是打印机特定的。应用程序将为一个打印输出生成相同的EMF文件,无论打印到什么类型的打印机。这就是为什么基于GDI的Windows应用程序可以打印到任何打印机。 EMF文件是应用程序和打印机之间的公共中间人。为了理解这一点,让我们解释一下打印下面这行文本:
Citrix的技术的确是最棒的。
此行文本的EMF文件将包含打印输出的说明,包括颜色,字体,字符和间距之类的内容。 EMF文档是尺寸非常小的矢量化文档。一旦生成EMF文件,应用程序就不再参与打印过程。
2)WPF框架开发的程序:Windows Vista 开始, Microsoft 引入了 WPF 开发框架,并配套有新的打印框架.。该框架为 GDI 应用程序提供了兼容。WPF 应用程序直接调用WPF显示框架生产XPS文件,如果驱动设置默认是采用EMF文件,则WPF应用程序需通过 XPS Print API 中的 Microsoft XPS to GDI Conversion组件, 将打印数据转化为 EMF 文件后传送给 Print Spooler。
3)直接打印的应用程序:基于这种直接打印的应用程序比较特殊,比如Adobe Photoshop软件。这种程序支持将打印文件不经过后台打印假脱机系统的缓存以及渲染等工作,直接生成打印机能够识别的打印格式文件。这种文件可以被所有支持这种语言的打印机所识别,由驱动程序将打印机语言传送到打印机的核心——控制器中,然后由打印机控制器负责完成页面描述文件到打印的图像的解释工作。从工作流程的角度看,采用打印机语言的打印方式对打印机的的控制器要求比较高,需要打印机能够自己独立处理转换的任务,并且需要打印机本身的内存空间足够大。目前支持这种直接打印的打印机语言主要有HP的PCL6和Adobe的PostScript,其中Adobe的PostScript是收费的,打印机厂商在其产品中使用PostScript语言要支付Adobe公司相关的使用费用,因此使用PostScript的打印机的成本要比使用其他语言和控制方式的打印机要高。而HP的PCL6则是免费的产品,所有的打印机厂商都可以在其产品中免费使用该打印机语言。直接打印的应用程序生产打印文件之后直接传递给打印机,因此客户端的过程和上述需要后台缓存和渲染的有所差异。
4)最后还有一种流程,就是使用打印机厂家自己的打印机驱动程序,其并不支持Windows的EMF或XPS,而是在应用程序调用打印接口生成打印文件是该打印机厂家自己定义的打印文件格式,比如佳能的打印机。同时,还有一些打印机直接生成原始的打印数据格式。什么是原始的打印数据格式呢?就是应用程序在生成打印文件的时候,打印机驱动程序直接调用了打印机图形接口组件直接渲染成打印机能够识别的打印机指令或者文件,然后在打印假脱机系统里面就只有缓存打印,而不用再次进行渲染的方式被称之为原始数据打印。
总体上,此时客户端过程结束,应用程序完成了打印文档的任务。用户可以自 由使用应用程序完成其他任务,而打印服务器端则保证文档被打印。
Spooler 过程
Spooler 过程即打印假脱过程,所谓假脱,就是当主机处理器向外部低速的输入输出设备(例如打印机)传送数据时,为了减少占用处理器的时间,将数据存放在临时缓存工作区中。打印程序可以在之后的任意时间点对其存取。而这临时缓存工作区通常是硬盘。
Spooler 工作流程如图右上角的虚线框所示,下面是此过程的详细描述:
1) GDI 或者WPF将打印作业转为EMF或XPS后,它将调用 Winspool.drv。
Winspool.drv 是一个进入 Spooler 的客户端接口。它导出的函数包括:spooler 的Win32 API,提供访问服务器的 RPC 栈。Winpool.drv 把数据发送到服务器端的核心打印进程Spoolsrv.exe。打印进程Spoolsrv.exe是一种系统服务,负责管理打印任务的汇总和分配。打印客户端通过远程进程调用RPC与Spoolsrv.exe进行通信。Spoolsrv.exe 是一个 Windows 服务,随着系统启动时启动。它提供服务端的RPC 接口,让本地客户端(Winspool.drv)或远程客户(Win32sql.dll)调用。Spoolsrv在接受到作业作用请求后,将调用打印路由器spoolss.dll提供的方法决定把数据传送到指定的打印提供者。打印路由器找到正确的打印提供者处理打印任务。
2)打印提供者将打印任务引向与打印机物理相链的机器,该机器可以是本地机器, 也可以是远端机器。它还管理打印任务队列,并实现启动,停止或枚举打印任务的API。Windwos操作系统提供本地打印提供者、Windows网络打印提供者、HTTP打印提供者等等。如果打印机不在本地,则由网络打印提供者在网络上传递打印任务。打印任务最后由本地打印提供者处理,将任务传递给打印处理器。打印过程中, Provider 负责创建打印作业,将 EMF 文件转化为 Spooler 文件(.spl),(.spl)是缓存文件。然后根据需要将缓存文件交给打印处理器进渲染处理。
3)打印处理器将打印文件转换成打印机能直接处理的原始数据格式。比如讲EMF文件转为RAW的格式,EMF文件格式是基于GDI图形接口生产的打印描绘文件,一般的打印机并不支持EMF文件来打印,因此一般都转为RAW的格式。通常来说, 我们提到渲染就是指这一过程。打印机的渲染一般的定义是指将一幅矢量图形进行光栅化。以OS的接口来说, 首先,针对打印生成的打印图形描画包括三个元素: 文字(Text)、 路径(Path)、 位图(Bitmap),打印机驱动端需要实现OS的这三个接口, 将这三个接口所对应的描述进行调用和转换。比如在Windows端打印文件里面描述的内容信息包括:
文字:主要提供的信息包括:位置、 间距、 字符、字体、 字体字符集、 颜色、 裁切等。 通常驱动会将字符对应的字体信息抽出, 作为下载字体一起发送给打印机。
路径:主要提供的信息包括:顶点、 连线方式、 画笔、 画刷、线型、 颜色、 裁切等。 路径是相对好处理的部分, 通常可以指令一对一的方式发送给打印机。
位图:主要提供的信息包括:位置、 大小、 图片位深、 叠加方式(ROP)、裁切等。 一些特殊的图片还会有透过度、 色彩表之类的信息。 位图是驱动处理比较麻烦的部分, 也是很见驱动功力的部分。 在渲染的过程中, 驱动也会将图片进行压缩再发送给打印机。
在描画之外,还有对于页面的控制部分。 如用纸大小、分辩率、复制份数等等。 这部分是将打印机设置部分的设置翻译为对应的页面控制指令。同样也是渲染的部分。
渲染工作完成之后,打印处理器将完成的打印原始数据返回打印提供者。
此时,Spooler过程完成。
总结来说,Windows的打印流程根据应用程序的类型,成熟不同的基于Windows的打印文件,然后经过Spooler的缓存以及渲染,传递给真实的打印机进行打印。
打印过程
打印过程包含两个组件:打印监视器和端口驱动程序。打印监视器负责把从Spooler 过程的打印提供者送来的数据准确地转发到相应的端口驱动程序。端口驱动再把数据发送给物理打印设备。其详细过程如下:
1)、打印提供者将原始数据传递给语言监视器,语言监视器为打印Spooler和打印机提供全双工的通信通道,并将数据传递给端口监视器。端口监视器提供打印Spooler和内核模式的端口I/O驱动程序之间的通信路径,该驱动程序直接存取打印机相链的硬件I/O端口。端口I/O驱动程序从主机向打印机发送数据。它还可以从打印机中接收状态信息,传回端口监视器及语言监视器。
2)打印机固件程序运行在打印机内嵌的CPU中,并从打印机I/O端口接收数据,解压、处理成驱动打印头的格式,并控制打印机中元数的机械、电力和电子部件,在纸上输出一个个小点。打印机一 般配有强大的RISC CPU、Buffer、一些内存和一块硬盘。并由复杂的实时、多任务操作系统和复杂的固件支持。
了解了以上的过程,那么我们就明白了为什么如果使用错误的驱动程序,打印机将打印失败了吧。使用错误的驱动程序会创建与打印机不兼容的假脱机文件,然而打印机并不知道这一点,打印机总是试图打印它接收到的任何东西。
此时,打印过程结束!
二、如何基于Windows的打印体系设计Citrix的打印体系?
根据上述的说明,我们可以思考:如何基于Windows的这套打印体系来设计虚拟桌面的打印体系?
首先,由上述我们明白,在Windows中有3种典型的打印流程:利用原始假脱机文件的打印流程,利用增强型图元文件(EMF)的打印流程和直接打印流程。这三个流程中,前两个打印流程会直接生成RAW文件和EMF或XPS文件,然后再发送给打印假脱机系统。
那么我们可不可以这样认为,在虚拟桌面架构下,我们可以在虚拟桌面里面实现打印流程的客户端过程。即在虚拟桌面生成RAW文件或者EMF文件或者XPS文件之后,虚拟桌面里面的Spooler客户端转发给Print Spooler时,直接将EMF文件或者XPS文件截取转发到本地终端,由本地终端的Print Spooler接收这个RAW文件、EMF或XPS文件,再由本地终端继续Windows的打印过程。
如果依靠这样的思路,虚拟桌面架构下我们仅仅需要实现的就是这样将这个RAW文件、EMF文件或者XPS文件数据截取然后转发给远程访问协议(ICA/HDX)。
那么如何来获取或者说在虚拟桌面里面截取这些文件呢?
根据Windows操作系统打印任务的工作流程,为了获取打印内容,打印过程中产生的假脱机文件是首先需要研究的对象。每个打印任务产生后缀名为SHD和SPL的2个假脱机文件,它们是在经过打印提供者的时候,有打印提供者调用后台线程将EMF文件或RAW、XPS文件解析缓存为缓存文件后生成的,并存放在系统后台打印文件夹,在打印任务完成之后由系统自动删除。后缀名为SHD的假脱机文件包含了一些和打印任务相关的重要信息。后缀名为SPL的假脱机文件包含了具体的打印内容,它有2种类型:和具体的打印机及其驱动程序相关的原始(RAW)打印内容,统一的EMF的打印内容。
理解了上面的内容之后,我们就思考:
1、是否可以将打印提供者的缓存文件直接拿出来?
2、打印提供者是否可以由打印机驱动程序自己编写一个用来替换原本的Windows的打印提供者?
在Windows后台打印假脱机系统中时,其中的几个模块组件Windows可以提供给我们进行自定义的分别是:
1)Windows打印客户端(Winspool.drv):该组件无法替换;
2)Windows打印系统服务进程(Spoolsrv.exe):该组件无法替换;
3)打印路由器(spoolss. dll):该组件无法替换;
4)打印提供者(Provider):扩展部分属于打印机驱动程序的范畴,该组件可以自定义;
5)打印处理器:该组件内置的打印提供者中,可以自定义;
6)打印监视器:扩展部分属于打印机驱动程序的范畴,该组件可以自定义;
有上述可以自定义的三个组件中,我们看见,Windows的打印假脱机系统中,打印提供者是可以进行替换的,那么:
我们可以直接编写一个打印提供者,在接受到打印文件EMF或XPS、RAW之后,不对其进行缓存操作,而是将其转发到远程访问协议的虚拟通道传递到本地终端!
到了这里我想大家都应该明白了,是的,Citrix的UPD技术就是在此提供了一个UPD的打印提供者,将生产的打印文件转发到ICA/HDX的虚拟通道。
但是还有个问题?我不用Citrix UPD技术的情况下,我怎么来转发我的数据?
在使用NPD的情况下,NPD就是使用真实打印机的驱动程序。因为真实的打印机驱动程序是基于传统的环境开发的,并不会在此提供一个打印提供者,就算在此提供一个打印提供者,该打印提供者也不会将数据转发给虚拟通道,而且也不知道要转发给虚拟通道?那怎么办呢?
既然我无法在打印提供者这里做动作,那么我们继续跟着流程往后走,再往后是什么?打印处理器。打印处理器会在虚拟桌面里面调用打印机驱动程序的打印图形接口来进渲染操作,完成之后将打印作业发送给下一个组件:打印监视器。
我们说打印监视器有两个部分组成:
语言监视器
端口监视器
语言监视器的作用主要是用于进行打印机和系统之间的双向通信以及插入控制指令。因此他对于我们获取到数据没有任何帮助。
而端口监视器则是真正将打印作业发送给打印机的驱动。根据上述我们的描述,我们最多端口监视器其实是可以替换的,那么我们的实现思路就出来了:开发一个端口监视器的模块替换Windows的端口监视器模块,让其打印作业转发到ICA/HDX协议的虚拟通道即可。
由以上分析结果可知:Citrix的打印技术在基于Windows的打印体系之上,利用Windows的打印体系可替换的组件,开发自研组件的组件或模块用于替换掉Windows的组件,在打印过程中,这些被替换的组件会将打印作业转发到Citrix ICA/HDX协议的虚拟通道,由该协议传递到本地终端进行其后的打印作业。
本文出自 “我拿流年乱了浮生” 博客,请务必保留此出处http://tasnrh.blog.51cto.com/4141731/1872403
原文地址:http://tasnrh.blog.51cto.com/4141731/1872403