控制代碼

在程式設計中，控制代碼（handle）是 Windows 作業系統用來標識被應用程式所建立或使用的物件的整數。其本質相當於帶有引用計數的智慧指標。當一個應用程式要引用其他系統（如資料庫、作業系統）所管理的記憶體塊或物件時，可以使用控制代碼。

控制代碼與指標

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

控制代碼與安全性

客戶獲得控制代碼時，控制代碼不僅是資源的識別符號，也被授予了對資源的特定訪問許可權。

控制代碼與作業系統

在上世紀 80 年代的作業系統（如 Mac OS 和 Windows）的記憶體管理中，控制代碼被廣泛應用。Unix 系統的檔案描述符基本上也屬於控制代碼。和其它桌面環境一樣，Windows API 大量使用控制代碼來標識系統中的物件，並建立作業系統與使用者空間之間的通訊渠道。例如，桌面上的一個窗體由一個 HWND 型別的控制代碼來標識。

如今，記憶體容量的增大和虛擬記憶體演算法使得更簡單的指標愈加受到青睞，而指向另一指標的那類控制代碼受到冷淡。儘管如此，許多作業系統仍然把指向私有物件的指標以及程序傳遞給客戶端的內部陣列下標稱為控制代碼。

譯名

康奈爾大學副教授所著的《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：如果不存在就建立；

//TRUNCATEEXISTING：將現有的檔案縮短為零長度。呼叫程序必須用 GENERICWRITE 訪問模式開啟檔案。如果檔案不存在則函式就會失敗.

DWORD dwFlagAndAttributes, // 指定新建立檔案的屬性；以及檔案的標誌位

//FILEATTRIBUTEARCHIVE：標記為歸檔屬性；

//FILEATTRIBUTENORMAL：預設屬性；檔案沒有被設定任何屬性；僅單獨使用生效。

//FILEATTRIBUTEHIDDEN：隱藏檔案或目錄；

//FILEATTRIBUTEOFFLINE 檔案資料不可直接利用。指出檔案資料已經在物理上移動到離線儲存。

//FILEATTRIBUTEREADONLY：檔案為只讀；

//FILEATTRIBUTESYSTEM：檔案為系統檔案；

//FILEATTRIBUTECOMPRESSED 將檔案標記為已壓縮，或者標記為檔案在目錄中的預設壓縮方式

//FILEATTRIBUTETEMPORARY 檔案用於臨時儲存；檔案系統儘量把檔案資料儲存在記憶體而不是刷回主儲存；臨時檔案不再使用後應用程式應該儘快刪除臨時檔案。

//FILEFLAGWRITE_THROUGH 	系統寫透過任何中間快取直接寫入硬碟；系統仍然可以快取寫操作，但不得懶惰地刷回硬碟

//FILEFLAGOVERLAPPED 	允許對檔案進行重疊 I/O 操作

//FILEFLAGNO_BUFFERING 	禁止對檔案進行緩衝處理。檔案只能寫入磁碟卷的扇區塊

//FILEFLAGRANDOM_ACCESS 	針對隨機訪問對檔案緩衝進行最佳化

//FILEFLAGSEQUENTIAL_SCAN 	針對從頭到尾的順序訪問方式對大檔案緩衝進行最佳化

//FILEFLAGDELETEONCLOSE 	關閉了所有開啟的控制代碼後，檔案立即被刪除。特別適合臨時檔案。如果沒有使用 FILESHAREDELETE, 後續的開啟檔案的請求將會失敗.

//FILEFLAGBACKUPSEMANTICS 指示系統檔案的開啟或建立將用於備份或恢復操作。系統保證呼叫程序如果有必要的特權（SEBACKUPNAME 與 SERESTORE_NAME），將忽略檔案安全檢查。這個標誌也適用於資料夾控制代碼。

//FILEFLAGPOSIX_SEMANTICS 指明檔案基於 POSIX 規則被訪問。例如多個檔案使用只有大小寫區別的檔名（如果檔案系統支援的話）。在 MS-DOS 與 16 位 Windows 下，由於難以相容，需謹慎使用。

//FILEFLAGOPENREPARSEPOINT 指出禁止 NTFS 重解析點的行為。標誌不能夠和 CREAT_ALWAYS 一起使用.

//FILEFLAGOPENNORECALL 指明需要檔案資料，但是將繼續從遠端儲存器中讀寫，不會將資料存放在本地儲存器中。這個標誌由遠端儲存系統或分層儲存管理器系統使用.

HANDLE hTemplateFile // 如果不為 0，則指定一個檔案控制代碼，新的檔案將從這個檔案中複製擴充套件屬性

)；// 如果函式失敗，返會值會是 INVALIDHANDLEVALUE

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 在記憶體中申請緩衝區，做到記憶體頁對齊（從而硬碟扇區也對齊）。