裝置的輸入輸出,即裝置I/O,可以分為“同步”和“非同步”兩種方式。同步的裝置I/O,調用的API函數總是等到裝置I/O完成才返回。而非同步裝置I/O,可以通過多種方法來實現,但是其根本原理是得到“裝置I/O的完成通知”。
本篇主要討論如何開啟和關閉一個裝置。注意,這裡的裝置,不是指像鍵盤、顯示器那種實體。而是一種抽象的概念,指一種與外界通訊的對象,可以接受外界的輸入,也可以對外界的請求作出響應,稱之為裝置I/O。這個概念比較抽象,這些裝置往往和某個核心對象關聯。要開啟這些裝置,就要建立相關的核心對象。
這些裝置包括檔案、目錄、邏輯磁碟驅動、物理磁碟驅動、序列埠、並行連接埠、郵槽、管道、通訊端、控制台(如下表):
裝置 |
主要用途 |
檔案 |
儲存資料 |
目錄 |
屬性和檔案壓縮設定 |
邏輯磁碟驅動 |
磁碟格式化 |
物理磁碟驅動 |
訪問分區表 |
序列埠 |
串列傳輸資料 |
並行連接埠 |
多位元據同時傳輸,主要是將資料轉送給印表機 |
郵槽 |
一對多傳輸資料,往往適用於一個網路中的一台電腦向其他機器發送資料 |
具名管道 |
一對一傳輸資料,往往適用於一個網路中的一台電腦向其他機器發送資料 |
匿名管道 |
一對一傳輸資料,適用於簡單的資料轉送,不適用於網路 |
通訊端 |
以流或資料報的形式發送資料,適用於一個網路中的通訊 |
控制台 |
一個文字視窗顯示緩衝區 |
要使用這些裝置,你首先應該開啟這些裝置。
Windows努力隱藏這些裝置的差異,所以,很多裝置的開啟的I/O工作可以通過同一個API函數完成,如下表:
裝置 |
經常用來開啟裝置的API函數和用法 |
檔案 |
CreateFile —— 開啟裝置的函數。 將參數pszName是一個檔案路徑名。 |
目錄 |
CreateFile —— 開啟裝置的函數。 將參數pszName是一個目錄名。Windows允許你開啟一個目錄,通過使用參數FILE_ FLAG_BACKUP_SEMANTICS旗標來呼叫CreateFile函數。開啟目錄之後,就可以這是目錄屬性,即檔案夾屬性,比如正常、隱藏、系統、唯讀等。 |
邏輯磁碟驅動 |
CreateFile —— 開啟裝置的函數。 將參數pszName設定為字串“\\.\x:”。 比如要開啟C盤,就將其設定為“\\.\C:”。 |
物理磁碟驅動 |
CreateFile —— 開啟裝置的函數。 將參數pszName設定為“\\.\PHYSICALDRIVEx”。比如開啟第一個物理硬碟扇區:可以這麼調用CreateFile函數: CreateFile(TEXT("\\.\PHYSICALDRIVE0"), ...); 這樣就可以開啟一個物理磁碟驅動,並且可以直接存取硬碟分區表。 但是開啟物理磁碟驅動是存在潛在危險的,特別是當錯誤的寫入,會造成物理磁碟內容的破壞。 |
序列埠 |
CreateFile —— 開啟裝置的函數。 將參數pszName設定為“COMx”,比如開啟COM1串口裝置,只要將其設定為“COM1”。 |
並行連接埠 |
CreateFile —— 開啟裝置的函數。 將參數pszName設定為“LPTx”,比如開啟LPT1並行連接埠,將其設定為“LPT1”。 |
郵槽(伺服器端) |
CreateMailslot —— 開啟裝置的函數。 將參數pszName設定為“\\.\mailslot\mailslotname”,其中,“mailsoltname”是為郵槽取的名字,可以任意,前面的字串是固定的。 |
郵槽(用戶端) |
CreateFile —— 開啟裝置的函數。 將參數pszName設定為“\\servername\mailslot\mailslotname”,其中,“mailsoltname”是為郵槽取的名字,可以任意,前面的字串是固定的。 |
具名管道 (伺服器端) |
CreateFile —— 開啟裝置的函數。 將參數pszName設定為“\\.\pipe\pipename”,其中,“pipename”是為具名管道取的名字,可以任意,前面的字串是固定的。 |
具名管道 (用戶端) |
CreateFile —— 開啟裝置的函數。 將參數pszName設定為“\\servername\pipe\pipename”,其中,“pipename”是為具名管道取的名字,可以任意,前面的字串是固定的。 |
匿名管道 |
CreatePipe —— 開啟裝置的函數。 無論是用戶端還是伺服器端都以該函數建立或開啟匿名管道。 |
通訊端 |
socket —— 建立一個通訊端描述符accept, or AcceptEx. |
控制台 Console |
CreateConsoleScreenBuffer,GetStdHandle —— 開啟裝置的函數 |
從上表可以發現,很多裝置都以CreateFile函數來建立和開啟。這個函數以後會講。
開啟了裝置,你得到了一個裝置的控制代碼,你就可以通過該控制代碼使用其他函數,來對相關裝置進行設定。
比如,現在開啟了一個序列埠,然後要設定它的傳輸傳輸速率:
BOOL SetCommConfig(
HANDLE hCommDev,
LPCOMMCONFIG pCC,
DWORD dwSize);
或者,你獲得了一個郵槽控制代碼,可以設定讀取資料的等待時間:
BOOL SetMailslotInfo(
HANDLE hMailslot,
DWORD dwReadTimeout);
最後,不要忘記關閉控制代碼,從而正確地關閉裝置:
BOOL CloseHandle(HANDLE hObject);int closesocket(SOCKET s); //通訊端的關閉
如果你有了一個裝置控制代碼,你可以調查它的裝置類型,通過使用GetFileType函數,該函數的傳回值表明了它是一個什麼類型的裝置,可以參考MSDN。
DWORD GetFileType(HANDLE hDevice);
好了,現在讓我們來討論一下CreateFile函數:
HANDLE CreateFile(
PCTSTR pszName, //指明裝置類型或一個特定的裝置實體
DWORD dwDesiredAccess, //訪問限制
DWORD dwShareMode, //共用方式
PSECURITY_ATTRIBUTES psa, //安全描述結構
DWORD dwCreationDisposition, //建立和開啟檔案
DWORD dwFlagsAndAttributes, //屬性旗標,與緩衝區和檔案操作屬性有關
HANDLE hFileTemplate); //裝置模版,一個裝置控制代碼
該函數成功,返回控制代碼,失敗返回INVALID_HANDLE_VALUE(值為-1)。
如果設定了最後一個參數hFileTemplate,那麼就照著這個參數所代表的裝置,建立一個屬性相同的裝置,當然,這個參數所表示的裝置要具有“可讀”的許可權,即有GENERIC_READ存取權限。
至於該函數的具體用法,可以參看本書或MSDN。