SDK的WIN32 API用句柄HANDLE操作Windows窗口,而VC將HANDLE封裝為CWnd類的一個成員變量m_hWnd,可以取此對象的m_hWnd屬性來得到句柄。同時,VC中每一個窗口類都可以聲明為一個窗口指針來調用窗口類的成員屬性和成員變量。
窗口是Windows應用程序中一個非常重要的元素,一個Windows應用程序至少要有一個窗口,稱為主窗口,窗口是指現在是屏幕上面的一快矩形區域,是Windows應用程序與用戶進行交互的接口。利用窗口,可以接收用戶的輸入及顯示輸出。
一個應用程序窗口通常包含標題欄、菜單欄、系統菜單、最小化框、最大化框、可調邊框、滾動條等
窗口可以分為客戶區和非客戶區,客戶區是窗口的一部分,應用程序通常在客戶區中顯示文字或繪制圖形。而標題欄、菜單欄、系統菜單、最小化和最大化,可調系統邊框為窗口的非客戶區,他們由windows來管理,而應用程序則主要管理客戶區的外觀及操作。
窗口可以有一個父窗口,有父窗口的窗口稱為子窗口,另外,對話框和消息框也是一種窗口,在對話框上面還包含許多子窗口,這些子窗口的形式有按鈕、單選按鈕、復選框、組框、文本編輯框等。
此外,在我們啟動Windows系統后,我們的桌面也是一個窗口,稱為桌面窗口,是位于最上層的窗口,由Windows系統創建和管理。
當用你CreateWindow創建了一個窗口,這個窗口其實并不是你創建的,而是系統替你的創建的,系統為創建這個窗口,它必須要保留很多的和這個窗口相關的數據,這些數據并不是給你用的,而是系統用來維護窗口用的,而句柄就是系統用來找到這個窗口相關數據的一人索引。你向一個API提供一個句柄,比如ShowWindow(HWND);要顯示這個窗口,這個工作還是系統替你做的,它根據你提供的索引,也就是句柄,找到窗口,然后進行相關的處理。所以說,句柄是相關對象的唯一索引。從這一點上看,有點像指針,但是指針的內容是對象的地址,而句柄并不是地址,句柄只是一個索引,一個整數。
句柄的實質就是一個結構體的實例。
Windows系統中有許多內核對象(這里的對象不完全等價于"面向對象程序設計"一詞中的"對象",雖然實質上還真差不多),比如打開的文件,創建的線程,程序的窗口,等等。這些重要的對象肯定不是4個字節或者8個字節足以完全描述的,他們擁有大量的屬性。為了保存這樣一個"對象"的狀態,往往需要上百甚至上千字節的內存空間,那么怎么在程序間或程序內部的子過程(函數)之間傳遞這些數據呢?拖著這成百上千的字節拷貝來拷貝去嗎?顯然會浪費效率。那么怎么辦?當然傳遞這些對象的首地址是一個辦法,但這至少有兩個缺點:
I 暴露了內核對象本身,使得程序(而不是操作系統內核)也可以任意地修改對象地內部狀態(首地址都知道了,還有什么不能改的?),這顯然是操作系統內核所不允許的;
II 操作系統有定期整理內存的責任,如果一些內存整理過一次后,對象被搬走了怎么辦?
所以,Windows操作系統就采用進一步的間接:在進程的地址空間中設一張表,表里頭專門保存一些編號和由這個編號對應一個地址,而由那個地址去引用實際的對象,這個編號跟那個地址在數值上沒有任何規律性的聯系,純粹是個映射而已。
在Windows系統中,這個編號就叫做"句柄"。
在Windows應用程序中,窗口是通過句柄HWND來標識的,我們要對某個窗口進行操作,首先就要獲取到這個窗口的句柄。
每個窗口在被創建出來之后就會被賦予一個句柄,該句柄指向一個數據結構體,結構體里明確表示著該窗口的各種信息,窗口大小,窗口名等,當我們得到這個句柄時就可以請求操作系統對它做一系列操作,列如:移動窗口,關閉窗口,最小化最大化等,這些都是通過窗口句柄來告訴操作系統的,我們要對哪個窗口進行操作,而消息則是告訴操作系統要做什么樣的操作,消息的附加參數就是操作值,列如移動窗口,會有附加的xy坐標參數!
句柄有Windows句柄,文件句柄,分配內存句柄,圖形句柄等,系統在創建這次資源的時候回為其分配內存,并返回標識這些資源的標識號,這個標識號就是句柄,實際上,我們可以將句柄看做是指針。
在使用句柄之前,必須先創建他們,當不在使用時,應當及時銷毀,如果不銷毀他們,最終將到時資源泄露,資源泄露有可能導致系統崩潰,所以,務必確保在適當的時候銷毀不在使用的句柄。
另外,程序運行時也是以進程存在,進程也是用ID或句柄去標識。
進程創建時,windows系統為進程構造了一個句柄表。當該進程希望獲得一個內核對象句柄或者創建一個內核對象從而獲得該對象句柄時。系統會將在句柄表中增加一個表項,表項的內容中存儲了指向目標內核對象的指針。同時,系統返回這個表項在句柄表中的索引作為句柄。這樣,進程就通過句柄查詢句柄表得到對象指針,從而可以訪問該對象。同時又由于有了句柄表的保護,可以防止對內核對象的非法操作。
windows對象并不是我們平時所說的“面向對象”程序設計中的“類的對象”,而是一種windows資源實體,如畫筆、字體等。
如果想要去使用這些windows對象我們需要用句柄來標識它們。
而MFC對象則是真正的“面向對象”思想中的“類的對象”(必須用構造函數去創建)。
在windows編程中,除了普通的“類的對象”外,用得最多的“C++類的對象”應該是MFC對象了(如果你是用MFC編程的話),
MFC對象是指“封裝了windows對象的C++對象”(也就是MFC對象中有一個控制window對象的控制器,那么控制器想工作就得和一個window對象鏈接起來,這樣就能控制器就能控制這個對象了)。
所謂Windows對象是Win32下用句柄表示的Windows操作系統對象;
所謂MFC對象就是C++對象,是一個C++類的實例.
一個MFC窗口對象是一個C++ CWnd類(或派生類)的實例,是程序直接創建的。
在程序執行中它隨著窗口類構造函數的調用而生成,隨著析構函數的調用而消失。
而Windows窗口則是Windows系統的一個內部數據結構的實例,由一個“窗口句柄”標識,Windows系統創建它并給它分配系統資源。
Windows窗口在MFC窗口對象創建之后,由CWnd類的Create成員函數創建,“窗口句柄”保存在MFC窗口對象的m_hWnd成員變量中。
Windows窗口可以被一個程序銷毀,也可以被用戶的動作銷毀。
MFC中的窗口類,就是C++類與Windows窗口的句柄的結合。在基于CWnd繼承而來的所有類中,都有一個公有的成員變量m_hWnd,這個成員變量就是窗口對象關聯的windows窗口句柄。我們在類中可以直接使用這個窗口句柄成員變量。這個窗口對象就是標準的C++對象。其實MFC窗口類并不神奇,就是包裝了一下API而已。m_hWnd的類型就是HWND。MFC給CWnd提供了兩個成員函數Attach(HWND hWndNew )和Detach()。前者傳入一個窗口句柄,將該窗口關聯到C++對象,后者則是將當前關聯的窗口解關聯。而關聯就是給m_hWnd賦值,解關聯就是將m_hWnd設置為NULL。解關聯后,窗口對象就不關聯任何窗口了,此時就不能執行窗口相關的任何操作,都會失敗。如果要關聯新的窗口,只要執行Attach函數即可。
4.1 MFC全局函數,如:
AfxMessageBox 顯示一個消息框
AfxGetApp 返回應用程序對象CWinApp的一個指針
AfxGetAppName 返回應用程序的名稱
AfxInitRichEdit 為應用程序初始化RichEdit控件
4.2 API函數,如:
SendMessage(),調用一個窗口的窗口函數,將一條消息發給那個窗口;
OpenFile() 這個函數能執行大量不同的文件操作;
RegCreateKey() 在指定的項下創建或打開一個項;
GetCaretPos() 判斷插入符的當前位置;
ShellExecute() 用指定程序打開指定路徑下的文件;
4.3 其父類定義的成員函數,如CWnd類:
PreSubclassWindow()
在調用SubclassWindow之前,允許其它必要的子類化工作
FromHandle()
當給定一個窗口的句柄時,返回CWnd對象的指針。如果沒有CWnd對象與這個句柄相連接,則創建一個臨時的CWnd對象并與之相連接
GetSafeHwnd
返回m_hWnd,如果該指針為NULL,則返回NULL
etFocus()
要求輸入焦點
SetWindowPos()
改變子窗口、彈出窗口和頂層窗口的大小、位置以及順序
GetClientRect
獲得CWnd客戶區域的大小
GetDlgItem(),
獲得指定的對話框中具有指定ID的控件
UpdateData()
初始化對話框或獲得對話框中的數據
SetWindowText()
將窗口的文本或標題文字(如果有)設為指定的文本
SetWindowText
將窗口的文本或標題文字(如果有)設為指定的文本
SetTimer()
安裝一個系統定時器,當它被激活時,發送一個WM_TIMER消息
MessageBox()
創建并顯示一個窗口,其中包含了應用程序提供的消息和標題
SendMessage()
向CWnd對象發送一個消息,直到這條消息被處理之后才返回
OnPaint()
調用這個函數以重畫窗口的一部分
OnLButtonDown()
當用戶按下鼠標左鍵時調用這個函數
OnTimer()
當達到SetTimer指定的時間間隔時調用這個函數
4.4 繼承關系不同的GetDlgItem()
1) CWindow::GetDlgItem HWND GetDlgItem(int nID)const; 2) CWnd::GetDlgItem CWnd* GetDlgItem(int nID) const; void CWnd::GetDlgItem( int nID, HWND *phWnd) const; 3)Windows SDK HWND GetDlgItem(HWND hDlg,int nIDDlgItem);
先看下面的兩行代碼,簡單了解一下窗口句柄和指針的一個使用場合:
5.1 API函數以句柄為參數
CString str="在編輯框上顯示給定文本。"; SendMessage(GetActiveWindow()->GetDlgItem(IDC_textbox)->m_hWnd, WM_SETTEXT , 0 , (LPARAM)str.GetBuffer(0));
此代碼可以在當前活動窗口的IDC_textbox文本框控件中顯示一個字符串str。
① API函數SendMessage()功能:將指定的消息發送到一個或多個窗口。
LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam)
hWnd:其窗口程序將接收消息的窗口的句柄。如果此參數為HWND_BROADCAST,則消息將被發送到系統中所有頂層窗口,包括無效或不可見的非自身擁有的窗口、被覆蓋的窗口和彈出式窗口,但消息不被發送到子窗口。
Msg:指定被發送的消息。
wParam:指定附加的消息特定信息。
IParam:指定附加的消息特定信息。
返回值:指定消息處理的結果,依賴于所發送的消息。
② CWnd類方法GetDlgItem(IDC_textbox),能夠返回一個MFC指針*CWnd;
③ CWnd類方法GetActiveWindow(),可以返回當前窗口的一個HWND句柄;
④ “->m_hWnd”是將一個指針轉換為句柄。一般窗口對象都會有一個其對應的句柄變量,所以我們可以取此對象的m_hWnd屬性來得到句柄。
5.2 MFC對象指針調用成員函數
下面的的代碼也是在編輯框上顯示給定文件:
CString str="在編輯框上顯示給定文本。"; CEdit *edit1=(CEdit*)GetDlgItem(IDC_EDIT2); edit1->SetWindowText(str);
GetDlgItem()可以返回一個MFC指針*CWnd,并用(CEdit*)做了下類型轉換,然后就可以使用CEdit類的成員函數,例如SetWindowText()。
5.3 API函數使用句柄作為參數
CString str="F:\\help\\list.html"; API函數ShellExecute(this->m_hWnd, "open", str,"", NULL, SW_SHOW);
this->m_hWnd; //返回的就是窗口類自己的句柄呢,也可以用MFC全局函數:
AfxGetMainWnd()->m_hWnd;
對MFC編程來說,對話框和控件都可以理解為一個子窗口,都有對應的一個ID和類。可以返回對應的對象指針。
HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)
HWND FindWindowEx(HWND hwndParent, HWND hwndChildAfter,LPCTSTR lpClassName, LPCTSTR lpWindowName)
HWND WindowFromPoint(POINT& Point)//獲得當前鼠標光標位置的窗口HWND
在任何類中獲得應用程序類
HWND AfxGetInstanceHandle()
獲取當前活動窗口句柄
HWND GetActiveWindow(VOID)
獲取前臺窗口句柄
HWND GetForegroundWindow(void);
獲得對話框中某控件的句柄
HWND GetDlgItem(m_hDLG,m_nID_DlgItem);
獲得GDI對象的句柄
HWND m_hGDIObj=m_pGDIObj->GetSafeHanle();
① 獲取當前應用進程的指針
AfxGetApp();
② 獲得主框架窗口指針(任何時候都可以用,只要是MFC程序中)
CWnd* pWnd=AfxGetMainWnd();
③ 獲得對話框中控件指針
CWnd* pWnd=GetDlgItem(IDC_xxx);
可以強制轉換為具體類型,如CEdit *edit1=(*CEdit)GetDlgItem(IDC_xxx);
④ 獲得工具欄指針
CToolBar * pToolBar=(CtoolBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);
① 指針轉化為句柄
在MFC應用程序中首先要獲得窗口的指針,然后將其轉化為句柄
CWnd *pwnd=FindWindow(“ExploreWClass”,NULL); //希望找到資源管理器 HWND hwnd=pwnd->m_hwnd; //得到它的HWND
這樣的代碼當開始得到的pwnd為空的時候就會出現一個“General protection error”,并關閉應用程序,因為一般不能對一個NULL指針訪問其成員,如果用下面的代碼:
CWnd *pwnd=FindWindow(“ExploreWClass”,NULL); //希望找到資源管理器 HWND hwnd=pwnd->GetSafeHwnd(); //得到它的HWND
就不會出現問題,因為盡管當pwnd是NULL時,GetSafeHwnd仍然可以用,只是返回NULL
② 句柄轉化為指針
在MFC應用程序中首先用GetDlgItem()獲得對話框控件的句柄,然后獲得其指針:
HANDLE hWnd; GetDlgItem(IDC_xxx,&hWnd); CWnd *pWnd=CWnd::FromHandle(hWnd); pWnd->Attach(hWnd); //Attaches a Windows to a CWnd object
// 用FindWindow()得到一個窗口句柄,然后轉換為容器指針
HWND hWnd=::FindWindow(NULL,_T("IDD_Assistants")); //得到對話框的句柄 CAssistantsDialog* pWnd=(CAssistantsDialog*)CAssistantsDialog::FromHandle(hWnd); //由句柄得到對話框的對象指針 pWnd- >xxx( ); //調用C***Dialog中的函數xxx();
ID->句柄,hWnd=::GetDlgItem(hParentWnd,id);
ID->指針,CWnd::GetDlgItem();
句柄->ID,id=GetWindowLong(hWnd,GWL_ID);
指針->ID,id=GetWindowLong(pWnd->GetSafeHwnd,GWL_ID);
HICON->ID,HICON hIcon=AfxGetApp()->LoadIcon(nIconID);
ID->HICON,HICON hIcon=LoadIcon(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(nIconID));
-End-
句柄(handle)是一個32位(4個字節,64位程序中為8字節)的無符號整數,實際上是Windows在內存中維護的一個對象(窗口等)內存物理地址(32或64位)列表的整數索引。因為Windows的內存管理經常會將當前空閑對象的內存釋放掉,當需要訪問時再重新提交到物理內存,所以對象的物理地址是變化的,不允許程序直接通過物理地址來訪問對象。程序將想訪問的對象的句柄傳遞給系統,系統根據句柄檢索自己維護的對象列表就能知道程序想訪問的對象及共物理地址了。
我們知道,所謂指針是一種內存地址。應用程序啟動后,組成這個程序的各對象是駐留在內存的。如果簡單地理解,似乎我們只要獲知這個內存的首地址,那么就可以隨時用這個地址訪問對象。但是,Windos是一個以虛擬內存為基礎的操作系統。在這種系統環境下,Windows內存管理器經常在內存中來回移動對象,以此來滿足各種應用程序的內存需要。對象被移動意味著它的地址變化了。如果內存總是如此變化,我們該到哪里去找該對象呢?為了解決這個問題,Windows系統為進程分配固定的地址(句柄)來存儲進程下的數據對象變化后的地址。Windows操作系統為各應用程序騰出一些內存地址,用來專門登記各應用對象在內存中的地址變化,而這個地址本身是不會變的。Windows內存管理器移動對象在內存中的位置后,把對象新的地址告知這個句柄地址來保存。這樣我們只需要記住這個句柄地址就可以間接地知道對象具體在內存中的哪個位置。這個地址是在對象裝載時由系統分配的,當系統卸載時又釋放給系統。但是,必須注意的是,程序每次重新啟動,系統不能保證分配給這個程序的句柄還是原來的那個句柄。而且絕大多數情況下的確不一樣。假如我們把進入電影院看電影看成是一個應用程序的啟動運行,那么系統給應用程序分配的句柄總是不一樣,這和每次電影院售給我們的門票總是不同的座位是一樣的道理。
所以,對于winows應用程序中的不同對象和同類中的不同的實例,諸如,一個窗口、按鈕、圖標、滾動條、輸出設備、控件或者文件等,數據對象加載進入內存中之后即獲得了地址,但Windows并不允許應用程序直接通過這個地址來訪問這些對象,而是通過標識或索引指針的句柄來訪問相應的對象的信息。
在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)。
(HINSTANCE(實例句柄),HBITMAP(位圖句柄),HDC(設備描述表句柄,相應的,CDC是設備描述表類),HICON(圖標句柄)等等,這當中還有一個通用的句柄,就是HANDLE。)
上面的這些項目,通過其指針去定位是無法找到或不穩定的,所以要使用這些項目,必須獲取其句柄,句柄才是唯一標識穩定的定位。所以,如果想再通俗一定理解項目的指針與句柄,可以想像有一票供展覽貨品,數量比較多,儲存在某一個公司的倉庫里,但倉庫的位置是有限的,可能儲存了很多票這樣的貨,這些貨因為不同的消耗進度,所以數量的增減會不一樣,為此,不同票的貨需要在不同的位置移動,以確保位置的最大化利用。怎么做呢?倉庫的位置編號,也就是內存地址對應的指針,然后每票貨都有一個在整個公司范圍內唯一的條碼號,這就是句柄。這個公司只需要維護一份條碼號與指針對應的索引表格就可以了。貨物移動時,將目的地的指針更改到條碼號。想要找貨物時,只需要在索引表格中找到這個條碼號,就可以查找到貨位的指針及其具體的位置了。這樣既可以保證倉庫空間的利用,也有方便地找到貨位。
句柄是對象生成時系統指定的,是引用指針的一個標識值或ID號,該標識可以被系統重新定位到一個內存地址上。句柄類似指向指針的指針,僅僅是類似,通過句柄可以找到對應的數據,但是不是二級指針。
句柄與普通指針的區別在于,指針包含的是引用對象的內存地址,而句柄則是由系統所管理的引用標識,該標識可以被系統重新定位到一個內存地址上。這種間接訪問對象的模式增強了系統對引用對象的控制。
在內存有限的年代,為了使有限的內存資源得到充分利用,有了虛擬內存和虛擬地址的概念或內存管理方式,相應的,也就有了句柄這樣一個編程概念。如今,內存容量的增大和虛擬內存算法使得更簡單的指針愈加受到青睞,而指向另一指針的那類句柄受到冷落。
指針標記某個物理內存地址,Windows系統用句柄標記系統資源,隱藏系統的信息。
以上是Windows編程對句柄的理解。而在一般的程序設計中,句柄是一種特殊的智能指針 。當一個應用程序要引用其他系統(如數據庫、操作系統)所管理的內存塊或對象時,就要使用句柄。
Windows系統中有許多內核對象(這里的對象不完全等價于"面向對象程序設計"一詞中的"對象",雖然實質上還真差不多),比如打開的文件,創建的線程,程序的窗口,等等。這些重要的對象肯定不是4個字節或者8個字節足以完全描述的,他們擁有大量的屬性。為了保存這樣一個"對象"的狀態,往往需要上百甚至上千字節的內存空間,那么怎么在程序間或程序內部的子過程(函數)之間傳遞這些數據呢?拖著這成百上千的字節拷貝來拷貝去嗎?顯然會浪費效率。那么怎么辦?當然傳遞這些對象的首地址是一個辦法,但這至少有兩個缺點:
所以,Windows操作系統就采用進一步的間接:在進程的地址空間中設一張表,表里頭專門保存一些編號和由這個編號對應一個地址,而由那個地址去引用實際的對象,這個編號跟那個地址在數值上沒有任何規律性的聯系,純粹是個映射而已。
所以,Windows操作系統就采用進一步的間接:在進程的地址空間中設一張表,表里頭專門保存一些編號和由這個編號對應一個地址,而由那個地址去引用實際的對象,這個編號跟那個地址在數值上沒有任何規律性的聯系,純粹是個映射而已。
在Windows系統中,這個編號就叫做"句柄"。
從廣義上,能夠從一個數值拎起一大堆數據的東西都可以叫做句柄。句柄的英文是"Handle",本義就是"柄",只是在計算機科學中,被特別地翻譯成"句柄",其實還是個"柄"。從一個小東西拎起一大堆東西,這難道不像是個"柄"嗎?
Handle本身是一個32或64位的無符號整數,它用來代表一個內核對象。它并不指向實際的內核對象,用戶模式下的程序永遠不可能獲得一個內核對象的實際地址(一般情況下)。那么Handle的意義何在?它實際上是作為一個索引在一個表中查找對應的內核對象的實際地址。那么這個表在哪里呢?每個進程都有這樣的一個表,叫句柄表。該表的第一項就是進程自己的句柄,這也是為什么你調用GetCurrentProcess()總是返回0x7FFFFFFF原因。
簡單地說,Handle就是一種用來"間接"代表一個內核對象的整數值。你可以在程序中使用handle來代表你想要操作的內核對象。這里的內核對象包括:事件(Event)、線程、進程、Mutex等等。我們最常見的就是文件句柄(file handle)。
另外要注意的是,Handle僅在其所屬的進程中才有意義。將一個進程擁有的handle傳給另一個進程沒有任何意義,如果非要這么做,則需要使用DuplicateHandle()在多個進程間傳遞Handle。
MFC是微軟公司提供的一個類庫(class libraries),以C++類的形式封裝了Windows API,并且包含一個應用程序框架,以減少應用程序開發人員的工作量。其中包含大量Windows句柄封裝類和很多Windows的內建控件和組件的封裝類。
所謂Windows Object(Windows對象)是Win32下用句柄表示的Windows操作系統對象;所謂MFC Object (MFC對象)是C++對象,是一個C++類的實例,這里MFC Object是有特定含義的,指封裝Windows Object的C++ Object,并非指任意的C++ Object。
MFC Object 和Windows Object是不一樣的,但兩者緊密聯系。以窗口對象為例:
一個MFC窗口對象是一個C++ CWnd類(或派生類)的實例,是程序直接創建的。在程序執行中它隨著窗口類構造函數的調用而生成,隨著析構函數的調用而消失。而Windows窗口則是Windows系統的一個內部數據結構的實例,由一個“窗口句柄”標識,Windows系統創建它并給它分配系統資源。Windows窗口在MFC窗口對象創建之后,由CWnd類的Create成員函數創建,“窗口句柄”保存在窗口對象的m_hWnd成員變量中。Windows窗口可以被一個程序銷毀,也可以被用戶的動作銷毀。MFC窗口對象和Windows窗口對象的關系如圖2-1所示。其他的Windows Object和對應的MFC Object也有類似的關系。
MFC給這些類定義了一個成員變量來保存MFC Object對應的Windows Object的句柄。對于設備描述表CDC類,將保存兩個HDC句柄。
MFC Object通過構造函數由程序直接創建;Windows Object由相應的SDK函數創建。
MFC中,使用這些MFC Object,一般分兩步:
首先,創建一個MFC Object,或者在STACK中創建,或者在HEAP中創建,這時,MFC Object的句柄實例變量為空,或者說不是一個有效的句柄。
然后,調用MFC Object的成員函數創建相應的Windows Object,MFC的句柄變量存儲一個有效句柄。
(句柄經常做為函數返回值或函數參數而存在。)
MFC Object和Windows Object的對應關系:
描述 | Windows句柄 | MFC Object |
窗口 | HWND | CWnd and CWnd-derived classes |
設備上下文 | HDC | CDC and CDC-derived classes |
菜單 | HMENU | CMenu |
筆 | HPEN | CGdiObject類,CPen和CPen-derived classes |
刷子 | HBRUSH | CGdiObject類,CBrush和CBrush-derived classes |
字體 | HFONT | CGdiObject類,CFont和CFont-derived classes |
位圖 | HBITMAP | CGdiObject類,CBitmap和CBitmap-derived classes |
調色板 | HPALETTE | CGdiObject類,CPalette和CPalette-derived classes |
區域 | HRGN | CGdiObject類,CRgn和CRgn-derived classes |
圖像列表 | HimageLIST | CimageList和CimageList-derived classes |
套接字 | SOCKET | CSocket,CAsynSocket及其派生類 |
可以從一個MFC Object得到對應的Windows Object的句柄;一般使用MFC Object的成員函數GetSafeHandle得到對應的句柄。
可以從一個已存在的Windows Object創建一個對應的MFC Object; 一般使用MFC Object的成員函數Attach或者FromHandle來創建,前者得到一個永久性對象,后者得到的可能是一個臨時對象。
從指針到句柄 :
CWnd *pWnd=GetDlgItem(ID_***); // 取得控件的指針,CWnd是窗口類
HWND hwnd=pWnd->GetSafeHwnd(); // 取得控件的句柄
hdc=GetDC(hwnd);
有了DC綽號hdc,就可以畫圖了:
MoveToEx( hdc,0, y_shift-(draw_yu-y_min)*ry, NULL );
LineTo(hdc,900,y_shift-(draw_yu-y_min)*ry);
CPaintDC dc(this); // device context for painting
//CPaintDC是CDC的一個派生類,只是在OnPaint()函數中使用;
CPen pen1(PS_SOLID, 1, RGB(198, 198, 198));
//把畫筆選到設置描述表當中.覆蓋默認畫筆.
CDC* pDC=pWnd->GetWindowDC(); //取得CDC的指針
//或CDC *pDC=GetDC();
dc.SelectObject(&pen1);
dc.MoveTo(0,455);//移動到該坐標
dc.LineTo(917,455); //畫線畫到這個點
pDC->TextOut(92,50,"(如:http://www.ifeng.com)");
從句柄到指針 :
CComBox* com; //聲明一個CComBox類的指針
HWND hwnd=GetDlgItem(hwnd,IDC_XX)->GetSafeHwnd();
com=(CComBox*)FromHandle(hwnd);
相當于
CWnd* pCtrl=this->FromHandle(::GetDlgItem(hwnd,IDC_XX));
注意這里FromHandle是CWnd的一個靜態函數,也就是說只能用在CWnd或者CWnd的子類,如CDIalog,或者你自己的繼承自CWnd的類中。
使用控件指針:
CWnd *pWnd=GetDlgItem(ID_***); // 取得控件的指針,CWnd是窗口類
HWND hwnd=pWnd->GetSafeHwnd(); // 取得控件的句柄
CRichEditCtrl* pRichEdit=(CRichEditCtrl*)(CWnd::FromHandle(hwnd));//hwnd是上面兩句獲得的句柄,轉化為對應控件的指針
CString szRichMsg;
szRichMsg.Format("%s",msg.c_str());
CString szUname;
std::string uname=stanza->from().username();
szUname.Format("%s",uname.c_str());
pRichEdit->ReplaceSel((szUname+"\t"+m_Time)+"\n"+szRichMsg+"\n");//使用控件指針
以下用控件指針也是可以操作控件的:
CEdit *edit1=(CEdit*)GetDlgItem(IDC_EDIT2);
edit1->GetWindowText(m_text);
ID--句柄--指針三者之間的互相轉換
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;
HICON->ID--------HICON hIcon=AfxGetApp()->LoadIcon(nIconID);
HICON hIcon=LoadIcon(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(nIconID));
Windows窗口句柄是用來確定Windows窗口的一個值,在win32編程中,都是很常見的。但是MFC為了簡化編程就提供了CWnd窗口類將其包裝了,然后對API函數封裝后,就在創建對象時將窗口句柄關聯到對象,然后提供的成員函數就可以省掉窗口句柄參數了。所以,在基于CWnd繼承而來的所有類中,都有一個公有的成員變量m_hWnd,這個成員變量就是窗口對象關聯的windows窗口句柄。我們在類中可以直接使用這個窗口句柄成員變量。這個窗口對象就是標準的C++對象。
其實MFC窗口類并不神奇,就是包裝了一下API而已。m_hWnd的類型就是HWND。因為窗口類都是一個C++對象,而C++對象就是我們平時使用C++時用的類實例化而來的。這么一說,其實窗口類和窗口對象其實就是將窗口句柄作為C++對象的一個成員,然后創建時關聯的而已。在CreateWindow函數中,傳入了窗口的ID,這樣就讓窗口類可以獲得這個窗口ID的窗口句柄,進而保存到窗口對象成員m_hWnd中。這樣就形成了關聯。這樣就創建了一個窗口對象。
MFC給這些類定義了一個成員變量來保存MFC Object對應的Windows Object的句柄。對于設備描述表CDC類,將保存兩個HDC句柄。
CDC類有兩個成員變量:m_hDC,m_hAttribDC,它們都是Windows設備描述表句柄。CDC的成員函數作輸出操作時,使用m_Hdc;要獲取設備描述表的屬性時,使用m_hAttribDC。
void CMyView::OnDraw(CDC *pDC)
{
CPen penBlack; //構造MFC CPen對象
if (penBlack.CreatePen(PS_SOLID, RGB(0, 0, 0)))
{
CPen *pOldPen=pDC->SelectObject(&penBlack)); //選進設備表,保存原筆
…
pDC->SelectObject(pOldPen); //恢復原筆
}else
{
…
}
}
pDC->SelectObject(&penBlack)返回了一個CPen *指針,也就是說,它根據原來PEN的句柄創建了一個MFC CPen對象。
-End-