SDK的WIN32 API用句柄HANDLE操作Windows窗口,而VC将HANDLE封装为CWnd类的一个成员变量m_hWnd,可以取此对象的m_hWnd属性来得到句柄。同时,VC中每一个窗口类都可以声明为一个窗口指针来调用窗口类的成员属性和成员变量。
1 窗口窗口是Windows应用程序中一个非常重要的元素,一个Windows应用程序至少要有一个窗口,称为主窗口,窗口是指现在是屏幕上面的一快矩形区域,是Windows应用程序与用户进行交互的接口。利用窗口,可以接收用户的输入及显示输出。
一个应用程序窗口通常包含标题栏、菜单栏、系统菜单、最小化框、最大化框、可调边框、滚动条等
窗口可以分为客户区和非客户区,客户区是窗口的一部分,应用程序通常在客户区中显示文字或绘制图形。而标题栏、菜单栏、系统菜单、最小化和最大化,可调系统边框为窗口的非客户区,他们由windows来管理,而应用程序则主要管理客户区的外观及操作。
窗口可以有一个父窗口,有父窗口的窗口称为子窗口,另外,对话框和消息框也是一种窗口,在对话框上面还包含许多子窗口,这些子窗口的形式有按钮、单选按钮、复选框、组框、文本编辑框等。
此外,在我们启动Windows系统后,我们的桌面也是一个窗口,称为桌面窗口,是位于最上层的窗口,由Windows系统创建和管理。
2 窗口句柄当用你CreateWindow创建了一个窗口,这个窗口其实并不是你创建的,而是系统替你的创建的,系统为创建这个窗口,它必须要保留很多的和这个窗口相关的数据,这些数据并不是给你用的,而是系统用来维护窗口用的,而句柄就是系统用来找到这个窗口相关数据的一人索引。你向一个API提供一个句柄,比如ShowWindow(HWND);要显示这个窗口,这个工作还是系统替你做的,它根据你提供的索引,也就是句柄,找到窗口,然后进行相关的处理。所以说,句柄是相关对象的唯一索引。从这一点上看,有点像指针,但是指针的内容是对象的地址,而句柄并不是地址,句柄只是一个索引,一个整数。
句柄的实质就是一个结构体的实例。
Windows系统中有许多内核对象(这里的对象不完全等价于"面向对象程序设计"一词中的"对象",虽然实质上还真差不多),比如打开的文件,创建的线程,程序的窗口,等等。这些重要的对象肯定不是4个字节或者8个字节足以完全描述的,他们拥有大量的属性。为了保存这样一个"对象"的状态,往往需要上百甚至上千字节的内存空间,那么怎么在程序间或程序内部的子过程(函数)之间传递这些数据呢?拖着这成百上千的字节拷贝来拷贝去吗?显然会浪费效率。那么怎么办?当然传递这些对象的首地址是一个办法,但这至少有两个缺点:
I 暴露了内核对象本身,使得程序(而不是操作系统内核)也可以任意地修改对象地内部状态(首地址都知道了,还有什么不能改的?),这显然是操作系统内核所不允许的;
II 操作系统有定期整理内存的责任,如果一些内存整理过一次后,对象被搬走了怎么办?
所以,Windows操作系统就采用进一步的间接:在进程的地址空间中设一张表,表里头专门保存一些编号和由这个编号对应一个地址,而由那个地址去引用实际的对象,这个编号跟那个地址在数值上没有任何规律性的联系,纯粹是个映射而已。
在Windows系统中,这个编号就叫做"句柄"。
在Windows应用程序中,窗口是通过句柄HWND来标识的,我们要对某个窗口进行操作,首先就要获取到这个窗口的句柄。
每个窗口在被创建出来之后就会被赋予一个句柄,该句柄指向一个数据结构体,结构体里明确表示着该窗口的各种信息,窗口大小,窗口名等,当我们得到这个句柄时就可以请求操作系统对它做一系列操作,列如:移动窗口,关闭窗口,最小化最大化等,这些都是通过窗口句柄来告诉操作系统的,我们要对哪个窗口进行操作,而消息则是告诉操作系统要做什么样的操作,消息的附加参数就是操作值,列如移动窗口,会有附加的xy坐标参数!
3 Windows对象和MFC对象的区别句柄有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 对话框控件消息响应函数中可以直接调用的函数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 窗口句柄和指针使用的场合先看下面的两行代码,简单了解一下窗口句柄和指针的一个使用场合:
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;
6 窗口ID和指针对MFC编程来说,对话框和控件都可以理解为一个子窗口,都有对应的一个ID和类。可以返回对应的对象指针。
7 获得窗口句柄的方法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();
8 获取窗口指针的办法① 获取当前应用进程的指针
AfxGetApp();
② 获得主框架窗口指针(任何时候都可以用,只要是MFC程序中)
CWnd* pWnd = AfxGetMainWnd();
③ 获得对话框中控件指针
CWnd* pWnd = GetDlgItem(IDC_xxx);
可以强制转换为具体类型,如CEdit *edit1 = (*CEdit)GetDlgItem(IDC_xxx);
④ 获得工具栏指针
CToolBar * pToolBar=(CtoolBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);
9 窗口、控件的指针和句柄的相互转化① 指针转化为句柄
在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();
10 窗口、控件的指针和句柄与其ID的相互转化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-
,