程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> Windows下實時行式打印系統設計

Windows下實時行式打印系統設計

編輯:關於VC++

眾所周知,Windows系統的頁式打印系統有許多好的特性,比如所見即所得、設備無關等等。但是,在一些實時性要求很高的工業控制系統中,需要將系統隨機出現的信息實時地打印出來,要求來一行打一行,而不能來一行打一頁,而Windows系統的頁式打印系統卻很難滿足這樣的應用需求。鑒於這個原因,為了滿足實時系統的打印要求,有必要設計一個新的實時行式打印系統。下面詳細介紹如何在Windows 2000/NT上實現這樣一個系統。

總體設計

實時系統的基本要求是實時性。本文采用以共享內存為中轉的打印假脫機技術,所有的打印操作在內存中提交完成,保證了應用系統對打印操作的及時響應。

另一要求是設備無關性,使提交打印的操作盡可能地做到與設備無關。程序不會因為打印機設備的不同,導致程序有較大的修改。如果打印機不同,只需加載相應的打印驅動程序即可。

總體上實時行式打印系統的設計分為兩個部分,一個是以內存為基礎的打印假脫機部分的設計;另一個是實時打印驅動層部分的設計。系統的概要設計圖如下:

實時行打印假脫機部分

這個部分的基本原理是使用一個基於共享內存技術的先入先出隊列,用來存儲其他應用程序提交的實時打印請求。打印進程從先入先出的環形隊列取出打印請求,經過必要的處理之後,提交給打印驅動部分,由打印驅動部分負責驅動相應的打印機輸出。

為滿足其他應用程序提交打印請求的需要,在此使用了Windows操作系統提供的共享內存技術。共享內存是進程之間通信時用的一種技術,是一種更為標准、更為核心的技術,而且它在不同操作系統平台之間的移植性也比較好(Unix系列操作系統也有這種技術)。另一個好處是提高了實時性能,因為避免了多次內存復制的系統空間和時間上的開銷。

Windows系統中與創建共享內存相關的系統函數有CreateFileMapping和MapViewOfFile。

第一個函數用來在系統中創建一塊共享內存,並返回共享內存的句柄。其參數說明如下:

HANDLE WINAPI CreateFileMapping (
HANDLE hFile,
LPSECURITY _ATTRIBUTES lpsa,
DWORD dwPROTECT,
DWORD dwMaxSizeHigh,
DWORD dwMaxSizeLow,
LPCSTR lpszMapName);

hFile為文件句柄,要創建共享內存,該參數必須為0xffffffff; lpsa為安全屬性結構指針; dwPROTECT是頁保護標識,如PAGE_READONLY,PAGE_READWRITE等; dwMaxSizeHigh和dwMaxSizeLow共同定義了共享內存的尺寸,分別為共享內存大小的高32位和低32位; lpszMapName定義了共享內存的名字,必須確保其在系統范圍內的惟一性。

第二個函數用來將創建的共享內存映射到調用進程的地址空間,並返回該地址空間的首地址。其參數說明如下:

MapViewOfFile(
HANDLE hFileMappingObject,
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh,
DWORD dwFileOffsetLow,
DWORD dwNumberOfBytesToMap);
hFileMappingObject定義了CreateFileMapping

函數返回的共享內存句柄; dwDesiredAccess定義了共享內存的訪問模式,如:FILE_MAP_ALL_ACCESS

等; dwFileOffsetHigh和dwFileOffsetLow共同定義了共享內存起始位置的偏移量,分別為該偏移量的高32位和低32位,通常情況下二者都為零值; dwNumberOfBytesToMap定義了映射到本進程地址空間的共享內存的字節數,如果該值為零,則映射所有的共享內存。

這裡定義實時打印系統所用共享內存的名字為g_szRealTimePrintSystemShareMemName。REALTIMEPRINT_DB是一個結構類型,定義了共享內存的內部結構,它是實時打印系統的數據核心,包括了要打印的信息、寫入指針、讀出指針等信息。 具體步驟如下(示意性代碼):

REALTIMEPRINT_DB *g_pRealTimePrint-
SystemDb ;
HANDLE hShareMemHandle = NULL;
DWORD dwRTPSShmLen = sizeof(REALTIMEPRINT_DB);
//REALTIMEPRINT_DB 結構的長度
hShareMemHandle =
//生成共享內存,並返回其句柄
CreateFileMapping((HANDLE)0xffffffff,NULL,PAGE_READWRITE,0,
dwRTPSShmLen, //共享內存的大小 “g_szRealTimePrintSystemShareMemName”);
g_pRealTimePrintSystemDb =
//將共享內存映射到本進程的地址空間
(REALTIMEPRINT_DB *)MapViewOfFile(hShareMemHandle,FILE_MAP_ALL_ACCESS,0,0,0);

在獲得了共享內存的地址後,任何調用進程都可以將要打印的信息寫到該共享內存中,供打印服務進程打印輸出。基於系統實時性和效率方面的考慮,使用環形先入先出隊列(FIFO),在此不再詳述。

通常情況下,將上述功能封裝成兩個類,生成動態連接庫供程序調用。一個類提供給需要提交打印信息請求的應用程序使用; 另一個類提供給響應打印請求的實時打印服務進程使用。在類中屏蔽以上算法細節和數據細節。

供提交打印信息請求的應用程序調用的類至少要提供如下接口操作:

class RealTimePrintSystemClnt
{//其他的數據和方法聲明
public:
BOOL IsWorking();
//打印機是否正在工作?
/*實時打印系統的FIFO隊列是否已滿?如果返回為TRUE,表示實時打印系統已經沒有空閒響應程序的打印請求,應用程序要等到該返回值為FALSE時,重發打印請求。*/
BOOL IsFull();
/*程序請求實時打印操作。pData 表示要打印的實時信息,由ASCII字符或者漢字字符組成的字符串。dwDataLen 是要打印的實時信息的字符長度。crColor 表示使用什麼顏色打印,如果使用彩色噴墨打印機將可以打印彩色,默認值為0,表示黑色。pPrinterName 表示該打印請求將在哪一個打印機上打印輸出,默認值為NULL,表示實時打印系統的默認打印機。返回值是實際提交給實時打印系統的信息長度,該值如果等於dwDataLen 則表示該請求已經完全提交成功,如果等於零值則表示該請求沒有被完整提交,應用程序要再次提交該請求。*/
DWORD Request(LPCSTR pData,DWORD dwDataLen,COLORREF crColor=0,LPCSTR pPrinterName=NULL);
//其他的數據和方法聲明
};

供響應打印請求的服務進程使用的類至少要提供如下操作:

class RealTimePrintSystemServ
{//其他的數據和方法聲明
//UnionPrintSystem是打印驅動層的封裝類,實時打印系統使用它來打印實時信息
UnionPrintSystem *m_pUnionPrintSystem;
public:
/*獲得當前FIFO隊列中的打印請求個數*/
DWORD GetRequstNum();
/*獲得一個打印請求的數據,pData得到數據的首地址,dwDataLen得到數據的長度,crColor 得到打印顏色,pPrinterName 得到執行請求打印機的名字。返回值為TRUE,表示成功,為FALSE表示失敗。*/
BOOL GetRequest(unsigned char ** pData,DWORD &dwDataLen,COLORREF &crColor,LPSTR pPrinterName);
/*實時打印系統的打印線程。在該打印線程中,要不斷檢測是否有打印請求,如果有打印請求,則取出打印請求,提交給打印驅動層,然後由打印驅動層驅動不同的打印機將請求在紙上打印出來。關於打印驅動層下面講述。*/
static DWORD WINAPI rtPrintThread(LPVOID lpParameter);
};

打印驅動部分

為了實現系統設備無關性的要求,設計了打印統一驅動層部分。這樣一旦打印機設備更改,應用程序只需加載新的打印機設備驅動即可,不會導致打印機系統程序和用戶程序的修改,使系統具有較好的穩定性、兼容性和可擴充性。

首先,定義統一驅動部分,該部分主要完成以下功能:加載相應的具體打印機驅動程序,並負責將請求打印的字符信息翻譯成不同打印機需要的打印點陣信息。

class UnionPrintSystem
{public:
GeneralPrinter *m_pPrinter;
//當前使用的打印機
GeneralPrinter *m_pDefaultPrinter;
//系統默認的打印機
GeneralPrinter *m_pPrinterSet[MAX_PRINTER_DRV_NUM];
//安裝的所有打印機
HZDotArrayFont *m_pHzDAF;
//漢字字符的打印點陣信息提取器,可以提取多種打印字體的點陣信息,如24點陣楷體,48點陣宋體等
public:
//加載一個打印機驅動程序
void LoadPrinterDriver(GeneralPrinter *p);
//加載一個獲取漢字打印點陣信息的驅動器
void LoadHzDAFDriver(HZDotArrayFont *p); //選擇一個工作打印機
void SelectWorkPrinter(char *szPrinterName); //打印機控制
void SetColor(int id) {m_pPrinter->SetColor(id);};
//其他打印機控制函數
/*打印一個字符串,可以包括漢字字符,只要調用該函數就可以將信息打印出來*/
int PrintString(char *str,int len,char * ColorName=“黑色”);
};

最後是打印機驅動程序的設計,這裡是設備無關性設計的關鍵所在。為滿足系統設備無關性的要求,設計了一個包含各種基本打印動作的打印機基類,所有的打印機都從該基類派生出來。在打印機的基類中定義了打印機的各種控制代碼、打印機的種種操作方法,在具體的打印機類中將它們實例化。代碼如下:

class GeneralPrinter
{public :
/*PRINTCOLOR是事先定義好的打印機顏色結構,用來定義打印機每一種顏色的控制代碼*/
PRINTCOLOR *m_pColor;
/*本打印機支持顏色的數量*/
int m_iPrtColorNum;
/*打印機是否支持漢字的直接打印,即該打印機本身是否帶有漢字的打印字庫,如果打印機不支持漢字的直接打印,漢字的打印點陣信息將由字庫點陣信息提取器類負責獲取。*/
BOOL m_bDirectPrintHz;
/*HPRINTHANDLE 是本系統事先定義好的打印機句柄結構,用來標識打印機的硬件端口,是數據輸出到打印機的通道。*/
PRINTHANDLE m_hPrinter;
char m_szPrinterName[PRINTR_PORTNAME_MAX_LEN];
/*打印機名字,用來區別打印機的惟一標志*/
PRINTERCONTROLCODE ...;
/*PRINTERCONTROLCODE 是本系統事先定義好的打印機控制代碼結構,用來控制打印機行為,如打印機初始化控制代碼,打印機設置頁長控制代碼等打印機支持的方法。*/
public:
/*szPort是打印機所接端口的端口名字,如‘com1’,‘/dev/bbp0’等*/
GeneralPrinter(char *szPort);
~GeneralPrinter();
virtual void ResetPrinter();
/*打印機初始化的方法*/
virtual void SetAutoCRLF(int iAuto); /*設置打印機是否自動換行的方法,iAuto為1表示自動換行,為0則不自動換行*/

/*如果是彩色打印機的話,下面兩個函數用來設置字符的打印顏色,szColorName是顏色的名字,如“黑色”,“紅色”等,但必須是打印機支持的顏色。iColorId 顏色的Id值一般為0~7之間的值。*/

virtual void SetColor(char *szColorName);
virtual void SetColor(int iColorId);
/*將打印機設置為圖形模式下的操作,iMode 表示不同的圖形模式,如180×180等*/
virtual void SetGraphMode(int iMode);
/*用來打印字符串(包括漢字字符)的方法,iStrLen 表示字符串長度*/
virtual int PrintString(char * szStr,int iStrLen);
/*用來打印圖形的點陣信息,pData存放圖形的點陣信息,比如漢字的打印點陣等*/
virtual int PrintGraphDotArray(char * pData,int iDataLen);
/*其他方法的聲明*/
};

驅動程序的編寫實例

要實現本系統的打印驅動,必須遵守本系統的規范,就是新的打印機驅動必須從基類GeneralPrinter派生出來,然後再將各種數據實例化,這樣才能保證設備的無關性。下面以佳能的BJC5500彩色噴墨打印機為例說明驅動程序的編寫方法。

class Printer_BJC5500 : public GeneralPrinter {
public:
PRINTCOLOR m_AllColor[BJC5500_PRT_
COLOR_NUM];
public:
Printer_BJC5500 (char *szPort);
~Printer_BJC5500 ();
virtual int PrintGraphDotArray(char * pData,int iDataLen);
};
//只需要在構造函數中將各種數據實例化即可
Printer_BJC5500 ::Printer_BJC5500 (char *szPort){
strcpy(szPrinterName,“BJC5500”);
//賦予該打印機的惟一標志名字
m_iPrtColorNum = 7;
//BJC5500能夠支持7種顏色
m_pColor = &m_AllColor[0];
//獲得BJC5500打印機顏色控制代碼的首地址
m_bDirectPrintHz=FALSE;
//BJC5500不帶漢字的硬字庫,因而不支持漢字的直接打印,需要提取漢字的打印點陣信息,然後在打印機的圖形模式下進行漢字的打印
int i=0;
int cslen = 6;
//黑顏色的打印控制代碼
m_AllColor[i].szColorName =“黑色”;
//顏色的名字
m_AllColor[i].iControlStrLen =cslen;
//打印控制代碼的長度
m_AllColor[i].szControlStr =“\x1b\x5b\x4d\x01\x00\x30”;
//打印控制代碼
i++;
//其他的顏色控制代碼和其他打印機行為的控制代碼
m_pResetPrintStream.cs=“\x1b\x43\x1\x1b\x41\x1”;
//初始化打印機的控制代碼
m_pResetPrintStream.len=6;
//設置圖形模式等其他功能的打印機控制代碼
}

如果該打印機的某些打印行為比較特殊,在此只需要重載相應的虛函數,比如BJC5500的打印圖形的點陣信息比較特殊,在此重載了PrintGraphDotArray,限於篇幅,不再給出其實現。

通過以上代碼,可以完成打印機驅動程序,同時又保證了程序接口的一致,實現了實時打印操作的設備無關性。

加入新的打印驅動程序

在系統的打印統一驅動層部分,有一個與該部分相關的配置文件PrinterDrive.ini,當安裝一個打印驅動時,將會增加一個“Printer*Cfg”設置項,“*”表示1,2……相關設置項如下:

[SystemCfg]
DefalutPrinter=Canon_Bjc5500
;缺省打印機
[Printer1Cfg]
Name=Canon_Bjc5500
DriveFile=c:\RealPrt\Drive\Bjc5500.dll
;打印機的設備驅動程序
[Printer2Cfg]
;其他打印驅動的設置

打印統一驅動層將讀取該配置並分析內容,然後使用LoadLibray和GetProcAddress動態創建該打印驅動引擎(即一個指向GeneralPrinter 的類指針),加載安裝的驅動程序。

部分代碼如下(其中g_pUnionPrintSystemEng是打印統一驅動引擎的指針):

typedef void* (*GetGeneralPrinterenginer_Func)(void);
GeneralPrinter *pPrtEng;
GetGeneralPrinterenginer_Func pFunc;
hHandle=LoadLibrary(“c:\RealPrt\Drive\Bjc5500.dll”);
if(hHandle ) {
pFunc=(GetGeneralPrinterenginer_Func )GetProcAddress(hHandle,“GetGeneralPrinterEnginer”);
if(pFunc)
V pPrtEng =(GeneralPrinter *)(*pFunc)();
if(pPrtEng)
g_pUnionPrintSystemEng->LoadPrinterDriver(pPrtEng);
/*在此將打印驅動程序裝入系統中*/
}

通過這種方法,我們可以將新的打印驅動裝入實時打印系統,實現設備無關性。

總 結

本打印系統主要設計了以下幾個類:

● RealTimePrintSystemClnt: 提交打印操作請求,供打印實時信息的應用程序調用的封裝類。

● RealTimePrintSystemServ: 響應打印操作請求,供實時打印系統進程使用的封裝類。

● UnionPrintSystem: 打印統一驅動層的封裝類。

● GeneralPrinter: 打印機驅動的基類。

● Printer_BJC5500: 佳能BJC5500彩色噴墨打印機驅動的封裝類。

● HZDotArrayFont: 漢字字符的打印點陣信息提取器的封裝類。

通過以上幾個類,我們成功地建立了一個實時行式打印系統,同時又實現了打印設備的無關性,使應用不需隨打印機的更改而改變。其中有許多技術細節,限於篇幅,在此沒有提供完整的實現方法,但是基本的技術框架和重要的技術細節完全具備,讀者可以在此基礎上進行詳細編碼以實現系統。

讀者也可以在此基礎之上進行擴展,構建一個客戶/服務器模式的分布式實時打印系統,因為該系統架構就是一個Client/Server 模式。還可以考慮規定打印優先級,將不同的打印信息送到不同的打印機上,也可以考慮Unix上的實現等等。這些都是可行的,最終以求得實現一個完美的實時行式打印系統。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved