Windows程序中,有各種各樣的資源(窗口、圖標、光標),系統在創建這些資源的時候會為他們分配內存,並返回標識這些資源的標識號,即句柄HANDLE(ID)。圖標句柄(HICON)、光標句柄(HCURSOR)、畫刷句柄(HBRUSH)。
為什么有個窗口對象還要窗口句柄呢?還有什么線程句柄,甚至還有控件ID和進程ID呢?MFC中的對象,比如應用程序對象,視圖對象等,是對象就會占用內存空間,我們就可以用指針指向此對象進行訪問,但windows還提供了句柄訪問,初看好像有點多余,實則不然。如果我們一個進程想訪問另一個進程,根據對象指針訪問就不行了。我們現在的Windows是一個完全保護的系統,應用程序工作於CPU的保護模式下,引入了虛存技術。每個進程擁有獨立的4GB的地址空間,所以在應用程序中的地址是自己眼中的地址,不具有通用性。那么兩個進程就不能簡單地傳個地址就行了,窗體句柄就是windows內核的一種數據結構,不同窗體(可以是不同進程的不同窗體)有不同的窗體句柄,windows通過句柄可以識別不同的窗體對象。那么進程句柄也是全局唯一的么?非也!進程句柄也是在本進程內有效,由創建進程或者打開進程時得到的句柄。進程ID才是全局唯一的。那么控件ID是全局唯一的么?也不是!控件ID代表一個資源,很多時候就是代表一個資源所在的路徑及資源名
句柄到底是什么東東呢,指針呢?
其實,句柄並沒有什么神奇之處,不管哪種句柄,實際都是一個整數。它標識一種資源,如窗口、位圖等等。就象你找一個人,必須知道它的地址一樣,如果你要操作一種資源,必須先獲得句柄。“取窗口句柄()”並不是只能取出窗口的句柄,所有窗口控件,如編輯框、標簽等都可以用本命令取出自己的句柄,如:編輯框1.取窗口句柄()或標簽1.取窗口句柄()。控件的句柄同樣,任何控件都有它自身的特有屬性,句柄也就指它的特有屬性(包括共性)。
句柄英文譯作HANDLE,HANDLE的本意是把柄,把手的意思,是與操作系統打交道的東東。有人舉過比較通俗的例子:你考上了大學,入學后,學校(操作系統)會給你一個學生證號。注意,這個號碼是學校指定的,你無法自選。有了這個號碼(學生證,假設一證多用)享受學校提供的服務:如你就可以去圖書館借書,去食堂吃飯,去教室上課等 等。但你不能到食堂里買啤酒,因為學校不允許這種服務。而在計算機中系統提供的服務就是API調用了。當你有了HANDLE,就可以理直氣壯地向系統提出調用API的服務。而指針的權力就大多了,有了指針你可以到處去喝酒,打架,學校(操作系統)管不着,所以句柄和指針的區別在於句柄只能調用系統提供的服務。而句柄雖然是一個能相互區別的號碼,但與我們普通的ID號又有區別,普通的ID號是可以由程序員自己定義的,而句柄不行,它是對象生成時系統指定的,是為了區別系統中存在的各個對象,這個句柄不是由程序員賦給的。
可以引用csdn上一個人的話來說明句柄,指針對象實例之間的關系:
牧童遙指杏花村。牧童的手為指針,杏花村的牌子為句柄,杏花村酒店為對象的實例。
深入探討句柄
更透徹的說,句柄是一種指向指針的指針。大家都知道,所謂指針是一種內存地址。應用程序啟動后,組成這個程序的各對象是住留在內存的。如果簡單地理解,似乎我們只要獲知這個內存的首地址,那么就可以隨時用這個地址訪問對象。但是,如果您真的這樣認為,那么您就大錯特錯了。我們知道,Windows是一個以虛擬內存為基礎的操作系統。在這種系統環境 下,Windows內存管理器經常在內存中來回移動對象,依此來滿足各種應用程序的內存需要。對象被移動意味着它的地址變化了。如果地址總是如此變化,我們該到哪里去找該對象呢? 為了解決這個問題,Windows操作系統為各應用程序騰出一些內存儲地址,用來專門登記各應用對象在內存中的地址變化,而這個地址(存儲單元的位置)本 身是不變的。Windows內存管理器在移動對象在內存中的位置后,把對象新的地址告知這個句柄地址來保存。這樣我們只需記住這個句柄地址就可以間接地知道對象具體在內存中的哪個位置。這個地址是在對象裝載(Load)時由系統分配給的,當系統卸載時(Unload)又釋放給系統。句柄地址(穩定) ─→記載着對象在內存中的地址─→對象在內存中的地址(不穩定) ─→實際對象 本質:WINDOWS程序中並不是用物理地址來標識一個內存塊,文件,任務或動態裝入模塊的,相反的,WINDOWS API給這些項目分配確定的句柄,並將句柄返回給應用程序,然后通過句柄來進行操作。 但是必須注意的是程序每次從新啟動,系統不能保證分配給這個程序的句柄還是原來的那個句柄,而且絕大多數情況的確不一樣的。假如我們把進入電影院看電影看 成是一個應用程序的啟動運行,那么系統給應用程序分配的句柄總是不一樣,這和每次電影院售給我們的門票總是不同的一個座位是一樣的道理。
MFC _窗口ID,句柄,指針三者相互轉換函數
ID--HANDLE--HWND三者之間的互相轉換
id->句柄 hWnd = ::GetDlgItem(hParentWnd,id);
id->指針 CWnd::GetDlgItem();
句柄->id id = GetWindowLong(hWnd,GWL_ID);
句柄->指針 CWnd *pWnd=CWnd::FromHandle(hWnd);
指針->ID id = GetWindowLong(pWnd->GetSafeHwnd,GWL_ID);
GetDlgCtrlID();
指針->句柄 hWnd=cWnd.GetSafeHandle() or mywnd->m_hWnd;
指針的使用在編程過程中至關重要,恰到好處並能正確無誤的使用指針不但能夠提高程序自身的運行效率,而且有助於節省程序執行所需要消耗的資源。指針對應着某個數據在內存空間中的地址,得到了指針就可以自由地修改該數據。句柄代表指針的“指針”,也可以將其比作表中數據項的索引值( 表對應某個進程自身的內存空間 )。句柄是間接的引用對象。
指針和句柄的不同之處:
- 句柄所指的可以是一個很復雜的結構,並且很有可能與系統有關的,比如上面所說線程的句柄,它指向的就是一個類或者結構,它和系統有很密切的關系。當一個線程由於不可預料的原因而終止時,系統就可以通過句柄來回收它所占用的資料,如CPU,內存等等。反過來想,這些句柄中的某一些,是與系統進行交互用的。
- 指針它也可以指向一個復雜的結構,但通常是由用戶自我定義的,所以一些必需的工作都要由用戶自己完成,特別是在刪除的時候。
- 另外需要注意的是句柄往往有自己的存在區限,比如一個進程,如果將其傳遞到另一個進程中,句柄也就失去了意義( 表中數據項的索引值,索引離開了具體的表也就失去了意義 )。
具體轉換:
( 句柄轉為指針 )
CWnd* pWnd=FromeHandle(hMyHandle);
pWnd->SetWindowText("Hello World!");
or
CWnd* pWnd;
pWnd->Attach(hMyHandle);
MFC類中有的還提供了標准方法,比如Window句柄:
static CWnd* PASCAL FromHandle(
HWND hWnd
);
HWND GetSafeHwnd( ) const;
對於位圖:
static CBitmap* PASCAL FromHandle(
HBITMAP hBitmap
);
static CGdiObject* PASCAL FromHandle(
HGDIOBJ hObject
);
HGDIOBJ GetSafeHandle( ) const;
當然,更詳細的信息需要在具體使用中自我查詢。
建 議:指針和句柄的使用屬於比較復雜、危險性較高的應用,在具體實踐中,如果可以,盡量不要使用指針和句柄,最好選擇現有的、封裝完好的方式來實現,更別提指針同句柄的轉換了,它更加危險。
比如在操作字符串時,盡量使用CString類來實現,通過定義好的構造、析構函數來完成分配和回收,最好不要通過指針來動態操作。