有关句柄的概念

尽管你会想要深入钻研源代码,但你其实并不会这样做。Windows编程起初都是非常困难的。让我们来快速的看一看Windows下的程序是这样运作的。你编程的关键将是处理和发送消息。那什么是消息呢?简单的说,消息是一个32位值,用来指明一个事件。比如:当你移动鼠标的时候,一个消息(定义为WM_MOUSEMOVE)就被发送给当前的窗口。当你按下键盘上的键,一个消息(定义为WM_KEYDOWN)就被发送给了当前的窗口。当你调整窗口的尺寸,一个消息(定义为WM_SIZE)被发送到当前的窗口。想象出这个过程了么?那么这些消息被发送到哪里去了呢?它们会排进一个队里,窗口最终会将它们拿出来并相应的执行。例如当窗口得到WM_MOVE消息,它将改变窗口坐标并在屏幕上重画窗口。    
        让我们来看看句柄。Windows在面向对象方面做的很好。你面对着一些window对象(比如桌面,你现在用来阅读的程序,等等...)。一个程序员用非面向对象的方式怎么区分这类东呢?    
        句柄用来引用不同的windows对象。你可以对窗口、文件使用句柄,也能对分配了的内存、图象使用句柄。可以把句柄理解为与指针类似。你必须通过某种方式来创建句柄;而且在使用完后销毁掉,不然将造成资源泄漏而使你的系统瘫痪。所以要保证它们在某个时候被销毁了。    
        好了,现在是时候让我们把这两件东西放在一起了。    
        比方说有一个窗口,你有一个指向它的句柄(称为HWND)。把你的句柄命名为your_HWND。当这个遮盖在这个窗口上面的其他窗口被移去的时候,系统会要求你重画窗口。windows会这样发送消息:    
  PostMessage(your_HWND,   WM_PAINT,   0,0);    
        这个函数发送一个重画的消息给句柄为your_HWND的窗口。最后的两个参数作为该消息的额外信息,现在先不必考虑。这样,你的程序就应该有一个包括很多分支判断的函数来处理不同的消息。比如:     
void HandleTheMessage(long   Message)     
{    
    switch(Message)    
    {    
        case   WM_PAINT    
            DrawWindow();    
            break;    
        case   WM_KEYDOWN    
            break;    
            etc...    
    }

}    
  sebsdin   (孤灯下)   from   BBS   水木清华站:    
  句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。句柄地址(稳定)→记载着对象在内存中的地址→对象在内存中的地址(不稳定)→实际对象。但是,必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄,而且绝大多数情况的确不一样的。假如我们把进入电影院看电影看成   是一个应用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电影院售给我们的门票总是不同的一个座位是一样的道理。        
  feeboby   from   csdn:    
  受M$的帮助文档以及很多Windows编程书籍的影响,大家对局柄比较普遍的认识是:句柄    
  是一个整数,用以标识Windows对象,句柄不是一个指针……而实际上,这些不过是M$进行数据封装的幌子而已,下面我们一起来分析一下HANDLE到底是什么。    
  请先到Windef.h找绝大多数句柄的定义:    
  DECLARE_HANDLE(HWND);    
  DECLARE_HANDLE(HHOOK);    
  ……    
  DECLARE_HANDLE(HGDIOBJ);    
  DECLARE_HANDLE(HBITMAP);    
  DECLARE_HANDLE(HBRUSH);    
  ……    
  typedef   HANDLE                             HGLOBAL;    
  typedef   HANDLE                             HLOCAL;    
  ……    
  OK,   现在大家跟我一起翻到Winnt.h,看看DECLARE_HANDLE和HANDLE到底是什么:    
  #ifdef   STRICT    
  typedef   void   *HANDLE;    
  #define   DECLARE_HANDLE(name)   struct   name##__   {   int   unused;   };   typedef   struct    
  name##__   *name    
  #else    
  typedef   PVOID   HANDLE;    
  #define   DECLARE_HANDLE(name)   typedef   HANDLE   name    
  #endif    
  typedef   HANDLE   *PHANDLE;    
  哈哈,现在知道了吧,HANDLE就是PVOID,也就是无类型指针,    
  而DECLARE_HANDLE(HWND);就是:    
  struct   HWND__   {    
        int   unused;};    
  typedef   struct   HWND__   *HWND;    
  现在实际上都清楚啦,这些Handles都不过是指向struct的指针,至于这个struct的用处    
  ,连M$都说unused了,^o^    
  现在解释下M$这么做的意义,这就是所谓数据封装,你可以在你的程序中把M$的内部结    
  构指针传来传去,可是你却不知道它到底指向的内容是什么,而且可以编个句柄的瞎话    
  防止大家的质疑:)。而M$的程序大可以这么写:    
  #include   <windows.h>     //这个和大家用的一样    
  #include   "windows_in.h"     //这个是M$自用的,外人别想看到^o^    
  HSOMETHINGELSE   DoSomething(HSOMETHING   hSomething)   {    
        struct   RealSomething*   p   =   (struct   RealSomething*)hSomething;   //先强制类型    
  转换成内部结构指针    
        ……do   something……    
        return   (HSOMETHINGELSE)pRealSomethingElse;//强制类型逆转换    
  }    
  classfactory   from   vcroad:    
  单从概念上讲,句柄指一个对象的标识,而指针是一个对象的首地址。从实际处理的角度讲,即可以把句柄定义为指针,又可以把它定义为同类对象数组的索引,这两种处理方法都有优缺点,至于选用哪种方式,完全应该看实际需要,这可以说是一种程序设计上的技巧。那种单纯认为句柄是指针或索引的想法都是机械的、不确切的。    
  其实,在Windows中类似的处理是很多的、很灵活的。再具个相似的例子:    
  我们知道,在Windows中有个函数叫做CallWindowProc。故名思义,它的作用就是向指定的窗口过程传递一个消息。你也许会想,既然我已经有了窗口过程的指针,为什么我不可以直接通过这个指针调用该函数(这是C语言的内建功能)?事实上,在Win16中确实可以这么做,因为GetWindowLong返回的确实是该函数的指针。但在Win32下,GetWindowLong返回的并不是该函数的指针,而是一个包含函数指针的数据结构的指针(MSDN上说返回的是一个窗口函数地址或它的句柄,就是指的这种情况)。该数据结构是可变的,但只要你使用CallWindowProc来调用的话是不会出错的。这里我们又看到使用句柄处理带来的好处。(补充说明一点:微软在这里之所以这么处理,是为了解决16位/32位以及ANSI/UNICODE的转化问题)    
  另有一段文字,作者不详:    
  句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样,每个人都会有一个,不同的人的姓名不一样,但是,也可能有一个名字和你一样的人。从数据类型上来看它只是一个16位的无符号整数。应用程序几乎总是通过调用一个WINDOWS函数来获得一个句柄,之后其他的WINDOWS函数就可以使用该句柄,以引用相应的对象。在WINDOWS编程中会用到大量的句柄,比如:HINSTANCE(实例句柄),HBITMAP(位图句柄),HDC(设备描述表句柄),HICON(图标句柄)等等,这当中还有一个通用的句柄,就是HANDLE,比如下面的语句:    
  HINSTANCE   hInstance;    
  可以改成:    
  HANDLE   hInstance;    
  上面的2句语句都是对的。    
  一个WINDOWS应用程序可以用不同的方法获得一个特定项的句柄。许多API函数,诸如CreateWindow,GlobalAlloc,OpenFile的返回值都是一个句柄值。另外,WINDOWS也能通过应用程序的引出函数将一个句柄作为参数传送给应用程序,应用程序一旦获得了一个确定项的句柄,便可在WINDOWS环境下的任何地方对这个句柄进行操作。其实句柄的大量使用已经影响到了每一个WINDOWS的程序设计。    
  句柄只有当唯一的确定了一个项目的时候,它才开始有意义。句柄对应着项目表中的一项,而只有WINDOWS本身才能直接存取这个表,应用程序只能通过API函数来处理不同的句柄,举个例子来说吧!比如:我们可以为我们的应用程序申请一块内存块,通过调用API函数GlobalAlloc,来返回一个句柄值:    
  hMem=GlobalAlloc(......);    
  其实现在hMem的值只是一个索引值,不是物理地址,应用程序还不能直接存取这块内存。这儿还有一个话外题,就是,一般情况下我们在编程的时候,给应用程序分配的内存都是可以移动的或者是可以丢弃的,这样能使有限的内存资源充分利用,所以,在某一个时候我们分配的那块内存的地址是不确定的,因为他是可以移动的,所以得先锁定那块内存块,这儿应用程序需要调用API函数GlobalLock函数来锁定句柄。如下:    
  lpMem=GlobalLock(hMem);    
  这样应用程序才能存取这块内存。  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值