句柄

在程序設計中，句柄（handle）是Windows操作系統用來標識被應用程序所建立或使用的對象的整數。其本質相當於帶有引用計數的智能指針。當一個應用程序要引用其他系統（如數據庫、操作系統）所管理的內存塊或對象時，可以使用句柄。

**句柄與指針**
句柄與普通指針的區別在於，指針包含的是引用對象的內存地址，而句柄則是由系統所管理的引用標識，該標識可以被系統重新定位到一個內存地址上。這種間接訪問對象的模式增強了系統對引用對象的控制。（參見封裝）。通俗的說就是我們調用句柄就是調用句柄所提供的服務，即句柄已經把它能做的操作都設定好了，我們只能在句柄所提供的操作範圍內進行操作，但是普通指針的操作卻多種多樣，不受限制。

**句柄與安全性**
客戶獲得句柄時，句柄不僅是資源的標識符，也被授予了對資源的特定訪問權限。

**句柄與操作系統**
在上世紀80年代的操作系統（如Mac OS和Windows）的內存管理中，句柄被廣泛應用。Unix系統的文件描述符基本上也屬於句柄。和其它桌面環境一樣，Windows API大量使用句柄來標識系統中的對象，並建立操作系統與用戶空間之間的通信渠道。例如，桌面上的一個窗體由一個HWND類型的句柄來標識。
如今，內存容量的增大和虛擬內存算法使得更簡單的指針愈加受到青睞，而指向另一指針的那類句柄受到冷淡。儘管如此，許多操作系統仍然把指向私有對象的指針以及進程傳遞給客戶端的內部數組下標稱為句柄。

**譯名**
康奈爾大學副教授David Gries所著的《Compiler Construction for Digital Computer》. John Wiley and Sons, New York, 1971, 491 pages, ISBN 0-471-32776-X，給出如下定義：
(2.3.10) DEFINITION. A handle of any sentential form is a leftmost simple phrase.
在該書的中譯本： D.格里斯著,仲萃豪等譯：《數字計算機的編譯程序構造》，科學出版社, 1976年版，給出瞭如下翻譯：
(2.3.10)定義.任一句型的句柄就是此句型的最左簡單短語。

**Windows句柄的本質**
內核對象是內存中的數據結構，由操作系統內核分配，並且只能由操作系統內核訪問。內核對象的數據結構使用引用計數，計數變為0後操作系統內核就會銷燬該內核對象。安全描述符（SECURITY_ATTRIBUTES結構）描述內核對象的安全性，指出內核對象的擁有者、哪些組或用戶可以訪問此對象。作為對照，用戶對象或GDI對象（如菜單、窗口、鼠標光標等）在創建時不需要指出安全描述符。
操作系統內核中有一個全局句柄表。每個進程有自己的一個句柄表，是一個數據結構組成的數組，每個數據結構包含一個指向內核對象的指針、訪問掩碼、繼承標識等。句柄是進程句柄表數組的下標。在32位系統中，句柄是一個32位值。64位系統中則是64位值。應用程序調用創建內核對象的API函數後，該API函數會返回一個句柄以標識操作系統內核所創建的內核對象。這個句柄可以由進程的任何線程使用。使用CloseHandle函數或者進程結束時，內核句柄表中相應項的計數值會被減1。
GetCurrentProcess函數返回當前進程的句柄，其值為-1；GetCurrentThread函數返回的句柄其值為-2。二者都是偽句柄，在進程的句柄表中沒有這兩項，僅代表當前進程和當前線程。
進程間共享內核對象有如下途徑：
*父子進程間的句柄繼承：在調用API函數來創建內核對象時，使用SECURITY_ATTRIBUTES結構來指出可以被繼承。創建子進程時，所有被繼承的句柄在子進程句柄表中的位置與在父進程中完全一樣；內核對象的使用計數也會加1。
*命名對象：注意所有種類的內核對象使用同一個命名空間。
*複製對象句柄：使用DuplicateHandle函數

**文件/設備句柄的操作系統API**
Windows操作系統提供下述API函數以訪問文件或設備：
SetHandleInformation： 設置指定句柄的當前標識
GetHandleInformation： 返回指定句柄的當前標識
HANDLE CreateFile(  
LPCTSTR,lpFileName,                        //指向文件名的指針  
DWORD dwDesiredAccess,                     //訪問模式（讀/寫/讀寫/0表示不讀寫僅獲取文件信息）  
DWORD dwShareMode,                         //共享模式  
LPSECURITY_ATTRIBUTES lpSecurityAttributes,//用於確定如何在子進程中繼承這個句柄  
DWORD dwCreationDisposition,               // 文件存在或不存在時的操作
//CREATE_NEW：創建文件，如果文件存在會出錯；
//CREATE_ALWAYS：創建文件，如果該文件已經存在,函數將覆蓋已存在的文件並清除已存在的文件屬性
//OPEN_EXISTING：打開已存在文件；
//OPEN_ALWAYS：如果不存在就創建；
//TRUNCATE_EXISTING：將現有的文件縮短為零長度。調用進程必須用GENERIC_WRITE訪問模式打開文件.如果文件不存在則函數就會失敗.  
DWORD dwFlagAndAttributes,                 //指定新創建文件的屬性；以及文件的標誌位 
//FILE_ATTRIBUTE_ARCHIVE：標記為歸檔屬性；
//FILE_ATTRIBUTE_NORMAL：默認屬性；文件沒有被設置任何屬性；僅單獨使用生效。
//FILE_ATTRIBUTE_HIDDEN：隱藏文件或目錄；
//FILE_ATTRIBUTE_OFFLINE 文件數據不可直接利用。指出文件數據已經在物理上移動到脫機存儲。
//FILE_ATTRIBUTE_READONLY：文件為只讀；
//FILE_ATTRIBUTE_SYSTEM：文件為系統文件；
//FILE_ATTRIBUTE_COMPRESSED 將文件標記為已壓縮，或者標記為文件在目錄中的默認壓縮方式 
//FILE_ATTRIBUTE_TEMPORARY 文件用於臨時存儲；文件系統儘量把文件數據保存在內存而不是刷回主存儲；臨時文件不再使用後應用程序應該儘快刪除臨時文件。
//FILE_FLAG_WRITE_THROUGH 	系統寫通過任何中間緩存直接寫入硬盤；系統仍然可以緩存寫操作，但不得懶惰地刷回硬盤
//FILE_FLAG_OVERLAPPED 	允許對文件進行重疊I/O操作
//FILE_FLAG_NO_BUFFERING 	禁止對文件進行緩衝處理。文件只能寫入磁盤卷的扇區塊
//FILE_FLAG_RANDOM_ACCESS 	針對隨機訪問對文件緩衝進行優化
//FILE_FLAG_SEQUENTIAL_SCAN 	針對從頭到尾的順序訪問方式對大文件緩衝進行優化
//FILE_FLAG_DELETE_ON_CLOSE 	關閉了所有打開的句柄後，文件立即被刪除。特別適合臨時文件。如果沒有使用FILE_SHARE_DELETE,後續的打開文件的請求將會失敗.
//FILE_FLAG_BACKUP_SEMANTICS 指示系統文件的打開或創建將用於備份或恢復操作。系統保證調用進程如果有必要的特權（SE_BACKUP_NAME與SE_RESTORE_NAME），將忽略文件安全檢查。這個標誌也適用於文件夾句柄。
//FILE_FLAG_POSIX_SEMANTICS 指明文件基於POSIX規則被訪問。例如多個文件使用只有大小寫區別的文件名（如果文件系統支持的話）。在MS-DOS與16位Windows下，由於難以兼容，需謹慎使用。
//FILE_FLAG_OPEN_REPARSE_POINT 指出禁止NTFS重解析點的行為。標誌不能夠和CREAT_ALWAYS一起使用.
//FILE_FLAG_OPEN_NO_RECALL 指明需要文件數據,但是將繼續從遠程存儲器中讀寫，不會將數據存放在本地存儲器中。這個標誌由遠程存儲系統或分層存儲管理器系統使用.
HANDLE hTemplateFile                       //如果不為0，則指定一個文件句柄，新的文件將從這個文件中複製擴展屬性  
)；//如果函數失敗,返會值會是INVALID_HANDLE_VALUE
SetFilePointer（）；//設置文件指針位置
BOOL WriteFile(  
HANDLE fFile,                  //文件句柄  
LPCVOID lpBuffer,              //數據緩存區指針  
DWORD nNumberOfBytesToWrite,   //所要寫的字節數  
LPDWORD lpNumberOfBytesWritten,//用於保存實際寫入字節數的存儲區的指針  
LPOVERLAPPED lpOverlapped      //OVERLAPPED結構體指針  
)；
BOOL ReadFile(  
HANDLE fFile,                  //文件句柄  
LPCVOID lpBuffer,              //數據緩存區指針  
DWORD nNumberOfBytesToRead,    //所要寫的字節數  
LPDWORD lpNumberOfBytesRead,   //用於保存實際寫入字節數的存儲區的指針  
LPOVERLAPPED lpOverlapped      //OVERLAPPED結構體指針  
)  ；
CloseHandle(hFILE); //關閉句柄
GetDiskFreeSpace(); //確定扇區的尺寸
文件存取的字節數必須是扇區尺寸的整倍數。進行讀和寫操作的地址必須扇區位置對齊（在內存中地址對齊到扇區容量的整倍數），可用VirtualAlloc()在內存中申請緩衝區，做到內存頁對齊（從而硬盤扇區也對齊）。

**參見**
 內存- 指針- 物理地址

**參考文獻 ==