從需求出發,咱們的目的是在電腦上提供一個虛擬打印機,而後讓用戶選擇這個虛擬機打印時產生的中間文件被攔截下來,以後進行進一步處理後在執行真實的打印。數據庫
首先附上查找Windows打印相關內容的連接,這個分類下包含了Windows打印的方方面面windows
https://msdn.microsoft.com/en-us/library/windows/hardware/ff561035(v=vs.85).aspx緩存
Windows2000之後的打印體系結構都是由一個打印機假脫機程序(Spooler)和一系列的打印驅動組成。應用程序經過調用設備無關的函數,就能建立打印任務,併發送到打印設備中。包括激光打印機、矢量繪圖機、光柵打印機和傳真機。網絡
其中打印驅動包括一個渲染組件和一個配置組件。併發
渲染組件負責將應用程序傳來的每一頁的繪製命令(GDI命令)轉換成打印機用來渲染的命令數據(打印機才能識別的命令)發送到打印機中。ide
配置組件又包含一個可讓用戶進行打選項配置的用戶接口組件和一個將打印機的配置和特徵傳遞給應用程序的程序接口。函數
當GDI程序執行打印時,經過調用API來傳遞GDI繪圖指令到繪圖引擎,繪圖引擎要麼和打印驅動一塊兒合做來緩存這些繪製指定到一個EMF文件中,要麼直接渲染成一個可打印的圖片發送到spooler中。Spooler解釋EMF文件,並將頁面佈局和做業控制指令信息插入到數據流中,而後發送這些數據裏到序列化、並行化或者網絡形式的打印機關聯的端口上。(XPS設備會有一點不一樣,這裏不進行介紹)。佈局
因爲Spooler和打印驅動都是能夠被單獨取代,因此硬件廠商們能夠很容易的增長對新硬件的支持。當須要增長對新款打印機的支持時,一般只須要建立根據微軟所提供的打印驅動類型中相關聯的數據類型就能夠了。字體
下圖是Windows提供的內置打印驅動程序:操作系統
大體瞭解了Windows打印體系組成以後,來分別看一下Spooler和打印驅動。
從Windows2000開始,打印假脫機程序由一系列的微軟提供的和可選的渲染組件組成,他們的做用包括:
一、檢測是否打印任務是在本地處理仍是跨網絡處理。
二、接受GDI和打印驅動爲特定類型的打印機所提供的數據流。
三、緩衝繪製數據到文件中。
四、從邏輯打印隊列中選出第一個有效的物理打印機。
五、將緩衝的數據流(如EMF)轉換成能唄打印機硬件所識別的格式(如PCL)。
六、發送打印數據流到打印機硬件中。
七、爲假脫機組件和打印機的相關信息維護一個基於註冊表的數據庫
Spooler主要組成結構以下圖所示:
Application經過調用GDI函數來建立打印任務,經過調用Winspool.drv提供的API接口,將打印內容路由到PrintProvider中。
PrintProvider負責管理本地打印和遠程打印,同時要管理打印任務堆裏的啓動、中止和枚舉打印隊列。
咱們這裏只討論本地打印流程,它提供了下面的能力:
一、打印任務緩衝和解析到打印隊列
二、爲Win2000之後的操做系統的打印驅動體系提供支持。
三、爲廠商提供的打印處理器的提供支持
四、爲場上提供的打印監視器的提供支持
下圖提供了本地打印任務處理流程:
如圖所示,應用程序經過GDI接口建立打印任務後,不論是否須要輸出爲EMF,本地的PrintProvider任務建立API都會建立一個spool文件。而後,當任務被調度的時候,經過讀取這個spool文件,若是是EMF格式的話,就讓EMF打印處理器配合打印機的渲染驅動,將打印任務發送回去給GDI轉換成RAW格式,最後和沒有使用EMF格式的任務同樣,將數據流傳遞到端口監視器中執行最終打印。
咱們經過定製本身的打印機,讓整個打印流程走如上圖中紅線描述的路徑,在打印處理器這一層攔截spool文件及其相關打印信息,來保留整個打印任務的相關數據,等待後續進行處理。
Windows提供了三種類型的打印驅動,分別爲:Universal Printer Driver、PostScript Printer Driver、Plotter Driver。原則上來講這三種類型的驅動已經能支持大多數打印機了,咱們只須要簡單的爲新的打印機提供對應驅動的DataFile便可。咱們這裏只討論Universal Printer Driver。
Universal Printer Driver由三部分組成:
一、Printer Graphics DLL: 負責和GDI一塊兒渲染打印任務,併發送渲染數據流到打印假脫機程序中。
二、Printer Interface DLL: 提供打印機參數配置接口和假脫機能調用的用於通知打印系統事件的接口。
三、Printer Data Files:對於Universal Printer Driver而言,這個數據文件就是GPD文件,它用於建立UnidrvMiniDrivers,主要用於描述打印機的可選項配置。包括打印機屬性、相關命令、特徵、可選項、字體描述、環境狀態等。
上面三個模塊對應的就是下圖中紅色矩形框住的部分。
因爲咱們不須要對打印渲染和用戶接口作過多的定製,只須要使用標準的Windows打印首選項對話框,因此無需自定義渲染插件和用戶接口插件。固然若是須要的話,也是能在https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff547298(v=vs.85).aspx中找到這些插件重定義的方式。
根據需求,咱們只須要本身定製GPD文件,來實現對系統標準的打印機首選項對話框相關設置的定製。(這種方式應該就是UnidrvMiniDrivers)。
綜上所述,咱們的虛擬打印機要本身定製的模塊就只有打印處理器和GPD文件。
打印處理器負責攔截打印生成的中間文件,GPD文件負責定製打印可選項。
打印處理器定製文檔:https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff563807(v=vs.85).aspx
GPD文件說明文檔:https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff551750(v=vs.85).aspx
WDK提供了支持相關示例,咱們在這個示例的基礎上直接更改,主要要更改的地方是在winprint.cpp中,PrintDocumentOnPrintProcessor函數的實現中。咱們能夠看到打印處理器對一個打印任務的處理流程以下:
因爲咱們這裏的目的是攔截打印任務產生的中間文件保存下來,因此只須要調用GDI Functions in Print Processors,緩存好spool文件後直接返回(阻止傳遞數據流到spooler中去)。
前面介紹了,GPD文件是使用GPD語言去描述一臺打印機,也就是說GPD文件有本身固定的格式,對應GPD語言也有固定的語法,其中主要包括下列信息:
一、Printer attributes: 描述打印機特徵
二、Printer commands: 用於控制打印機的操做
三、Printer features: 描述能被通用打印驅動所控制的能力
四、Printer options: 呈現能用來設置Printer features值的狀態。
五、Printer font descriptions :描述和硬件相關連的字體
六、Conditional statements: 描述Printer attributes 和 打印機的配置之間的依賴關係。
此外,GPD語言也定義了一些用來控制一些操做的GPD文件設置,這種操做咱們暫不須要。
根據上面的描述,要徹底自定義一個GPD文件相對來講是比較困難的,這裏咱們能夠經過參考其餘打印機中有使用GPD文件的部分,或者參考WDK目錄下\src\print\mini中的GPD文件示例。而後根據須要去修改相應的部分更加簡單。
我這裏只對本身所理解的部分進行闡述,更詳細的信息請參考MSDN。
首先GPD文件中最基本的值設置格式Entries,其格式爲:
其中的EntryName都是GPD解析器預約義好的關鍵字(否則GPD解析器沒法識別你寫這麼個關鍵字是要作什麼),而EntryValue則只能是GPD所支持的值類型中的一種(否則GPD解析器沒法判斷你這個關鍵字對應的內容正不正確)。關於類型,請自行參考https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff550568(v=vs.85).aspx
下面拿咱們的GPD文件做爲參考進行描述。
如圖所示,對於這種*AttributeName:AttributeValue格式的文本,都是前面描述的Printer Attribute,其中放在文件頭部,又更細分爲Root-Level-Only Attributes,其各部分含義可參見https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff561989(v=vs.85).aspx
如圖所示,上面*%的部分都是註釋,後面的Attribute,則是細分在Printer Capability Attributes下的一些屬性,詳細描述參見https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff560780(v=vs.85).aspx
如圖所示,形如*Command:CommandName{CommandAttributes}的內容,則屬於Printer Commands。前面描述過這類型是用於控制打印機的操做,其中CommandName都是預約義的命令名,具體這些命令名稱的含義,參見https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff546117(v=vs.85).aspx中各種別的描述。
如圖所示,形如*Feature:FeatureName{FeatureAttributes}的內容,描述的即是咱們打印機所提供的打印選項功能。Feature又分爲標準類型和自定義類型,固然咱們只須要提供標準的特徵,所支持的標準特種參見https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff562697(v=vs.85).aspx,如截圖中所示即爲標準特徵中容許選擇紙張方向的特徵,這裏不只僅描述了這個特徵的說明,並且描述了這個特徵是否是必須的和支不支持自定義選項。
須要說明的是*rcNameID這個屬性段,表述的是對應特徵(或者可選項)顯示在界面上的內容的ID,MSDN上也描述了可使用*Name來直接指定內容。可是咱們不須要本身指定。Unidrv驅動提供的stdname.gpd中包含了標準特徵顯示的文本的ID,咱們只須要引用其中的就能夠的。
另外補充說明一下上面Feature中的Option,前面描述了Option對應的實際上是Feature能夠設置的選項的描述,對應的每個選項也有固定的格式,能夠參見https://msdn.microsoft.com/zh-cn/library/windows/hardware/ff559622(v=vs.85).aspx
對應上面Feature描述的界面效果以下:
相似的對應的*Feature:PaperSize產生的界面效果以下:
至此,關於GPD文件的描述就結束了。具體須要添加某項特徵選項時,可在MSDN上找到對應選項的關鍵字段,而後根據描述設置相應的屬性便可。
打印處理器緩衝spool文件相關資料http://www.undocprint.org/winspool/spool_files