句柄的本質

句柄是WONDOWS用來標識被應用程序所建立或使用的對象的唯一整數,WINDOWS使用各種各樣的句柄標識諸如應用程序實例,窗口,控制,位圖,GDI對象等等。WINDOWS句柄有點象C語言中的文件句柄

從上面的定義中的我們可以看到,句柄是一個標識符,是拿來標識對象或者項目的,它就象我們的姓名一樣,每個人都會有一個,不同的人的姓名不一樣,但是,也可能有一個名字和你一樣的人。從數據類型上來看它只是一個16位的無符號整數。應用程序幾乎總是通過調用一個WINDOWS函數來獲得一個句柄,之後其他的WINDOWS函數就可以使用該句柄,以引用相應的對象。

如果想更透徹一點地認識句柄,我可以告訴大家,句柄是 一種指向指針的指針。我們知道,所謂指針是一種內存地址。應用程序啓動後,組成這個程序的各對象是住留在內存的。如果簡單地理解,似乎我們只要獲知這個內 存的首地址,那麼就可以隨時用這個地址訪問對象。但是,如果您真的這樣認爲,那麼您就大錯特錯了。我們知道,Windows是一個以虛擬內存爲基礎的操作 系統。在這種系統環境下,Windows內存管理器經常在內存中來回移動對象,依此來滿足各種應用程序的內存需要。對象被移動意味着它的地址變化了。如果 地址總是如此變化,我們該到哪裏去找該對象呢?

爲了解決這個問題,Windows操作系統爲各應用程序騰出一些內存儲地址,用來專門登記各應用對象在內存中的地址變化,而這個地址(存儲單元的位置)本身是不變的。Windows內存管理器在移動對象在內存中的位置後,把對象新的地址告知這個句柄地址來保存。這樣我們只需記住這個句柄地址就可以間接地知道對象具體在內存中的哪個位置。這個地址是在對象裝載(Load)時由系統分配給的,當系統卸載時(Unload)又釋放給系統。

句柄地址(穩定)→記載着對象在內存中的地址────→對象在內存中的地址(不穩定)→實際對象

本質:WINDOWS程序中並不是用物理地址來標識一個內存塊,文件,任務或動態裝入模塊的,相反的,WINDOWS API給這些項目分配確定的句柄,並將句柄返回給應用程序,然後通過句柄來進行操作。

但是必須注意的是程序每次從新啓動,系統不能保證分配給這個程序的句柄還是原來的那個句柄,而且絕大多數情況的確不一樣的。假如我們把進入電影院看電影看成是一個應用程序的啓動運行,那麼系統給應用程序分配的句柄總是不一樣,這和每次電影院售給我們的門票總是不同的一個座位是一樣的道理。

受M$的幫助文檔以及很多Windows編程書籍的影響,大家對局柄比較普遍的認識是:句柄是一個整數,用以標識Windows對象,句柄不是一個指針……

而實際上,這些不過是M$進行數據封裝的幌子而已,下面我們一起來分析一下HANDLE到底是什麼。

請先到Windef.h找絕大多數句柄的定義:

DECLARE_HANDLE(HWND);

DECLARE_HANDLE(HHOOK);

……

DECLARE_HANDLE(HGDIOBJ);

DECLARE_HANDLE(HBITMAP);

DECLARE_HANDLE(HBRUSH);

……

typedef HANDLE          HGLOBAL;

typedef HANDLE          HLOCAL;

……

OK, 現在大家跟我一起翻到Winnt.h,看看DECLARE_HANDLE和HANDLE到底是什麼:

#ifdef STRICT

typedef void *HANDLE;

#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name

#else

typedef PVOID HANDLE;

#define DECLARE_HANDLE(name) typedef HANDLE name

#endif

typedef HANDLE *PHANDLE;

哈哈,現在知道了吧,HANDLE就是PVOID,也就是無類型指針,

而DECLARE_HANDLE(HWND);就是:

struct HWND__ {

   int unused;};

typedef struct HWND__ *HWND;

現在實際上都清楚啦,這些Handles都不過是指向struct的指針,至於這個struct的用處,連M$都說unused了,^o^

現在解釋下M$這麼做的意義,這就是所謂數據封裝,你可以在你的程序中把M$的內部結構指針傳來傳去,可是你卻不知道它到底指向的內容是什麼,而且可以編個句柄的瞎話防止大家的質疑:)。而M$的程序大可以這麼寫:

#include <windows.h> //這個和大家用的一樣

#include "windows_in.h" //這個是M$自用的,外人別想看到^o^

HSOMETHINGELSE DoSomething(HSOMETHING hSomething) {

   struct RealSomething* p = (struct RealSomething*)hSomething; //先強制類型轉換成內部結構指針

   ……do something……

   return (HSOMETHINGELSE)pRealSomethingElse;//強制類型逆轉換

一、書上定義:

<<Microsoft Windows 3 Developer''s Workshop>>(Microsoft Press,by Richard Wilton)
    在Windows環境中,句柄是用來標識項目的,這些項目包括:模塊(module)、任務(task)、實例 (instance)、文件(file)、內存塊(block of memory)、菜單(menu)、控制(control)、字體(font)、資源(resource),包括圖標(icon),光標 (cursor),字符串(string)等、GDI對象(GDI object),包括位圖(bitmap),畫刷(brush),元文件(metafile),調色板(palette),畫筆(pen),區域 (region),以及設備描述表(device context)。

<<WINDOWS編程短平快>>(南京大學出版社):
    句柄是WONDOWS用來標識被應用程序所建立或使用的對象的唯一整數,WINDOWS使用各種各樣的句柄標識諸如應用程序實例,窗口,控制,位圖,GDI對象等等。WINDOWS句柄有點象C語言中的文件句柄。

二、MFC源代碼:

#ifdef STRICT
typedef void *HANDLE;
#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name
#else
typedef PVOID HANDLE;
#define DECLARE_HANDLE(name) typedef HANDLE name
#endif

DECLARE_HANDLE(HMODULE);
DECLARE_HANDLE(HINSTANCE);
DECLARE_HANDLE(HLOCAL);
DECLARE_HANDLE(HGLOBAL);
DECLARE_HANDLE(HDC);
DECLARE_HANDLE(HRGN);
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HMENU);
DECLARE_HANDLE(HACCEL);
DECLARE_HANDLE(HTASK);


三、理解:
    HANDLE就是PVOID,也就是無類型指針,
    上面這些資源的句柄Handles都不過是指向struct的指針,至於這個struct的用處,連M$都說unused了,現在解釋下M$這麼做的意義,這就是所謂數據封裝,你可以在你的程序中把M$的內部結構指針傳來傳去,可是你卻不知道它到底指向的內容是什麼。

    句柄與指針確實是完全不同的兩個概念。句柄僅僅是一個32位整數,WIN32中用於標記某個系統或進程的對象,可以理解爲對象索引(由於M$未完全公開相關技術,在一定程度上只能如此理解),這個索引更像是一種映射關係(從句柄到對象指針的映射),而不是純粹意義上的“數組下標”。


     句柄可以理解爲用於指向或標識內存的一塊“資源”,這些資源如:文件(file)、內存塊(block of memory)、菜單(menu)等等。操作系統通過句柄來定位核心對象和系統資源。
    指針即爲指向內存的“數據或指令”某一單元。

    說的確切一點,句柄實際上是一種指向某種資源的指針,但與指針又有所不同:指針對應着一個數據在內存中的地址,得到了指針就可以自由地修改該數據。Windows並不希望一般程序修改其內部數據結構,因爲這樣太不安全。所以Windows給每個使用GlobalAlloc等函數聲明的內存區域指定一個句柄(本質上仍是一個指針,但不要直接操作它),平時你只是在調用API函數時利用這個句柄來說明要操作哪段內存。

    
四、引喻:
   牧童遙指杏花村
   牧童的手爲指針,杏花村的牌子爲句柄,杏花村酒店爲對象的實例.


附註:獲得窗口句柄三種方法

1.HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)

   HWND FindWindowEx(HWND hwndParent, HWND hwndChildAfter,LPCTSTR lpClassName, LPCTSTR lpWindowName)

2.HWND WindowFromPoint(POINT& Point)//獲得當前鼠標光標位置的窗口HWND

3.BOOL CALLBACK EnumChildProc(HWND hwnd,LPARAM lParam)

   BOOL CALLBACK EnumChildWindows(HWND hWndParent, WNDENUMPROC lpEnumFunc,LPARAM lParam)

   BOOL CALLBACK EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam)

   BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)

發佈了27 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章