代码改变世界

你了解句柄吗?

2009-06-23 19:08  htc开发  阅读(277)  评论(0编辑  收藏  举报

我觉得自己对于指针理解比较透彻了,但关于句柄的理解,就容易犯理解成指针的错误.
一般说:句柄是资源(或项目)的唯一标识.
俺觉得这其实说得很不明白的,现在我想问的是句柄到底是怎么标识资源的:
1,句柄在32位机上是32位整型吗?
2,当HANDLE handle;声明句柄时,handle是它的标识符吗?如int a;则a就是标识符.
3,既然句柄是一个整型值,那么,它在内存的什么位置呢?栈上吗?
4,句柄是通过在它所占内存中记录对象在内存中的首地址来"标识"对象的吗?如果是,那与指针在这方面就完全一样了.

可能有点玄,大家谈论下吧.

---------------------------------------------------------------

对句柄的认识 
之前在编写MFC程序的时候,有时会碰到句柄这一概念,由于对它一直不是很重视,所以并没有主动去了解它.随着不断深入学习MFC,特别是看完候先生的<<深入浅出MFC>>后,对句柄的实质产生了很大的兴趣,于是到处收集资料,现在总结一下.
       handle的中文意思主要有多种,一种是作为名词:柄,把柄;一种是作为动词:处理.硬要从中文意思来说的话,这两种中文意思都可以用来勉强解释句柄.例如一个锅的手柄,你只要抓住了它,你就可以很好地操作那个锅,不过很明显你只能通过锅的手柄来做一些诸如炒菜之类的事,你不可能是因为抓住了锅的手柄才能去看电视.不能的物体都会至少有一个属于它的"手柄".在MFC里面,有很多对象,例如对话框,窗口,画笔,显示器,打印机等,每个对象都会产生一个属于它自己的句柄.oh!慢着,似乎还没说清楚句柄的本质,下面再作详细的解说吧.

       如果查看各种HANDLE的定义,如HDC,HPEN,HINSTANCE等等,你会发现有这样一个声明:

       DECLARE_HANDLE(HDC);

       再把DECLARE_HANDLE这个宏展开来看一下:

       #define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *name

       这是什么意思?用HDC替换上面的name来看看,我们就得到了下面的一段代码:

      struct HDC__ 

       { 

       int unused; 

       }; 

       typedef struct HDC__ *HDC

        oh,原来句柄就是一个指向某一结构的指针,这个结构体只有一个成员,它是一个整数(不知为什么MS要把这个成员叫做unused,难道真的没有用吗?).

        嗯,我们再看一个HANDLE的定义,在winnt.h头文件中,我们看到了HANDLE的定义:

        typedef PVOID HANDEL;

        PVOID是什么?再看它的定义:

        typedef void *PVOID;

        真相出来了,原来HANDLE不过是一个指向void型,即无类型的指针,嗯,目前的指针是32位的吧.其实也不能说HANDLE是一种指针,它只充当一种索引的作用.真的很难解释HANDLE是什么,再往下看看,只能意会吧,呵呵.

        知道了HANDLE的本质(一个32位的整数,用16进制表示),再来说说HANDLE的作用.看我前面第二段的例子,你可以了解到为了控制一个物体,你需要找到它的"手柄".HANDLE就是充当win32下各种"物体"的"手柄".事实上,很多windows API都需要一个HANDLE作为输入参数,这是因为API需要和一些你看不见的对象打交道,这些对象是不能被你所移动或修改的,所以不能给你一个指针来对它进行操作.oh,指针的确是个好东西,它所拥有的能力实在太强大了,但所谓能力越大,责任越大,使用指针的时候千万要小心,不然会造成毁灭性的错误.所以,为了安全,还是给你一个HANDLE吧,通过这个HANDLE,你就可以对那些对象进行一些处理.HANDLE在这里充当什么角色呢?应该算是一个中间人吧,HANDLE是固定的,它不会变,但是对象的地址会变,当对象在内存中的位置发生改变后,我们不能通过之前的对象指针找到对象,怎么办呢?嗯,这时候HANDLE的用处就出来了,HANDLE就是用来记录对象的最新地址的.有点明白了吧?换一种说法吧,嗯,其实就是你知道HANDLE在哪里,但你不知道对象在哪里,而HANDLE知道对象在哪里,所以,只要你知道了HANDLE在哪里,你就能找到对象(尽管你还是不知那个对象住在内存的哪个地方,不过找到它就行了,不是吗?).嗯,是不是有点像索引的作用呢?为什么不给我们知道对象在内存中的位置呢?呵呵,操作系统怕你对它进行一些不利的操作啊.反正你通过HANDLE能让对象做它应该做的事,这就足够了.这就是封装吧,MS最喜欢做的事情,呵呵.

       其实对于HANDLE的定义,MS公司也不能说得清清楚楚,把HANDLE翻译成句柄也是不太合适的,但有时就是这样,英文能理解的东西,翻译到中文反而就混乱了,所以"句柄"这个名词比较适合初学者,如我.那些有丰富编程经验的人一定不喜欢使用"句柄"这个名词吧,还是用handle这个原版英文单词比较容易理解

---------------------------------------------------------------

怎么又有人问到这个问题了,看看下面的说法够不够清楚:

    1、句柄在C语言里(尤其是WIN32 API)常用在为上层提供多实例库函数服务的模块中,主要目的是对上层屏蔽实现细节。句柄对用户来说一般就是一个void *类型的指针(或一个WORD32),而在库函数实现里则把它转换为保存诸如实例状态等信息的数据结构指针。在这种库提供的服务中,一般都要求用户先调用一个open函数以创建一个实例,指向该实例的void *指针返回给用户,此后用户可调用该库中其他函数对该实例进行操作,这些函数中第一个参数为那个open返回的句柄,最后有一个close函数用于放弃对实例的使用,释放相关资源。
    2、对于提供服务的库模块来说,其句柄的内存分配要求要么是静态定义要么是动态分配的,总之必须是具有全局生存期的。之所以这样要求是因为不止一个函数要使用它,如果分配在栈里,也即将指向的实例定义为局部变量,则后续函数将访问以前栈中数据,会造成未定义的结果。
    3、对于用户来说,句柄的存储分配没有上述要求。譬如,如果你只会在一个函数中调用该库中的函数,那个返回的句柄完全可以定义为局部变量。