裝置描述表對象HDC,畫刷對象HBRUSH,畫筆對象HPEN等等,這些不是核心對象,是GDI繪圖對象,也是使用者物件。
區別使用者物件和核心對象的通用方法是,建立核心對象的時候有個PSECURITY_ATTRIBUTES類型的參數,該參數是一個指向 SECURITY_ATTRIBUTES 結構的指標,該結構描述了所建立的核心對象的安全性,比如是否可以被子進程繼承。
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength;
LPVOID lpSecurityDescriptor;
BOOL bInheritHandle;
} SECURITY_ATTRIBUTES;
核心對象是屬於windows的,而不是屬於進程的,但進程中有一個控制代碼表,引用了核心對象。
核心對象有一個使用計數,一個進程的核心物件控點表中引用了某個核心對象,該核心對象的使用計數就遞增1,該進程調用CloseHandle(hHandle)時,引用計數遞減,當引用計數為0時,核心對象被銷毀,當進程退出時,進程所有核心對象的引用計數都遞減。
常見的核心對象:檔案對象,檔案對應物件,事件(event)對象,互斥量(mutex)對象,進程對象,線程對象。
建立一個核心對象時,返回一個控制代碼,該控制代碼在32位windows時為一個無符號32位整數,講該整數右移兩位就得到了控制代碼在進程的控制代碼表中的索引值。
下面是如何在進程間共用核心對象。
跨進程邊界共用核心對象有三種方法: 物件控點繼承, 為對象命名, 複製物件控點。
1 物件控點繼承:
SECURYTY_ATTRIBUTES中的第三個成員 bInheritHandle 指定了建立的核心對象是否可被子進程共用,如果指定為TRUE,CreateProcess建立子進程的時候,指定參數bInheritHandle為TRUE,那麼該物件控點就會被共用。 (此時子進程與父進程的核心物件控點在兩個進程的控制代碼表中的位置一摸一樣,所以相同的控制代碼值,都能知道控制代碼表中的那條控制代碼記錄,並指向相同的記憶體位址,那個記憶體位址儲存了核心對象,當然你不能直接修改那個記憶體位址的內容,因為你無權訪問)。
控制代碼值一般通過建立子進程時候的命令列參數傳遞,並在子進程解析出控制代碼值。
可以用SetHandleInformation() 和 GetHandleInformation() 兩個函數來重設控制代碼的繼承標誌 或者 擷取繼承標誌。
2 通過對象名稱
建立核心對象的時候最後一個參數為PCTSTR類型,可以為核心對象指定一個名稱,在其他進程建立同名對象時,會先去系統核心對象列表中尋找是否存在同名同類型核心對象,如果找到就不建立新核心對象,直接在進程的核心物件控點表建立一個控制代碼,記憶體位址指向該核心對象。並將核心對象的使用計數遞增。
也可以通過Open*(DWORD dwDesiredAccess, BOOL bInheritHandle, PCTSTR pszName)函數來尋找指定名稱的核心對象,如果沒有找到,則返回一個NULL值。
好處是共用核心對象的兩個進程可以不是父子進程的關係
3 複製物件控點