句柄是操作系统用来管理各种对象的一个“名字”。例如Windows中有各种各样的对象:Kernel对象:Thread,Process,Event,File等等;User对象Window,Menu,Icon,Cursor等;GDI对象Device context,Brush,Pen,Bitmap等等。各种不同的句柄意义和使用环境也不大相同,但它们有一个共同点就是,它们都必然以某种方式与对应的对象联系起来。举例来说,一个Window对象在USER中是用一个WND结构来表示的,该结构存储了USER用于管理一个窗口的信息,其中包括你在RegisterClass和CreateWindow中指定的部分参数的副本(当然还有其他许多信息,这其中很大一部分你都可以通过各种各样的关于窗口的API获得);而所谓的hwnd基本上可以理解为是WND结构体数组的索引值。又例如Kernel对象Process,在Kernel中是由一个ProcessDatabase的结构表示的,该结构不同于WND结构,它其中的大部分成员用常规的办法是无法得到的,而只是作为Kernel的内部管理之用——毕竟,它可是“Kernel”对象啊。类似地,hProcess也可以理解为是一个指向Process Handle Table(Kernel用于进程管理的另一个数据结构)的索引或指针(当然实际上并不是这么简单,只有微软知道它到底是怎么指过去的)。
另外,不同的handle一般有不同的作用域,例如USER对象往往是全局的,因此hwnd也是全局的——比如你完全可以在一个进程中取到另一个进程中窗口的句柄,并对其操作;而且,USER对象还是唯一的,例如一个窗口只可能有一个hwnd。而KERNEL对象一般只能在本进程或其子进程中使用,对于其他进程是没有意义的(注意,hProcess与Process ID是不同的,后者可以唯一地标识一个进程,并可在其他进程中使用);并且KERNEL对象可以被重用,例如同一个进程可以被另外两个进程同时打开,对应两个handle。GDI对象一般只能在本进程范围内使用。
由于以上的原因,假设你同时针对一个Kernel对象打开了两个句柄,那么该Kernel对象的引用计数将是2。如果你的程序忘了关闭其中一个handle,那么该对象的引用计数将维持1——本来此时引用计数应该为0,Kernel会释放该对象占用的资源,而此时却不能。这便是为什么总要关掉句柄的原因——虽然Kernel会在某个进程退出时,尽量关闭其所占用的资源(这就是前面说的Process Handle Table的作用),但这并不总是可靠的。所以程序在使用完资源(各种各样的句柄)以后,最好主动释放。

解决方案 »

  1.   

    不要想的那么复杂,其实就是一个指针嘛!
    typedef void * HANDLE
    typedef HANDEL HBrush
    ......
    void *型的指针。
      

  2.   

    不要想的那么复杂,其实就是一个指针嘛!
    typedef void * HANDLE
    typedef HANDEL HBrush
    ......
    void *型的指针。
      

  3.   

    不要想的那么复杂,其实就是一个指针嘛!
    typedef void * HANDLE
    typedef HANDEL HBrush
    ......
    void *型的指针。
      

  4.   

    上面的说的不正确
    把局柄理解为地址是一种非常错误的看法!在Jeffer Richter的 《Advance Windows》的
    第一章中有这种描述: 每一个进程都有一张Process Handle Table 里面纪录者进程打开
    的所有内核对像的信息包括内核对象的局柄和内核对象的指针,这个所谓的局柄就是这张
    Process Handle Table(其实就是一个纪录结构的数组)的索引。当你把局柄传给相应的
    API函数时。API通过你的Process Handle Table的索引找到相应的对象地址的指针然后进行
    操作的。
    单从概念上讲,句柄指一个对象的标识,而指针是一个对象的首地址。从实际处理的角度讲,即可以把句柄定义为指针,又可以把它定义为同类对象数组的索引,这两种处理方法都有优缺点,至于选用哪种方式,完全应该看实际需要,这可以说是一种程序设计上的技巧。那种单纯认为句柄是指针或索引的想法都是机械的、不确切的。    其实,在Windows中类似的处理是很多的、很灵活的。再具个相似的例子:    我们知道,在Windows中有个函数叫做CallWindowProc。故名思义,它的作用就是向指定的窗口过程传递一个消息。你也许会想,既然我已经有了窗口过程的指针,为什么我不可以直接通过这个指针调用该函数(这是C语言的内建功能)?事实上,在Win16中确实可以这么做,因为GetWindowLong返回的确实是该函数的指针。但在Win32下,GetWindowLong返回的并不是该函数的指针,而是一个包含函数指针的数据结构的指针(MSDN上说返回的是一个窗口函数地址或它的句柄,就是指的这种情况)。该数据结构是可变的,但只要你使用CallWindowProc来调用的话是不会出错的。这里我们又看到使用句柄处理带来的好处。(补充说明一点:微软在这里之所以这么处理,是为了解决16位/32位以及ANSI/UNICODE的转化问题)