在windows中,句柄是一個係統內部數據結構的引用。例如,當你操作一個窗口,或說是一個delphi窗體時,係統會給你一個該窗口的句柄,係統會通知你:你正在操作142號窗口,就此,你的應用程序就能要求係統對142號窗口進行操作——移動窗口、改變窗口大小、把窗口極小化為圖標,等等。實際上許多windows api函數把句柄作為它的第一個參數,如gdi(圖形設備接口)句柄、菜單句柄、實例句柄、位圖句柄等等,不僅僅局限於窗口函數。
換句話說,句柄是一種內部代碼,通過它能引用受係統控製的特殊元素,如窗口、位圖、圖標、內存塊、光標、字體、菜單等等。
案例:獲取窗口句柄 案例說明
本例實現窗口句柄的獲取。
實現過程
private declare function getwindowlong lib "user32" alias "getwindowlonga" (byval hwnd as long, byval nindex as long) as long
private declare function setwindowlong lib "user32" alias "setwindowlonga" (byval hwnd as long, byval nindex as long, byval dwnewlong as long) as long
private declare function setlayeredwindowattributes lib "user32" (byval hwnd as long, byval crkey as long, byval balpha as byte, byval dwflags as long) as long
private const ws_ex_layered = &h80000
private const gwl_exstyle = (-20)
private const lwa_alpha = &h2
private sub form_activate()
on error resume next
for i = 0 to 150 step 2.5
setlayeredwindowattributes me.hwnd, 0, i, lwa_alpha
doevents
next i
end sub
private sub form_load()
dim rtn as long
rtn = getwindowlong(me.hwnd, gwl_exstyle)
rtn = rtn or ws_ex_layered
setwindowlong me.hwnd, gwl_exstyle, rtn
setlayeredwindowattributes me.hwnd, 0, 0, lwa_alpha
end sub
No. 2
在Windows中,句柄是一個係統內部數據結構的引用。例如,當你操作一個窗口,或說是一個Delphi窗體時,係統會給你一個該窗口的句柄,係統會通知你:你正在操作142號窗口,就此,你的應用程序就能要求係統對142號窗口進行操作——移動窗口、改變窗口大小、把窗口極小化為圖標,等等。實際上許多Windows API函數把句柄作為它的第一個參數,如GDI(圖形設備接口)句柄、菜單句柄、實例句柄、位圖句柄等等,不僅僅局限於窗口函數。
換句話說,句柄是一種內部代碼,通過它能引用受係統控製的特殊元素,如窗口、位圖、圖標、內存塊、光標、字體、菜單等等。
案例:獲取窗口句柄 案例說明
本例實現窗口句柄的獲取。
實現過程
Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SetLayeredWindowAttributes Lib "user32" (ByVal hwnd As Long, ByVal crKey As Long, ByVal bAlpha As Byte, ByVal dwFlags As Long) As Long
Private Const WS_EX_LAYERED = &H80000
Private Const GWL_EXSTYLE = (-20)
Private Const LWA_ALPHA = &H2
Private Sub Form_Activate()
On Error Resume Next
For i = 0 To 150 Step 2.5
SetLayeredWindowAttributes Me.hwnd, 0, i, LWA_ALPHA
DoEvents
Next i
End Sub
Private Sub Form_load()
Dim rtn As Long
rtn = GetWindowLong(Me.hwnd, GWL_EXSTYLE)
rtn = rtn Or WS_EX_LAYERED
SetWindowLong Me.hwnd, GWL_EXSTYLE, rtn
SetLayeredWindowAttributes Me.hwnd, 0, 0, LWA_ALPHA
End Sub
單從概念上講,句柄指一個對象的標識,而指針是一個對象的首地址。從實際處理的角度講,即可以把句柄定義為指針,又可以把它定義為同類對象數組的索引,這兩種處理方法都有優缺點,至於選用哪種方式,完全應該看實際需要,這可以說是一種程序設計上的技巧。那種單純認為句柄是指針或索引的想法都是機械的、不確切的。
其實,在Windows中類似的處理是很多的、很靈活的。再具個相似的例子:
我們知道,在Windows中有個函數叫做CallWindowProc。顧名思義,它的作用就是嚮指定的窗口過程傳遞一個消息。你也許會想,既然我已經有了窗口過程的指針,為什麽我不可以直接通過這個指針調用該函數(這是C語言的內建功能)?事實上,在Win16中確實可以這麽做,因為GetWindowLong返回的確實是該函數的指針。但在Win32下,GetWindowLong返回的並不是該函數的指針,而是一個包含函數指針的數據結構的指針(MSDN上說返回的是一個窗口函數地址或它的句柄,就是指的這種情況)。該數據結構是可變的,但衹要你使用CallWindowProc來調用的話是不會出錯的。這裏我們又看到使用句柄處理帶來的好處。(補充說明一點:微軟在這裏之所以這麽處理,是為瞭解决16位/32位以及ANSI/UNICODE的轉化問題)
1.句柄是什麽?
在windows中,句柄是和對象一一對應的32位無符號整數值。對象可以映射到唯
一的句柄,句柄也可以映射到唯一的對象。
2.為什麽我們需要句柄?
更準確地說,是windows需要句柄。windows需要嚮程序員提供必要地編程接口
,在這些接口中,允許程序員訪問、創建和銷毀對象。但是,出於封裝地考慮,wi
ndows並不想嚮程序員返回指針。指針包含了太多的信息。首先指針給出了對象存儲
的確切位置;其次,要操作一個指針,程序員必須知道指針所指對象的內部結構特
徵,也即,windows必須嚮程序員暴露相應的數據結構,而這些數據結構也許是操作
係統想嚮程序員隱藏的。
如果說COM技術嚮用戶隱藏了數據,衹暴露了接口並衹允許按接口定義的方法操
作數據的話,句柄這種方式則允許你按自己的方式直接操作數據,但windows又不嚮
你直接暴露數據。直接操作數據是程序員需要的,不暴露數據是windows所需要的,
句柄封裝方式實現了各取所需。
3.句柄如何與對象映射?
封裝背後,必須有一個地方可以實現解碼,以實現句柄和對象的相互轉換。在
windows中,存在兩種映射方式:
a. 全等映射。也即,句柄本身就是一個指針。映射在這裏衹是類型轉換而已。
這種情況有,進程實例句柄或模塊句柄,以及資源句柄等等。
b. 基於表格的映射。這是對象指針與句柄之間最普通的映射機製。操作係統創
建表格,並保存所有要考慮的對象。需要創建新對象時,要先在表格中找到空入口
,然後把表示對象的數據添入其中。當對象被刪除時,它的數據成員和其在表中的
入口被釋放。
4.句柄的定義和實現
我們以GDI對象為例進行討論。創建了GDI對象,就會得到該對象的句柄。句柄
的對象可能是HBRUSH、HPEN、HFONT或HDC中的一種,這依賴於你創建 的GDI對象類
型。但是最普通的GDI對象類型是HGDIOBJ。HGDIOBJ被定義成空指針。
HPEN的實際編譯類型定義隨編譯時間宏STRICT的不同而不同。如果STRCIT已經
被定義了,HPEN是這樣的:
struct HPEN__ {int unused};
typedef struct HPEN__* HPEN;
如果STRICT沒有定義,HPEN是這樣定義的:
typedef void *HANDLE;
typedef HANDLE HPEN;
上面這段代碼是一個註重細節的程序員最接近句柄的地方,因此我們重點分析
一下。這裏有一點點技巧。如果定義了STRICT宏,HPEN是指嚮有單個未使用字段的
結構的指針,否則HPEN是空指針。C/C++編譯器允許把任何類型的指針作為空指什傳
遞,反之則不可以。兩個不同類型的非空指針是互不兼容的。在STRICT版本中,編
譯對GDI對象句柄的不正確混用將給出警告,對於非GDI句柄,如HWND、HMENU的不正
確混用也會給出警告,從而使程序在編譯器得到更STRICT的檢查。
接下來的分析可能不那麽令你感興趣,但它更深刻地揭示了句柄。對GDI句柄來
說,儘管windows頭文件把它定義成指針,但如果你仔細檢查這些句柄的值,它根本
就不像指針,這也是為什麽我說它衹是一個32位無符整數值的原因。對句柄就是指
針的情況,這句話也仍然適用。讓我們隨意地生成一些句柄,比如你用GetStockOb
ject()以得到一些句柄,你會發現,它們的值總在區間0x01900011到0xba040389。
前者指嚮用戶區中的未分配的無效區域,後者指嚮內核地址空間。另外你可能發現
,兩個句柄之間的值可能衹差數值1,這也說明GDI句柄不是指針。
和多數人想象的不一樣,句柄也不是一個單純的索引值。對GDI對象句柄來說,
GDI句柄由8位 、1位堆對象標記(表明對象是否創建在堆中)、7位對象類型信息和
高4位為0的16位索引組成,如圖:
3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
| 8 位引用計數 |堆 | 對象類型7 | 16位索引 |
標
記
在這裏你可以看到,對GDI來說,它衹使用了16位作為索引。這意味着一個進程最多衹
可以創建小於64K個句柄,實際上受其他一些限製,整個Windows係統中大概可以容納約
16384(0x4000)個GDI對象。