


Windows 打印管理在Linux 平臺(tái)實(shí)現(xiàn)
1.理解 Windows 打印機(jī)管理和 Linux 打印機(jī)管理
Windows 平臺(tái)提供了非常簡(jiǎn)單而且完善的打印機(jī)管理系統(tǒng)。在 Windows 編程中,打印功能被融入了 GDI (Graphic Device Interface)模塊。在 GDI 模塊中,程序員只要調(diào)用 EnumPrinters() 等 APIs 就可以輕松獲取打印機(jī)信息。Windows 的這種成熟打印管理機(jī)制很大程度上得益于打印機(jī)供應(yīng)商所提供的完善的打印機(jī)驅(qū)動(dòng)。Windows 的打印機(jī)驅(qū)動(dòng)屏蔽了打印機(jī)的具體打印實(shí)現(xiàn)細(xì)節(jié),同時(shí)為上層調(diào)用提供了簡(jiǎn)單的 API 接口。
與 Windows 平臺(tái)相反,打印機(jī)管理機(jī)制在 Linux 平臺(tái)上從產(chǎn)生到成熟卻經(jīng)過(guò)了一個(gè)漫長(zhǎng)的過(guò)程。Linux 打印系統(tǒng)最早源于 UNIX 打印系統(tǒng),但 UNIX 系統(tǒng)卻一直缺乏統(tǒng)一的標(biāo)準(zhǔn)接口。由于歷史原因,不同 UNIX 平臺(tái)使用著不同的打印系統(tǒng)。在各種 UNIX 打印解決方案中,最流行的是 Berkeley 打印系統(tǒng)和 System V 打印系統(tǒng)。一方面,不同打印系統(tǒng)需要不一樣的打印驅(qū)動(dòng)支持;另一方面,UNIX 只擁有相對(duì)較小的客戶群。這些因素使得很多打印機(jī)供應(yīng)商完全放棄了對(duì) UNIX 平臺(tái)的支持。統(tǒng)一打印接口的缺乏和底層驅(qū)動(dòng)的不完善使打印在很長(zhǎng)一段時(shí)間內(nèi)成為了 Linux 平臺(tái)的一大功能漏洞。
最終 CUPS (Common UNIX Printing System)的出現(xiàn)解決了上述窘境。CUPS 是UNIX/Linux 上通用的打印系統(tǒng)。CUPS 提供了一套 CUPS API 來(lái)完成 UNIX/Linux系統(tǒng)和打印機(jī)之間的交互。例如,用戶可以通過(guò) CUPS 獲取打印機(jī)的信息,也可以通過(guò) CUPS 設(shè)置打印機(jī)。CUPS 提供了對(duì) Berkeley 和 System V 打印命令的支持,這種兼容性使得之前的系統(tǒng)不用進(jìn)行大規(guī)模修改就可被延續(xù)使用。同時(shí),CUPS 還提供一系列模塊化的過(guò)濾接口。通過(guò)這些接口,打印機(jī)提供商只需要開(kāi)發(fā)一個(gè)驅(qū)動(dòng)程序就可以滿足所有平臺(tái)的需求。至今為止,CUPS 已被所有 UNIX 和 Linux 平臺(tái)所支持。
2.打印機(jī)管理移植架構(gòu)
打印機(jī)管理移植是應(yīng)用程序跨平臺(tái)移植的重要組成部分。不同平臺(tái)所支持的打印接口是不同的,因此移植的核心就是實(shí)現(xiàn)平臺(tái)之間的打印機(jī)管理接口的轉(zhuǎn)換。下圖展示了打印機(jī)管理移植的架構(gòu)。
Windows 提供了一系列 API 來(lái)獲取打印機(jī)信息。這些信息被封裝在預(yù)定義的Windows 標(biāo)準(zhǔn)結(jié)構(gòu)中,比如 DEVMODE,PRINTER_INFO_2,PRINTER_INFO_4 等等。Linux 使用 CUPS 來(lái)獲取打印機(jī)信息,這些信息被封裝在 cups_dest_t,ipp_attribute_t 等數(shù)據(jù)結(jié)構(gòu)中。只要正確獲取 Linux 平臺(tái)上打印機(jī)信息,并把它們轉(zhuǎn)化成 Windows 打印機(jī)數(shù)據(jù)結(jié)構(gòu),就可以完成打印機(jī)管理。
3.CUPS 基礎(chǔ)知識(shí)
CUPS 是 UNIX/Linux 平臺(tái)上的打印系統(tǒng)。CUPS 的定義和實(shí)現(xiàn)是基于 IPP(Internet Printing Protocol)協(xié)議的。IPP 是通用的打印系統(tǒng)標(biāo)準(zhǔn),它的功能和操作被一系列RFC(Request for Comments)所詳細(xì)定義。這些具體功能和操作包括:建立 IPP請(qǐng)求,應(yīng)答 IPP 請(qǐng)求和設(shè)置 IPP 請(qǐng)求等等。和 IPP 相關(guān)的 RFC 包括 RFC1179,RFC2910,RFC2911,RFC3196 等等。在網(wǎng)絡(luò)協(xié)議中,IPP 位于 HTTP(Hyper-Text Transport Protocol)協(xié)議之上。因此以下代碼示例將涉及到很多 IPP 和 HTTP的系統(tǒng)調(diào)用,例如 ippAddString() 和 httpConnectEncrypt() 等等。此外,在UNIX/Linux 平臺(tái)上在使用 CUPS 之前要提前引入下列頭文件:
#include <cups/cups.h>#include <cups/language.h>#include <cups/http.h>#include <cups/ipp.h> |
有了上述 CUPS 基礎(chǔ)知識(shí),下文將舉例說(shuō)明使用 CUPS 實(shí)現(xiàn)打印機(jī)管理移植的技術(shù)細(xì)節(jié)。
4.獲取打印機(jī)數(shù)量
Windows 通過(guò) API EnumPrinters() 的返回參數(shù) pcReturned 來(lái)獲取系統(tǒng)的打印機(jī)數(shù)量。Windows 程序的具體實(shí)現(xiàn)如下所示:
int n_PrinterCount;EnumPrinters( , , , , , , &n_PrinterCount); |
在 Linux 中,CUPS 函數(shù) cupsGetDests() 可實(shí)現(xiàn)同樣的功能。需要注意的是,在調(diào)用結(jié)束后,調(diào)用者需要使用 cupsFreeDests() 來(lái)釋放內(nèi)存。
cups_dest_t *dests; int n_PrinterCount = cupsGetDests( &dests ); cupsFreeDests(count, dests); |
5.獲取打印機(jī)名稱(chēng),打印機(jī)端口和打印機(jī)型號(hào)
Windows使用API EnumPrinters() 來(lái)獲取打印機(jī)名稱(chēng),打印機(jī)端口和打印機(jī)型號(hào)。詳情請(qǐng)參考Windows MSDN。在Linux平臺(tái)上,CUPS可實(shí)現(xiàn)同樣的功能。具體實(shí)現(xiàn)流程如下圖所示:
圖2 獲取打印機(jī)信息(名稱(chēng),端口,型號(hào))流程
5.1建立 HTTP 連接
使用 CUPS 獲取打印機(jī)名稱(chēng),打印機(jī)端口和打印機(jī)型號(hào)信息首先需要開(kāi)啟 IPP 和HTTP 服務(wù)。開(kāi)啟服務(wù)的第一步是建立一個(gè) HTTP 連接來(lái)和 CUPS 服務(wù)器取得聯(lián)系。在下面的代碼中,cupsServer() 將返回指向默認(rèn) CUPS 服務(wù)器名稱(chēng)的指針;ippPort() 將返回 IPP 請(qǐng)求的默認(rèn)端口號(hào);cupsEncryption() 將返回當(dāng)前 CUPS 請(qǐng)求的默認(rèn)加密設(shè)置。將這些返回值作為參數(shù)傳遞給函數(shù) httpConnectEncrypt() 就可以建立一個(gè) HTTP 連接。如果 HTTP 連接建立成功,即 httpConnectEncrypt() 的返回值pHTTPConnection 有效,那么就可以基于這個(gè)連接進(jìn)行下一步 IPP 請(qǐng)求。
http_t *pHTTPConnection = httpConnectEncrypt( cupsServer(), ippPort(), cupsEncryption() ); if (!pHTTPConnection) { g_print("Cannot connect to CUPS server\n"); return 0; } |
5.2建立 IPP 請(qǐng)求
建立一個(gè)新的 IPP 請(qǐng)求是通過(guò) IPP 調(diào)用 ippNew() 來(lái)實(shí)現(xiàn)的。在此,operation_id 被設(shè)置為 CUPS_GET_PRINTERS,其語(yǔ)義是當(dāng)前 IPP 請(qǐng)求要獲取和打印機(jī)相關(guān)的信息。同時(shí),request_id 被設(shè)置為 1,這是 IPP 協(xié)議所規(guī)定的。
ipp_t *pIPPReq = ippNew(); pIPPReq->request.op.operation_id = CUPS_GET_PRINTERS; pIPPReq->request.op.request_id = 1; |
5.3設(shè)置 IPP 請(qǐng)求
以下是進(jìn)一步設(shè)置當(dāng)前 IPP 請(qǐng)求 pIPPReq 的細(xì)節(jié)。需要指出的是,在和 CUPS 服務(wù)器進(jìn)行交互的過(guò)程中,很多信息是通過(guò)字符串來(lái)傳遞的。這就涉及到了文字語(yǔ)言編碼表示的問(wèn)題。函數(shù) cupsLangDefault() 就是用來(lái)獲取 CUPS 服務(wù)器的默認(rèn)語(yǔ)言設(shè)置。cupsLangDefault() 的返回值 pDefLang 還將作為參數(shù)傳遞給其它函數(shù)來(lái)完成對(duì) IPP 請(qǐng)求的進(jìn)一步設(shè)置。
根據(jù) IPP 協(xié)議,對(duì) IPP 請(qǐng)求的設(shè)置要從設(shè)置參數(shù) "attributes-charset"(字符集)和"attributes-natural-language"(自然語(yǔ)言)開(kāi)始。下列代碼分別用系統(tǒng)默認(rèn)字符集和CUPS 默認(rèn)語(yǔ)言來(lái)設(shè)置這兩個(gè)參數(shù)。完成這兩項(xiàng)規(guī)定設(shè)置后,用戶就可以根據(jù)需求對(duì)需要的信息提出請(qǐng)求。此處需要獲得的信息是打印機(jī)名稱(chēng),端口號(hào)和打印機(jī)型號(hào)。在 IPP 協(xié)議中,這三項(xiàng)對(duì)應(yīng)的IPP請(qǐng)求關(guān)鍵字分別是 "printer-name", "device-uri" 和 "printer-make-and-model"。下列代碼定義了數(shù)組 pReqAttrs 來(lái)存儲(chǔ)上述關(guān)鍵字,然后通過(guò)請(qǐng)求參數(shù) "requested-attributes" 來(lái)設(shè)置這些 IPP 請(qǐng)求。
cups_lang_t *pDefLang = cupsLangDefault(); if (!pDefLang) { g_print("Cannot get default language\n"); return 0; } ippAddString(pIPPReq, IPP_TAG_OPERATION, IPP_TAG_CHARSET, "attributes-charset", NULL, cupsLangEncoding(pDefLang)); ippAddString(pIPPReq, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, "attributes-natural-language", NULL, pDefLang->language);static const char *pReqAttrs[] = {"printer-name", "device-uri","printer-make-and-model"}; ippAddStrings(pIPPReq, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "requested-attributes", 3, NULL, pReqAttrs); |
5.4發(fā)送 IPP 請(qǐng)求
設(shè)置好 IPP 請(qǐng)求之后,通過(guò)函數(shù) cupsDoRequest() 就可以把指定IPP請(qǐng)求發(fā)送到服務(wù)器端。如果請(qǐng)求發(fā)送成功,那么請(qǐng)求發(fā)送方將得到有效的IPP應(yīng)答pIPPRes。需要指出的是,即使 IPP 應(yīng)答有效,也并不意味著所有 IPP 請(qǐng)求的內(nèi)容都得到了正確的回復(fù)。還需要進(jìn)一步檢查 IPP 應(yīng)答的狀態(tài)代碼 "request.status.status_code" 來(lái)核實(shí)反饋信息的有效性。
ipp_t *pIPPRes = cupsDoRequest(pHTTPConnection, pIPPReq, "/"); if (!pIPPRes) { g_print("No response from CUPS server\n"); return 0; } if (pIPPRes->request.status.status_code > IPP_OK_CONFLICT) { printf("IPP Error: %s\n", ippErrorString(pIPPRes->request.status.status_code)); ippDelete(pIPPRes); return 0; } |
5.5獲取 IPP 應(yīng)答
如果上述操作都成功返回,就可以進(jìn)一步從 pIPPRes 結(jié)構(gòu)中提取感興趣的信息。在下列代碼中,變量 pPrinterName,pPortName 和 pPrinterModel 分別用來(lái)存儲(chǔ)打印機(jī)名稱(chēng),打印機(jī)端口號(hào)和打印機(jī)的類(lèi)型信息。通過(guò)依次枚舉 IPP 應(yīng)答 pIPPRes 來(lái)尋找屬性 pAttr->name 為 "printer-name" 或 "device-uri" 或 "printer-make-and-model" 的分量,就可以得到上述信息。
char *pPrinterName = NULL; char *pPortName = NULL; char *pPrinterModel = NULL; for (ipp_attribute_t *pAttr = pIPPRes->attrs; pAttr != NULL; pAttr =pAttr->next) { if (pAttr->group_tag == IPP_TAG_PRINTER) { if (0 == strcmp(pAttr->name, "printer-name")) pPrinterName = pAttr->values->string.text; if (0 == strcmp(pAttr->name, "device-uri")) pPortName = pAttr->values->string.text; if (0 == strcmp(pAttr->name, "printer-make-and-model")) pPrinterModel = pAttr->values->string.text; } } |
5.6釋放內(nèi)存
最后,需要釋放相關(guān)內(nèi)存以免內(nèi)存泄露:
httpClose(pHTTPConnection); ippDelete(pIPPRes); |
6字符編碼轉(zhuǎn)換
在實(shí)現(xiàn)打印機(jī)管理的移植過(guò)程中,還需要特別注意字符編碼轉(zhuǎn)換的問(wèn)題。當(dāng)然,字符編碼問(wèn)題不僅僅局限于本文所探討的范疇,它同時(shí)還是所有應(yīng)用程序移植都需要特別關(guān)注的技術(shù)細(xì)節(jié)。以本文為例,在 Linux 上獲取的字符串,比如打印機(jī)名稱(chēng),通常是 UTF-8(Unicode Transformation Format) 編碼的。而 Windows 應(yīng)用程序并不使用 UTF-8 編碼。由于歷史原因,Windows 程序或使用 ANSI 編碼方式,或使用UTF-16 編碼方式。因此,從 CUPS 獲取的字符串還需要根據(jù)程序運(yùn)行環(huán)境進(jìn)行編碼轉(zhuǎn)換,之后才能被 Windows 應(yīng)用程序使用。字符編碼轉(zhuǎn)換可以使用 IBM ICU(International Components for Unicode)來(lái)完成。詳情請(qǐng)參考 http://www-306.ibm.com/software/globalization/icu/index.jsp
7.結(jié)束語(yǔ)
移植是一項(xiàng)實(shí)現(xiàn)應(yīng)用程序跨平臺(tái)運(yùn)行的核心技術(shù)。本文重點(diǎn)闡述了 Windows 打印機(jī)管理系統(tǒng)在 Linux 平臺(tái)上的移植技術(shù)。通過(guò)具體舉例,本文詳細(xì)分析了打印機(jī)管理在 Linux 平臺(tái)上的移植細(xì)節(jié)。本章涉及的移植架構(gòu)和移植概念不僅僅局限于打印機(jī)管理移植。這些概念和思想也是所有應(yīng)用程序所通用的。應(yīng)用程序跨平臺(tái)移植已經(jīng)在很多軟件中被應(yīng)用。實(shí)現(xiàn)應(yīng)用程序在不同平臺(tái)上無(wú)縫隙的運(yùn)行操作也將是每位移植技術(shù)人員的共同目標(biāo)。
參考資料
- "CUPS Software Programmers Manual", Easy Software Products, Copyright 1997-2004
- "CUPS Implementation of IPP", Easy Software Products, Copyright 1997-2004
- RFC2910 "Internet Printing Protocol/1.1: Encoding and Transport", 2000
- RFC2911 "Internet Printing Protocol/1.1: Model and Semantics", 2000
- RFC3196 "Internet Printing Protocol/1.1: Implementor's Guide", 2001
- MSDN: Microsoft Developer Network
- http://www-306.ibm.com/software/globalization/icu/index.jsp
關(guān)鍵字:管理、平臺(tái)、打印
新文章:
- CentOS7下圖形配置網(wǎng)絡(luò)的方法
- CentOS 7如何添加刪除用戶
- 如何解決centos7雙系統(tǒng)后丟失windows啟動(dòng)項(xiàng)
- CentOS單網(wǎng)卡如何批量添加不同IP段
- CentOS下iconv命令的介紹
- Centos7 SSH密鑰登陸及密碼密鑰雙重驗(yàn)證詳解
- CentOS 7.1添加刪除用戶的方法
- CentOS查找/掃描局域網(wǎng)打印機(jī)IP講解
- CentOS7使用hostapd實(shí)現(xiàn)無(wú)AP模式的詳解
- su命令不能切換root的解決方法
- 解決VMware下CentOS7網(wǎng)絡(luò)重啟出錯(cuò)
- 解決Centos7雙系統(tǒng)后丟失windows啟動(dòng)項(xiàng)
- CentOS下如何避免文件覆蓋
- CentOS7和CentOS6系統(tǒng)有什么不同呢
- Centos 6.6默認(rèn)iptable規(guī)則詳解