一、为什么我们需要句柄?
更准确地说,是windows需要句柄。windows需要向程序员提供必要地编程接口,在这些接口中,允许程序员访问、创建和销毁对象。但是,出于封装地考虑,windows并不想向程序员返回指针。指针包含了太多的信息。首先指针给出了对象存储的确切位置;其次,要操作一个指针,程序员必须知道指针所指对象的内部结构特征,也即,windows必须向程序员暴露相应的数据结构,而这些数据结构也许是操作系统想向程序员隐藏的。
如果说COM技术向用户隐藏了数据,只暴露了接口并只允许按接口定义的方法操作数据的话,句柄这种方式则允许你按自己的方式直接操作数据,但windows又不向你直接暴露数据。直接操作数据是程序员需要的,不暴露数据是windows所需要的,句柄封装方式实现了各取所需。
句柄和指针
句柄与指针确实是完全不同的两个概念。句柄仅仅是一个32位整数,WIN32中用于标记某个系统或进程的对象,可以理解为对象索引(由于MS未完全公开相关技术,在一定程度上只能如此理解),这个索引更像是一种映射关系(从句柄到对象指针的映射)。
句柄可以理解为用于指向或标识内存的一块“资源”,这些资源如:文件(file)、内存块(block of memory)、菜单(menu)等等。操作系统通过句柄来定位核心对象和系统资源。
指针即为指向内存的“数据或指令”某一单元。说的确切一点,句柄实际上是一种指向某种资源的指针,但与指针又有所不同:指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。
指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数据。Windows并不希望一般程序修改其内部数据结构,因为这样太不安全。所以Windows给每个使用GlobalAlloc等函数声明的内存区域指定一个句柄(本质上仍是一个指针,但不要直接操作它),平时你只是在调用API函数时利用这个句柄来说明要操作哪段内存。当你需要对某个内存进行直接操作时,可以使用GlobalLock锁住这段内存并获得指针来直接进行操作。
句柄是指针的“指针”,使用句柄主要是为了利于windows在进程内存地址空间移动分配的内存块,以防止进程的内存空间被撕的四分五裂而存在过多的碎片。
句柄是一些表的索引也就是指向指针的指针。间接的引用对象,windows可以修改对象的"物理"地址和描述器的值,但是句柄的值是不变的。
句柄可以在获得窗口的时候使用,指针可以进行调用窗口,两个使用的地方不一样.一个括号外,一个括号内.
从窗口指针获取窗口句柄:GetSafeHwnd();
从窗口句柄获取临时窗口指针:FromHandle();
从窗口句柄获取永久窗口指针: FromHandlePermanent();
其实两者被没有关系,实际上是MFC在创建窗口的时候用钩子函数沟住HCBT_CREATEWND消息,
然后通过CWnd::Attach()函数把二者捆绑在一起。
以后就可以用GetSafeHwnd(),FromHandle(),FromHandlePermanent()这三个函数可以互相得到了。
MFC之所以要这样做,主要是为了使原来的SDK面向过程的编程遍成面向对象的编程,所有的MFC的窗口都共用一窗口过程函数,在窗口过程函数里,通过窗口句柄(HWND)找到窗口对象指针(CWnd *)从而把消息分发到窗口对象中,这样以后就可以在窗口类中实行消息响应编程处理了。
二、CSDN上有人说过:牧童遥指杏花村。
牧童的手为指针,杏花村的牌子为句柄,杏花村酒店为对象的实例.
句柄是资源在创建过程中由Windows赋予的,它就是代表这个资源的。
而指针实质上是某个变量或者对象所在内存位置的首地址,是指向对象的。
一个是指向,一个是代表,二者是不同的。
一个是直接找到对象(指针),一个是间接找到对象(句柄)。
例如,杏花村可以搬家(实际上程序运行过程中,资源在内存中的地址是变化的),那么牧童的手的指向也就不同(指针)了,然而即使搬了家,“杏花村”这块牌匾是不变的,通过打听“杏花村”这个名称,还是可以间接找到它的(地址)。
另外一个例子:
HANDLE的本意是把柄,把手的意思,是你与操作系统打交道的东东。
举个通俗的例子,比如你考上了大学,入学后,学校(操作系统)会给你一个学生证号。注意,这个号码是学校指定的,你无法自选。有了这个号码(学生证,假设一证多用)享受学校提供的服务:如你就可以去图书馆借书,去食堂吃饭,去教室上课等等。但你不能到食堂里买啤酒,因为学校不允许这种服务。而在计算机中系统提供的服务就是API调用,你有了HANDLE,就可以理直气壮地向系统提出调用API的服务。
而指针的权力就大多了,有了指针你可以到处去喝酒,打架,学校(操作系统)管不着,所以句柄和指针的区别在于句柄只能调用系统提供的服务。
而句柄虽然是一个能相互区别的号码,但与我们普通的ID号又有区别,普通的ID号是可以由程序员自己定义的,而句柄不行,它是对象生成时系统指定的,是为了区别系统中存在的各个对象,这个句柄不是由程序员符给的.
附注:获得窗口句柄三种方法
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)
附:句柄种类
HACCEL Handle to an accelerator table.
HANDLE Handle to an object.
HBITMAP Handle to a bitmap.
HBRUSH Handle to a brush.
HCONV Handle to a dynamic data exchange (DDE) conversation.
HCONVLIST Handle to a DDE conversation list.
HCURSOR Handle to a cursor.
HDC Handle to a device context (DC).
HDDEDATA Handle to DDE data.
HDESK Handle to a desktop.
HDROP Handle to an internal drop structure.
HDWP Handle to a deferred window position structure.
HENHMETAFILE Handle to an enhanced metafile.
HFILE Handle to a file opened by OpenFile, not CreateFile.
HFONT Handle to a font.
HGDIOBJ Handle to a GDI object.
HGLOBAL Handle to a global memory block.
HHOOK Handle to a hook.
HICON Handle to an icon.
HIMAGELIST Handle to an image list.
HIMC Handle to input context.
HINSTANCE Handle to an instance.
HKEY Handle to a registry key.
HKL Input locale identifier.
HLOCAL Handle to a local memory block.
HMENU Handle to a menu.
HMETAFILE Handle to a metafile.
HMODULE Handle to a module.
HMONITOR Handle to a display monitor.
HPALETTE Handle to a palette.
HPEN Handle to a pen.
HRGN Handle to a region.
HRSRC Handle to a resource.
HSZ Handle to a DDE string.
HWINSTA Handle to a window station.
HWND Handle to a window