打印作业是由应用程序通过微软的Win32 GDI函数调用来创建的,这些函数调用可以被假脱为EMF数据类型记录以备EMF打印处理器以后回放,或者它们可以对每个文档页面立即绘制一个可打印的图像。 是否立即执行或在EMF记录的回放时期进行图像的绘制,是由GDI绘制引擎来控制的,在绘制的操作中,GDI绘制引擎将调用合适的Windows 2000打印驱动程序来支持。
Windows 2000打印机驱动程序具有如下责任:
■通过提供GDI不能支持的特定打印机的绘制能力来帮助GDI绘制打印作业。
■发送绘制的图像数据流到打印假脱机。
■提供一个与打印机及打印文档相关的可修改配置参数的用户接口,如选择的输入输出的托架、打印的份数、图像的精度及方向等等。
Windows 2000打印驱动程序由一套打印驱动程序组件组成,这些组件将驱动程序的绘制以及用户接口操作分到独立的DLL中。
本章的目的是解释怎样为Windows 2000编写驱动程序,但非常重要的一点是,必须记住下面三个与操作系统一起发布的打印机驱动程序:
■微软统一打印机驱动程序
■微软Postscript打印机驱动程序
■微软绘图仪打印机驱动程序
这三个驱动程序支持当今市场上许多打印设备。 只需要在打印机设备与微软提供的合适的打印驱动程序不兼容时才写打印机驱动程序,许多新的打印机可以简单到只需增加一个打印机的数据文件到这些驱动程序中就可以获得支持。 那些包含由私有的命令序列控制的硬件绘制加速器的设备可能才需要重新写驱动程序。
3.1打印机驱动程序组件
所有的Windows 2000打印机驱动程序由以下组件构成:
■一个打印机图形DLL,它帮助GDI绘制打印作业并发送绘制的数据流到打印假脱机。
■一个打印机接口的DLL,它为驱动程序参数配置提供一个用户接口和一个假脱机可以调用以来通知驱动程序系统打印相关事件的接口。
另外,微软提供的打印机驱动程序利用打印机的数据文件。
3.1.1打印机图形DLL
本部分提供以下主题:
■3.1.1.1打印机图形DLL介绍
■3.1.1.2由打印机图形DLL定义的函数
■3.1.1.3打印机图形DLL实例
■3.1.1.4绘制一个打印作业
■3.1.1.5支持设备字体
■3.1.1.6返回专用的打印机信息
■3.1.1.7选择用户模式或内核模式
■3.1.1.8构建一个打印机图形DLL
3.1.1.1打印机图形DLL介绍
打印机图形DLL实现了在第一部分的第3章支持DDI中描述的Drv为前辍的DDI函数。 这些DLL具有以下列两项责任:
■帮助GDI绘制打印作业:一个打印机图形DLL可以支持DDI绘制函数来处理那些必须以特定设备的方式执行以及那些不能由GDI的绘制引擎完成的绘制操作。
■发送绘制数据流到假脱机:打印机图形DLL通常以RAW数据类型(包括命令序列)产生一个输出流,假脱机可以通过打印监视器将它发送到打印机硬件。
一个打印机图形DLL必需提供的、特定打印机类型的绘制帮助的总数量,依赖于硬件的绘制能力并包括以下情形:
■GDI绘制引擎用一个GDI管理的表面执行所有的绘制,图形的DLL不提供任何的DDI绘制函数。
■图形DLL提供一些DDI绘制函数用一个GDI管理的表面与GDI绘制引擎一起工作。 由图形DLL提供绘制DDI函数可以选择性地回调GDI绘制引擎的GDI支持的服务。
■图形DLL通过提供DDI绘制函数和一个设备管理的表面来执行所有的绘制。
例如,当微软统一打印机驱动程序使用一个设备管理的表面时,微软统一打印机驱动程序(Unidrv:Microsoft Universal Printer Driver)使用一个GDI管理的表面并提供一些DDI绘制函数。
更多的关于在图形驱动程序中提供绘制帮助的信息,参考第一部分的第2章表面类型和第一部分的第3章支持DDI部分的内容。
打印机图形DLL在EMF假脱一个打印作业时不能调用,当EMF打印处理器回放EMF记录时适当的图形DLL调用。
但要注意,打印机图形DLL不能发送绘制的打印作业到硬件,被绘制的数据流返回到假脱机,假脱机发送该数据流到一个端口监视器,该端口监视器是一个用户模式的接口到一个内核模式的本地并行端口或网络端口的微端口驱动程序
对Windows 2000来说,可以设计一个打印机的图形DLL以在内核模式执行,或者可以设计它在用户模式执行,更多的信息可以参考选择用户模式或内核模式部分的内容。
下面的两个图表示当一个应用程序用GDI创建一个打印作业时发生的数据流,第一个图表是一个用户模式的打印机图形DLL。
插入图3-1 打印作业数据流,使用一个用户模式的打印机图形DLL
第二个图表是一个内核模式的打印机图形DLL。
插入图3-2 打印作业数据流,使用一个内核模式的打印机图形DLL
注意,在图表中,如果从GDI输出的格式是EMF,打印机图形DLL在EMF打印处理器回放EMF作业记录前不接收作业,也要注意的是EMF打印处理器改变输出格式到非EMF格式。
上面两个图表示了一个完整的本地环境,如果打印机连接到一个服务器,EMF记录则通常客户的GDI绘制引擎(GRE)拷贝来产生,然后假脱到一个本地文件后送到服务器,服务器的假脱机拷贝读该文件并将这些记录送到服务器的EMF打印处理器,并且服务器端的GRE拷贝调用服务器打印机图形DLL。
3.1.1.2由打印机图形DLL定义的函数
如果所有的图形驱动程序,打印机图形DLL负责定义以下的DDI函数,由初始化驱动程序DrvEnableDriver函数的入口点开始,其余的函数以字母顺序列出。
函数名称 描述
DrvEnableDriver
允许驱动程序初始化它自身并返回支持DDI函数的指针
DrvCompletePDEV
对一个设备实例提供具有GDI句柄的驱动程序
DrvDisableDriver
(可选)允许驱动程序在卸载之前执行清除工作
DrvDisablePDEV
允许驱动程序删除设备特定实例的信息
DrvDisableSurface
允许驱动程序删除绘制表面
DrvEnablePDEV
允许驱动程序提供具有物理设备特征的GDI并初始化设备特定实例的信息
DrvEnableSurface
允许驱动程序创建绘制表面
DrvQueryDeviceSupport
(可选)返回被请求的特定设备的信息
DrvQuery
(可选)返回被请求的特定驱动程序的信息
打印机图形DLL也对定义以下特定打印的DDI函数负责,它们在绘制打印作业过程中的某一特定点时被调用。
函数 何时调用
DrvEndDoc
当GDI已经结束发送一个文档到驱动程序以备绘制时
DrvNextBand
(可选)当GDI已经结束绘制一个物理页面的一个区带(band),这样驱动程序可以将该区发送到打印机
DrvQueryPerBandInfo
(可选)在GDI开始为一个物理页面绘制区带之前,这样驱动程序可以提供具有特定区带信息的GDI。
DrvSendPage
当GDI已经结束绘制物理页面,这样驱动程序可以将此页面发送到打印机。
DrvStartBanding
(可选)当GDI准备绘制时开始发送一个物理页面的区带信息到驱动程序
DrvStartDoc
当GDI准备绘制时开始发送一个文档到驱动程序
DrvStartPage
当GDI准备绘制时开始发送一个文档页到驱动程序
通常,一个打印机图形DLL也定义任何必需的附加DDI函数以完成打印作业绘制。 被定义函数的数量及类型依赖于:
■是否驱动程序支持绘制表面时使用被管理的GDI或被管理的设备(或者两者都支持),更多的信息,可以参考第一部分的第2章表面类型。
■一个绘制操作可以被GDI函数处理而不是由驱动程序自身执行的程度。 更多的信息,参考第一部分第3章支持DDI部分的内容。
所有的由打印机图形DLL定义的函数都是被GDI内核模式图形绘制引擎(GRE)来调用的。
DrvEnableDriver和DrvQueryDriverInfo函数由图形DLL导出,所有其他支持DDI函数的地址放置于一个由DrvEnableDriver函数返回的表中。
3.1.1.3打印机图形DLL实例
plotter.dll的源代码是打印机图形DLL为微软绘图仪驱动程序提供的,与些DDK一起提供,其代码放置于包含DDK实例目录的子目录中。
3.1.1.4绘制一个打印作业
打印作业或者是在它们创建时绘制,或者是被假脱成为EMF记录绘制。 在后一种情况下,绘制发生在当EMF打印处理器(Winprint.dll)回放EMF记录时。 绘制则由一系列对GDI的Win32绘制函数的调用构成,并以CreateDC为调用的开始。
当一个应用程序调用GDI的CreateDC函数来创建打印机的设备设备环境,GDI检查看是否有合适的打印机图形DLL被装载,如果没有,GDI装载DLL并调用它的DrvEnableDriver函数,该函数不会被再次调用,除非驱动程序被重新装载。
接下来,GDI调用打印机图形DLL的DrvEnablePDEV函数,这样驱动程序可以创建一个设备实例并返回设备特征。 GDI用返回的信息以创建设备实例的内部描述。 图形DLL必须用这一句柄作为一个Eng为前缀,由GDI绘制引擎(参考第一部分第2章GDI支持服务)提供的回调的输入。
调用图形DLL的DrvEnableSurface函数之后,驱动程序可以通过调用EngCreateBitmap为设备实例创建一个绘制表面,或者,如果绘制表面是设备管理的,则调用EngCreateDeviceSurface。 如果EngCreateBitmap不能提供一个足够大的位图来包含整个的物理页面,并且如果驱动程序支持页面分区带的功能,EngMarkBandingSurface可以被调用以通知进行GDI分区带的工作。 最后,EngAssociateSurface必须被调用以允许GDI将创建的表面与特定设备实例协调,并允许GDI知道哪一个驱动程序提供的DDI绘制函数(如果任意)应当在绘制这一特殊表面时调用。
从这一点来看,一个绘制表面已经创建并且绘制可以开始,对每一个绘制的文档来说,GDI在打印机图形DLL中调用以下的函数:
l DrvStartDoc
For each physical page{
l DrvStartPage
DrStartBanding
For each banding surface{
DrvQueryPerbandInfo
Rendering operations
DrvNextBand
}
DrvSendPage
}
l DrvendDoc
除DrvqueryPerBandInfo函数之外,这些函数都是为了允许打印机图形DLL向打印机硬件发送控制序列(通过调用EngWritePrinter),并执行初始化或完成处理一个文档、页面或区带等任何内部操作。
打印机图形DLL负责在适当的时间发送绘制图像(即,绘制表面的内容)到打印机(通过调用EngWritePrinter),如下:
■对GDI管理或设备管理的位图表面:绘制表面是GDI提供的或驱动程序提供的位图,打印机图形DLL可以分出一些起绘制函数(参考第一部分第2章的表面协商部分),如果使用了页面区带,DrvNextBand函数应当发送绘制表面内容。 如果不使用区带,DrvSendPage函数应当发送绘制表面内容。
■对设备管理的向量表面:绘制表面是在设备里,打印机图形DLL分出的所有绘制函数(参考第一部分第2章的表面协商部分),并且这些函数在绘制操作过程中发送图像数据到打印机,而页面分带区不能被使用。
如果预料任意由打印机DLL提供的DDI函数可以潜在执行5秒以上,就应当包括至少每5秒一次调用EngCheckAbort至少每5秒一次的代码并看是否打印作业被中断。
在GDI调用DrvendDoc以指明一个文档已经绘制完成后,它调用DrvDisableSurface。如果DrvEnableSurface调用EngCreateBitmap,则DrvdisableSurface必须调用EngDeletesurface。
GDI在一个应用程序调用DrvDeleteDC时调用一个打印机图形DLL的DrvDisablePDEV函数。
如果一个应用程序在打印一个文档时调用GDI的ResetDC函数,GDI创建一个新的设备设备环境并为该新设备设备环境调用打印机图形DLL的DrvEnablePDEV函数。 然后,GDI调用DrvResetPDEV函数,这样图形DLL可以用从旧的设备环境获得的信息修改新的设备环境。 接着,DrvDisableSurface及DrvDisablePDEV为旧的设备环境而调用,对新的设备环境,接下来被调用的函数就是DrvEnableSurface。 最后,GDI调用DrvStartDoc在一个新页上进行绘制。
GDI调用DrvDisableDriver优先于卸载打印机图形DLL。
如果打印机硬件支持GDI绘制函数不支持的绘制操作,则打印机图形DLL可以提供一个DrvDrawEscape函数。
如果需要支持通过GDI函数不能实现的绘制或非绘制的操作,则一个打印机图形DLL需要提供一个DrvEscape函数。 例如,微软的Postscript打印机驱动程序用escape来支持Postscript入口点,或者,一个应用程序也许需要获得一个传真机的电话号码,DrvEscape函数也用于指出由DrvDrawEscape函数支持的操作。
3.1.1.5支持设备字体
如果一个打印机提供设备字体,打印机图形DLL必须定义一个DrvTextOut函数以产生文本输出命令,图形DLL必须也定义以下函数:
DrvQueryAdvanceWidths
DrvQueryFont
DrvQueryFontData
DrvQueryFontTree
更多的关于支持设备字体的信息,参考第一部分第3章中的支持DDI字体及文本函数部分。
3.1.1.6返回专用的打印机信息
GDI有时请求打印机图形DLL返回在打印作业之间的打印机专用的信息,通过调用这些DrvQuery前缀的DDI函数如DrvqueryAdvanceWidths(如果被图形DLL定义)。
什么时候会发生这些的一个例子是在字处理应用程序中维持了一个所见即所得(WYSIWYG)屏幕显示的可打印页面。 为了准确地显示文本中的线的间断,字处理器必须对字符宽度及其他选定的打印机实现的字体的尺度进行基准线的合适性计算。
3.1.1.7选择用户模式或内核模式
在4.0版以前的Windows NT中,GDI及所有的图形驱动程序都是在用户模式下执行,在4.0版中,这些组件被移到内核模式,这一改变提高了显示设备的性能,但对打印设备的性能获得并不显著,不足以抵消继承用户模式失去的优点。
因此,对Windows 2000,可以设计一个打印机图形DLL以在内核模式下执行,或者可以设计在用户模式下执行。 用户模式下的打印机图形DLL执行提供以下超过内核模式下执行的优点:
■无限制的栈空间
■存取Win32 API
■更少的导致系统崩溃的潜在因素
■用户模式的纠错器,更容易纠错
■更好的浮点计算能力,尽管DDI浮点函数不是必需的
■调用任何定制的,厂商提供的用户模式的但不是Windows 2000打印机驱动程序体系结构中描述的部分的DLL的能力。
在用户模式下使用图形DDI
一个用户模式打印机图形DLL并没有限定于去调用GDI支持的服务(第一部分第2章)和其他以Eng为前缀的图形DDI回调函数。 但是,有一些必须遵从的规则:
■如同内核模式图形DLL,用户模式图形DLL必须调用图形DDI的版本函数以创建或修改绘制表面,这些回调函数是GDI支持的服务,并且不允许调用这些绘制函数在Win32中的等效的函数。
对用户模式的DLL,调用这些绘制的回调函数被用户模式的GDI客户端截听,之后,客户端会通知GDI的内核模式图形绘制引擎(GRE)。
■下面的以Eng为前缀的DDI函数不能被用户模式的DLL调用。
EngCreatePath
EngCreateClip
EngDeleteClip
EngGetTypeFontList
EngMapModule
EngDebugBreak
所有其他以Eng为前缀的函数对内核模式的打印机图形DLL都是可用的,在用户模式下也是也用的。 对那些与Win32完全等效的函数,如EngGetPrinter或EngGetPrinterDriver,则Win32等效的函数被调用。 没有完全与Win32等效的函数,则在GDI客户端来实现。
■用户模式的打印机图形DLL可以继续用DDI函数以实现对浮点的支持。
将一个存在的打印机图形DLL转换到用户模式
如果以前开发过运行于内核模式的打印机图形DLL,对Windows 2000来说,会有以下选择:
1.什么也不做。 已经存在的内核模式的打印机图形DLL将会继续在内核模式下工作,微软也许会在以后的操作系统发行版本中删除对内核模式打印机图形DLL的支持。
2.转换该DLL到用户模式下执行。 为了执行,只需增加一个DrvQueryDriverInfo函数到DLL,然后遵从构建打印机图形DLL的规则。
在创建一个新的在用户模式下的打印机图形DLL
当开发一个新的执行在用户模式下的打印机图形DLL时,可以继续使用所有的被内核模式DLL使用的图形DDI函数。 但是,也有下边这些选择:
■对以Eng为前缀有完全等效Win32的函数,可以调用Win32函数,这些函数由以下一些函数构成:
EngAllocMem EngGetForm EngMulDiv
EngAllocUserMem EngGetLastError EngSetLastError
EngEnumForms EngGetPrinter EngSetPrinterData
EngFreeMem EngGetPrinterData EngUnloadImage
EngFreeUserMem EngGetPrinterDriver EngWritePrinter
EngFindImageProAddress EngLoadImage
■对以Eng为前缀的那些与Win 32函数符合一些简单功能的函数,可以调用Win32函数,这些函数由以下一些函数组成:
EngAcquirSemaphore EngGetCurrentCodePage
EngMultiByteToWideChar EngCreateSemaphore
EngGetDriverName EngQueryLocalTime
EngDeleteSemaphore EngGetPrinterDataFileName
EngReleaseSemaphore EngFindResource
EngLoadModule EngUnicodeToMultiByteN
EngFreeModule EngMultiByteToUnicodeN
EngWideCharToMultiByte
■对那些创建或修改一个绘制服务的函数,新的驱动程序必须继续调用GDI支持服务而不是它们等效的Win32函数。
■不使用DDI函数来支持浮点,可以使用FLOAT数据类型。
3.1.1.8构建一个打印机图形DLL
当构建一个打印机图形DLL时,必须清楚打算在用户模式下执行和打算在内核模式下执行的DLL之间的以下差别:
构建打印机图形DLL的规则
用户模式图形DLL 内核模式图形DLL
在source文件中,设置TARGETTYPE=DYNLINK
在source文件中,设置TARGETTYPE=GDI_DRIVER
处理器宏USERMODE_DRVER必须在winddi.h包含之前在source文件中被定义
处理器宏USERMODE_DRIVER不能被定义
对象模块必须被与umpdddi.lib和gdi32.lib导入库链接在一起
对象模块必须与win32k.lib导入库链接
DrvQueryDriverInfo函数必须返回DRVQUERY_USERMODE
DrvQueryDriverInfo函数必须给DRVQUERY_USERMODE返回FALSE(或二者选择其一,该函数可以被省略)
3.1.2打印机接口DLL
本部分提供以下主题:
■3.1.2.1打印机接口DLL介绍
■3.1.2.2由打印机接口DLL定义的函数
■3.1.2.3打印机接口DLL实例
■3.1.2.4为打印机创建原型表单页面
3.1.2.1打印机接口DLL介绍
打印机通常给用户提供大量的可修改配置的选择,这些选择项可以在每一个被付印的文档中被改变。 如页面、托架、字体选择、图像精度、尺寸、色彩等等都必须可以通过用户接口被存取,也可以被应用程序调用。
一个打印机驱动程序的打印机接口DLL,它是执行在用户模式下,负责导出一个用户的接口到打印机的配置选项。 提供这一接口包括为打印机创建原型表单页面。 应用程序(如打印折叠器)通过调用由打印假脱机导出的Win32函数显示接口,并且,假脱机返过来,又调用由打印机接口DLL定义的函数。
为配置选项提供一个用户接口不是打印机接口DLL的唯一责任,DLL同时也导出假脱机可以调用的函数以通知驱动程序打印相关的系统事件,如驱动程序安装、升级或者添加打印机及打印机连接等。
3.1.2.2由打印机接口DLL定义的函数
打印机接口DLL导出函数列于下面表格中:
函数 目的
DllEntryPoint
初始化DLL的入口点,通常调用DLLMain(在平台的SDK文档中有描述)
DrvConvertDevMode
转化专用的DEVMODE结构到另外一个版本
DrvDeviceCapabilites
返回请求的关于一个打印机能力的信息
DrvDevicePropertySheets
调用CPSUI以产生描述打印机属性的表单页面属性
DrvDocumentEvent
(可选)允许打印机接口DLL去处理与打印文档相关的一定的事件
DrvDriverEvent
(可选)允许打印机接口DLL对假脱机来的已经发生的关于特定打印机事件的消息通知作响应
DrvDocumentPropertySheets
调用CPSUI以产生描述打印文档属性的表单页面属性
DrvPrinterEvent
允许打印机接口DLL对假脱机来的已发生的关于特定打印机的事件的消息通知作响应
DrvQueryColorProfile
(可选)允许打印机接口DLL指定一个ICC的profile以在色彩管理中使用
DrvQueryJobAttributes
(可选)允许打印机接口DLL指定诸如打印多个文档页面在一个物理页上(“N-up”打印)、打页某一页面的多个拷贝以及校对页面等
DrvQueryPrintEx
决定一个打印作业是否能用打印机当前配置进行打印
DrvUpgradePrinter
(可选)当一个新版本的驱动程序增加到系统时,修改打印机的注册表设置
3.1.2.3打印机接口DLL实例
plotui.dll的源代码,即微软绘图仪驱动程序的打印机接口DLL与此DDK一起提供,其代码放置于包含DDK示例代码的目录树的子目录中。
3.1.2.4为打印机创建原型表单页面
打印机接口DLL,与CPSUI连在一起,负责创建Windows 2000用户从事的与打印机及打印文档相关的视图及修改配置参数表单页面的属性。 每一个打印机接口的DLL必须提供一个DrvDevicePropertySheets函数以创建特定打印机页面和一个DrvDocumentPropertySheets函数以创建特定文档的页表。
为了理解这些函数是怎样被设计,阅读描述CPSUI、显示表单页面属性包括应用程序之间的交互、打印假脱机、打印机接口DLL以及CPSUI执行流等许多在打印机驱动程序中使用CPSUI一章中描述的内容是非常重要的。
3.1.3打印机数据文件
微软提供的打印机驱动程序利用数据文件来描述各个打印设备,更多的信息,可以参考以下几章内容:
■微软统一打印机驱动程序:描述了.gpd文件
■微软Postscript打印机驱动程序:描述了.ppd文件
■微软绘图仪打印机驱动程序:描述了.pcd文件
Windows 2000打印机驱动程序具有如下责任:
■通过提供GDI不能支持的特定打印机的绘制能力来帮助GDI绘制打印作业。
■发送绘制的图像数据流到打印假脱机。
■提供一个与打印机及打印文档相关的可修改配置参数的用户接口,如选择的输入输出的托架、打印的份数、图像的精度及方向等等。
Windows 2000打印驱动程序由一套打印驱动程序组件组成,这些组件将驱动程序的绘制以及用户接口操作分到独立的DLL中。
本章的目的是解释怎样为Windows 2000编写驱动程序,但非常重要的一点是,必须记住下面三个与操作系统一起发布的打印机驱动程序:
■微软统一打印机驱动程序
■微软Postscript打印机驱动程序
■微软绘图仪打印机驱动程序
这三个驱动程序支持当今市场上许多打印设备。 只需要在打印机设备与微软提供的合适的打印驱动程序不兼容时才写打印机驱动程序,许多新的打印机可以简单到只需增加一个打印机的数据文件到这些驱动程序中就可以获得支持。 那些包含由私有的命令序列控制的硬件绘制加速器的设备可能才需要重新写驱动程序。
3.1打印机驱动程序组件
所有的Windows 2000打印机驱动程序由以下组件构成:
■一个打印机图形DLL,它帮助GDI绘制打印作业并发送绘制的数据流到打印假脱机。
■一个打印机接口的DLL,它为驱动程序参数配置提供一个用户接口和一个假脱机可以调用以来通知驱动程序系统打印相关事件的接口。
另外,微软提供的打印机驱动程序利用打印机的数据文件。
3.1.1打印机图形DLL
本部分提供以下主题:
■3.1.1.1打印机图形DLL介绍
■3.1.1.2由打印机图形DLL定义的函数
■3.1.1.3打印机图形DLL实例
■3.1.1.4绘制一个打印作业
■3.1.1.5支持设备字体
■3.1.1.6返回专用的打印机信息
■3.1.1.7选择用户模式或内核模式
■3.1.1.8构建一个打印机图形DLL
3.1.1.1打印机图形DLL介绍
打印机图形DLL实现了在第一部分的第3章支持DDI中描述的Drv为前辍的DDI函数。 这些DLL具有以下列两项责任:
■帮助GDI绘制打印作业:一个打印机图形DLL可以支持DDI绘制函数来处理那些必须以特定设备的方式执行以及那些不能由GDI的绘制引擎完成的绘制操作。
■发送绘制数据流到假脱机:打印机图形DLL通常以RAW数据类型(包括命令序列)产生一个输出流,假脱机可以通过打印监视器将它发送到打印机硬件。
一个打印机图形DLL必需提供的、特定打印机类型的绘制帮助的总数量,依赖于硬件的绘制能力并包括以下情形:
■GDI绘制引擎用一个GDI管理的表面执行所有的绘制,图形的DLL不提供任何的DDI绘制函数。
■图形DLL提供一些DDI绘制函数用一个GDI管理的表面与GDI绘制引擎一起工作。 由图形DLL提供绘制DDI函数可以选择性地回调GDI绘制引擎的GDI支持的服务。
■图形DLL通过提供DDI绘制函数和一个设备管理的表面来执行所有的绘制。
例如,当微软统一打印机驱动程序使用一个设备管理的表面时,微软统一打印机驱动程序(Unidrv:Microsoft Universal Printer Driver)使用一个GDI管理的表面并提供一些DDI绘制函数。
更多的关于在图形驱动程序中提供绘制帮助的信息,参考第一部分的第2章表面类型和第一部分的第3章支持DDI部分的内容。
打印机图形DLL在EMF假脱一个打印作业时不能调用,当EMF打印处理器回放EMF记录时适当的图形DLL调用。
但要注意,打印机图形DLL不能发送绘制的打印作业到硬件,被绘制的数据流返回到假脱机,假脱机发送该数据流到一个端口监视器,该端口监视器是一个用户模式的接口到一个内核模式的本地并行端口或网络端口的微端口驱动程序
对Windows 2000来说,可以设计一个打印机的图形DLL以在内核模式执行,或者可以设计它在用户模式执行,更多的信息可以参考选择用户模式或内核模式部分的内容。
下面的两个图表示当一个应用程序用GDI创建一个打印作业时发生的数据流,第一个图表是一个用户模式的打印机图形DLL。
插入图3-1 打印作业数据流,使用一个用户模式的打印机图形DLL
第二个图表是一个内核模式的打印机图形DLL。
插入图3-2 打印作业数据流,使用一个内核模式的打印机图形DLL
注意,在图表中,如果从GDI输出的格式是EMF,打印机图形DLL在EMF打印处理器回放EMF作业记录前不接收作业,也要注意的是EMF打印处理器改变输出格式到非EMF格式。
上面两个图表示了一个完整的本地环境,如果打印机连接到一个服务器,EMF记录则通常客户的GDI绘制引擎(GRE)拷贝来产生,然后假脱到一个本地文件后送到服务器,服务器的假脱机拷贝读该文件并将这些记录送到服务器的EMF打印处理器,并且服务器端的GRE拷贝调用服务器打印机图形DLL。
3.1.1.2由打印机图形DLL定义的函数
如果所有的图形驱动程序,打印机图形DLL负责定义以下的DDI函数,由初始化驱动程序DrvEnableDriver函数的入口点开始,其余的函数以字母顺序列出。
函数名称 描述
DrvEnableDriver
允许驱动程序初始化它自身并返回支持DDI函数的指针
DrvCompletePDEV
对一个设备实例提供具有GDI句柄的驱动程序
DrvDisableDriver
(可选)允许驱动程序在卸载之前执行清除工作
DrvDisablePDEV
允许驱动程序删除设备特定实例的信息
DrvDisableSurface
允许驱动程序删除绘制表面
DrvEnablePDEV
允许驱动程序提供具有物理设备特征的GDI并初始化设备特定实例的信息
DrvEnableSurface
允许驱动程序创建绘制表面
DrvQueryDeviceSupport
(可选)返回被请求的特定设备的信息
DrvQuery
(可选)返回被请求的特定驱动程序的信息
打印机图形DLL也对定义以下特定打印的DDI函数负责,它们在绘制打印作业过程中的某一特定点时被调用。
函数 何时调用
DrvEndDoc
当GDI已经结束发送一个文档到驱动程序以备绘制时
DrvNextBand
(可选)当GDI已经结束绘制一个物理页面的一个区带(band),这样驱动程序可以将该区发送到打印机
DrvQueryPerBandInfo
(可选)在GDI开始为一个物理页面绘制区带之前,这样驱动程序可以提供具有特定区带信息的GDI。
DrvSendPage
当GDI已经结束绘制物理页面,这样驱动程序可以将此页面发送到打印机。
DrvStartBanding
(可选)当GDI准备绘制时开始发送一个物理页面的区带信息到驱动程序
DrvStartDoc
当GDI准备绘制时开始发送一个文档到驱动程序
DrvStartPage
当GDI准备绘制时开始发送一个文档页到驱动程序
通常,一个打印机图形DLL也定义任何必需的附加DDI函数以完成打印作业绘制。 被定义函数的数量及类型依赖于:
■是否驱动程序支持绘制表面时使用被管理的GDI或被管理的设备(或者两者都支持),更多的信息,可以参考第一部分的第2章表面类型。
■一个绘制操作可以被GDI函数处理而不是由驱动程序自身执行的程度。 更多的信息,参考第一部分第3章支持DDI部分的内容。
所有的由打印机图形DLL定义的函数都是被GDI内核模式图形绘制引擎(GRE)来调用的。
DrvEnableDriver和DrvQueryDriverInfo函数由图形DLL导出,所有其他支持DDI函数的地址放置于一个由DrvEnableDriver函数返回的表中。
3.1.1.3打印机图形DLL实例
plotter.dll的源代码是打印机图形DLL为微软绘图仪驱动程序提供的,与些DDK一起提供,其代码放置于包含DDK实例目录的子目录中。
3.1.1.4绘制一个打印作业
打印作业或者是在它们创建时绘制,或者是被假脱成为EMF记录绘制。 在后一种情况下,绘制发生在当EMF打印处理器(Winprint.dll)回放EMF记录时。 绘制则由一系列对GDI的Win32绘制函数的调用构成,并以CreateDC为调用的开始。
当一个应用程序调用GDI的CreateDC函数来创建打印机的设备设备环境,GDI检查看是否有合适的打印机图形DLL被装载,如果没有,GDI装载DLL并调用它的DrvEnableDriver函数,该函数不会被再次调用,除非驱动程序被重新装载。
接下来,GDI调用打印机图形DLL的DrvEnablePDEV函数,这样驱动程序可以创建一个设备实例并返回设备特征。 GDI用返回的信息以创建设备实例的内部描述。 图形DLL必须用这一句柄作为一个Eng为前缀,由GDI绘制引擎(参考第一部分第2章GDI支持服务)提供的回调的输入。
调用图形DLL的DrvEnableSurface函数之后,驱动程序可以通过调用EngCreateBitmap为设备实例创建一个绘制表面,或者,如果绘制表面是设备管理的,则调用EngCreateDeviceSurface。 如果EngCreateBitmap不能提供一个足够大的位图来包含整个的物理页面,并且如果驱动程序支持页面分区带的功能,EngMarkBandingSurface可以被调用以通知进行GDI分区带的工作。 最后,EngAssociateSurface必须被调用以允许GDI将创建的表面与特定设备实例协调,并允许GDI知道哪一个驱动程序提供的DDI绘制函数(如果任意)应当在绘制这一特殊表面时调用。
从这一点来看,一个绘制表面已经创建并且绘制可以开始,对每一个绘制的文档来说,GDI在打印机图形DLL中调用以下的函数:
l DrvStartDoc
For each physical page{
l DrvStartPage
DrStartBanding
For each banding surface{
DrvQueryPerbandInfo
Rendering operations
DrvNextBand
}
DrvSendPage
}
l DrvendDoc
除DrvqueryPerBandInfo函数之外,这些函数都是为了允许打印机图形DLL向打印机硬件发送控制序列(通过调用EngWritePrinter),并执行初始化或完成处理一个文档、页面或区带等任何内部操作。
打印机图形DLL负责在适当的时间发送绘制图像(即,绘制表面的内容)到打印机(通过调用EngWritePrinter),如下:
■对GDI管理或设备管理的位图表面:绘制表面是GDI提供的或驱动程序提供的位图,打印机图形DLL可以分出一些起绘制函数(参考第一部分第2章的表面协商部分),如果使用了页面区带,DrvNextBand函数应当发送绘制表面内容。 如果不使用区带,DrvSendPage函数应当发送绘制表面内容。
■对设备管理的向量表面:绘制表面是在设备里,打印机图形DLL分出的所有绘制函数(参考第一部分第2章的表面协商部分),并且这些函数在绘制操作过程中发送图像数据到打印机,而页面分带区不能被使用。
如果预料任意由打印机DLL提供的DDI函数可以潜在执行5秒以上,就应当包括至少每5秒一次调用EngCheckAbort至少每5秒一次的代码并看是否打印作业被中断。
在GDI调用DrvendDoc以指明一个文档已经绘制完成后,它调用DrvDisableSurface。如果DrvEnableSurface调用EngCreateBitmap,则DrvdisableSurface必须调用EngDeletesurface。
GDI在一个应用程序调用DrvDeleteDC时调用一个打印机图形DLL的DrvDisablePDEV函数。
如果一个应用程序在打印一个文档时调用GDI的ResetDC函数,GDI创建一个新的设备设备环境并为该新设备设备环境调用打印机图形DLL的DrvEnablePDEV函数。 然后,GDI调用DrvResetPDEV函数,这样图形DLL可以用从旧的设备环境获得的信息修改新的设备环境。 接着,DrvDisableSurface及DrvDisablePDEV为旧的设备环境而调用,对新的设备环境,接下来被调用的函数就是DrvEnableSurface。 最后,GDI调用DrvStartDoc在一个新页上进行绘制。
GDI调用DrvDisableDriver优先于卸载打印机图形DLL。
如果打印机硬件支持GDI绘制函数不支持的绘制操作,则打印机图形DLL可以提供一个DrvDrawEscape函数。
如果需要支持通过GDI函数不能实现的绘制或非绘制的操作,则一个打印机图形DLL需要提供一个DrvEscape函数。 例如,微软的Postscript打印机驱动程序用escape来支持Postscript入口点,或者,一个应用程序也许需要获得一个传真机的电话号码,DrvEscape函数也用于指出由DrvDrawEscape函数支持的操作。
3.1.1.5支持设备字体
如果一个打印机提供设备字体,打印机图形DLL必须定义一个DrvTextOut函数以产生文本输出命令,图形DLL必须也定义以下函数:
DrvQueryAdvanceWidths
DrvQueryFont
DrvQueryFontData
DrvQueryFontTree
更多的关于支持设备字体的信息,参考第一部分第3章中的支持DDI字体及文本函数部分。
3.1.1.6返回专用的打印机信息
GDI有时请求打印机图形DLL返回在打印作业之间的打印机专用的信息,通过调用这些DrvQuery前缀的DDI函数如DrvqueryAdvanceWidths(如果被图形DLL定义)。
什么时候会发生这些的一个例子是在字处理应用程序中维持了一个所见即所得(WYSIWYG)屏幕显示的可打印页面。 为了准确地显示文本中的线的间断,字处理器必须对字符宽度及其他选定的打印机实现的字体的尺度进行基准线的合适性计算。
3.1.1.7选择用户模式或内核模式
在4.0版以前的Windows NT中,GDI及所有的图形驱动程序都是在用户模式下执行,在4.0版中,这些组件被移到内核模式,这一改变提高了显示设备的性能,但对打印设备的性能获得并不显著,不足以抵消继承用户模式失去的优点。
因此,对Windows 2000,可以设计一个打印机图形DLL以在内核模式下执行,或者可以设计在用户模式下执行。 用户模式下的打印机图形DLL执行提供以下超过内核模式下执行的优点:
■无限制的栈空间
■存取Win32 API
■更少的导致系统崩溃的潜在因素
■用户模式的纠错器,更容易纠错
■更好的浮点计算能力,尽管DDI浮点函数不是必需的
■调用任何定制的,厂商提供的用户模式的但不是Windows 2000打印机驱动程序体系结构中描述的部分的DLL的能力。
在用户模式下使用图形DDI
一个用户模式打印机图形DLL并没有限定于去调用GDI支持的服务(第一部分第2章)和其他以Eng为前缀的图形DDI回调函数。 但是,有一些必须遵从的规则:
■如同内核模式图形DLL,用户模式图形DLL必须调用图形DDI的版本函数以创建或修改绘制表面,这些回调函数是GDI支持的服务,并且不允许调用这些绘制函数在Win32中的等效的函数。
对用户模式的DLL,调用这些绘制的回调函数被用户模式的GDI客户端截听,之后,客户端会通知GDI的内核模式图形绘制引擎(GRE)。
■下面的以Eng为前缀的DDI函数不能被用户模式的DLL调用。
EngCreatePath
EngCreateClip
EngDeleteClip
EngGetTypeFontList
EngMapModule
EngDebugBreak
所有其他以Eng为前缀的函数对内核模式的打印机图形DLL都是可用的,在用户模式下也是也用的。 对那些与Win32完全等效的函数,如EngGetPrinter或EngGetPrinterDriver,则Win32等效的函数被调用。 没有完全与Win32等效的函数,则在GDI客户端来实现。
■用户模式的打印机图形DLL可以继续用DDI函数以实现对浮点的支持。
将一个存在的打印机图形DLL转换到用户模式
如果以前开发过运行于内核模式的打印机图形DLL,对Windows 2000来说,会有以下选择:
1.什么也不做。 已经存在的内核模式的打印机图形DLL将会继续在内核模式下工作,微软也许会在以后的操作系统发行版本中删除对内核模式打印机图形DLL的支持。
2.转换该DLL到用户模式下执行。 为了执行,只需增加一个DrvQueryDriverInfo函数到DLL,然后遵从构建打印机图形DLL的规则。
在创建一个新的在用户模式下的打印机图形DLL
当开发一个新的执行在用户模式下的打印机图形DLL时,可以继续使用所有的被内核模式DLL使用的图形DDI函数。 但是,也有下边这些选择:
■对以Eng为前缀有完全等效Win32的函数,可以调用Win32函数,这些函数由以下一些函数构成:
EngAllocMem EngGetForm EngMulDiv
EngAllocUserMem EngGetLastError EngSetLastError
EngEnumForms EngGetPrinter EngSetPrinterData
EngFreeMem EngGetPrinterData EngUnloadImage
EngFreeUserMem EngGetPrinterDriver EngWritePrinter
EngFindImageProAddress EngLoadImage
■对以Eng为前缀的那些与Win 32函数符合一些简单功能的函数,可以调用Win32函数,这些函数由以下一些函数组成:
EngAcquirSemaphore EngGetCurrentCodePage
EngMultiByteToWideChar EngCreateSemaphore
EngGetDriverName EngQueryLocalTime
EngDeleteSemaphore EngGetPrinterDataFileName
EngReleaseSemaphore EngFindResource
EngLoadModule EngUnicodeToMultiByteN
EngFreeModule EngMultiByteToUnicodeN
EngWideCharToMultiByte
■对那些创建或修改一个绘制服务的函数,新的驱动程序必须继续调用GDI支持服务而不是它们等效的Win32函数。
■不使用DDI函数来支持浮点,可以使用FLOAT数据类型。
3.1.1.8构建一个打印机图形DLL
当构建一个打印机图形DLL时,必须清楚打算在用户模式下执行和打算在内核模式下执行的DLL之间的以下差别:
构建打印机图形DLL的规则
用户模式图形DLL 内核模式图形DLL
在source文件中,设置TARGETTYPE=DYNLINK
在source文件中,设置TARGETTYPE=GDI_DRIVER
处理器宏USERMODE_DRVER必须在winddi.h包含之前在source文件中被定义
处理器宏USERMODE_DRIVER不能被定义
对象模块必须被与umpdddi.lib和gdi32.lib导入库链接在一起
对象模块必须与win32k.lib导入库链接
DrvQueryDriverInfo函数必须返回DRVQUERY_USERMODE
DrvQueryDriverInfo函数必须给DRVQUERY_USERMODE返回FALSE(或二者选择其一,该函数可以被省略)
3.1.2打印机接口DLL
本部分提供以下主题:
■3.1.2.1打印机接口DLL介绍
■3.1.2.2由打印机接口DLL定义的函数
■3.1.2.3打印机接口DLL实例
■3.1.2.4为打印机创建原型表单页面
3.1.2.1打印机接口DLL介绍
打印机通常给用户提供大量的可修改配置的选择,这些选择项可以在每一个被付印的文档中被改变。 如页面、托架、字体选择、图像精度、尺寸、色彩等等都必须可以通过用户接口被存取,也可以被应用程序调用。
一个打印机驱动程序的打印机接口DLL,它是执行在用户模式下,负责导出一个用户的接口到打印机的配置选项。 提供这一接口包括为打印机创建原型表单页面。 应用程序(如打印折叠器)通过调用由打印假脱机导出的Win32函数显示接口,并且,假脱机返过来,又调用由打印机接口DLL定义的函数。
为配置选项提供一个用户接口不是打印机接口DLL的唯一责任,DLL同时也导出假脱机可以调用的函数以通知驱动程序打印相关的系统事件,如驱动程序安装、升级或者添加打印机及打印机连接等。
3.1.2.2由打印机接口DLL定义的函数
打印机接口DLL导出函数列于下面表格中:
函数 目的
DllEntryPoint
初始化DLL的入口点,通常调用DLLMain(在平台的SDK文档中有描述)
DrvConvertDevMode
转化专用的DEVMODE结构到另外一个版本
DrvDeviceCapabilites
返回请求的关于一个打印机能力的信息
DrvDevicePropertySheets
调用CPSUI以产生描述打印机属性的表单页面属性
DrvDocumentEvent
(可选)允许打印机接口DLL去处理与打印文档相关的一定的事件
DrvDriverEvent
(可选)允许打印机接口DLL对假脱机来的已经发生的关于特定打印机事件的消息通知作响应
DrvDocumentPropertySheets
调用CPSUI以产生描述打印文档属性的表单页面属性
DrvPrinterEvent
允许打印机接口DLL对假脱机来的已发生的关于特定打印机的事件的消息通知作响应
DrvQueryColorProfile
(可选)允许打印机接口DLL指定一个ICC的profile以在色彩管理中使用
DrvQueryJobAttributes
(可选)允许打印机接口DLL指定诸如打印多个文档页面在一个物理页上(“N-up”打印)、打页某一页面的多个拷贝以及校对页面等
DrvQueryPrintEx
决定一个打印作业是否能用打印机当前配置进行打印
DrvUpgradePrinter
(可选)当一个新版本的驱动程序增加到系统时,修改打印机的注册表设置
3.1.2.3打印机接口DLL实例
plotui.dll的源代码,即微软绘图仪驱动程序的打印机接口DLL与此DDK一起提供,其代码放置于包含DDK示例代码的目录树的子目录中。
3.1.2.4为打印机创建原型表单页面
打印机接口DLL,与CPSUI连在一起,负责创建Windows 2000用户从事的与打印机及打印文档相关的视图及修改配置参数表单页面的属性。 每一个打印机接口的DLL必须提供一个DrvDevicePropertySheets函数以创建特定打印机页面和一个DrvDocumentPropertySheets函数以创建特定文档的页表。
为了理解这些函数是怎样被设计,阅读描述CPSUI、显示表单页面属性包括应用程序之间的交互、打印假脱机、打印机接口DLL以及CPSUI执行流等许多在打印机驱动程序中使用CPSUI一章中描述的内容是非常重要的。
3.1.3打印机数据文件
微软提供的打印机驱动程序利用数据文件来描述各个打印设备,更多的信息,可以参考以下几章内容:
■微软统一打印机驱动程序:描述了.gpd文件
■微软Postscript打印机驱动程序:描述了.ppd文件
■微软绘图仪打印机驱动程序:描述了.pcd文件