句柄Handle的含义及使用

2023-05-16

句柄Handle的含义及使用

1.句柄 :

头文件:winnt.h(也可以使用windows.h头文件,如果winnt.h和windows.h一起包含时,如果先后顺序不当,会造成错误,windows.h需要在winnt.h之前include)

 注:在动态链接库DLL程序中,不用添加头文件就可以使用HANDLE

无效的返回值为: INVALID_HANDLE_VALUE

句柄是windows编程的一个关键性的概念,编写windows应用程序总是要和各种句柄打交首,所谓句柄,就是一个4字节长的唯一的数,用以标识许多不同的对象类型,由于windwos是一个多任务操作系统,它可以同时运行多个程序或一个程序的多个副本,这些运行的程序称为一个实例,为了对同一程序的多个副本进行管理,windwos引入了实例句柄,windows为每个应用程序建立一张表,实例句柄就好像是这张表的一个索引。windows不仅使用句柄来管理实例,也用它来管理窗口,位图,字体,元文件,图标等系统资源。在windows环境中,句柄是用来标识项目的,这些项目包括:模块,任务,实例,文件,内存块,菜单,控制,字体,资源,包括图标,光标,字符串等,GDI对象,所括位图,画刷,元文件,调色板,画笔,区域,以及设备描述表句柄是用来标识(被应用程序所建立或使用的)对象的唯一整数,windows使用各种各样的句柄标识诸如应用程序实例,窗口,控制,位图,GDI对象等等

例子:

#include<iostream>
#include<windows.h>
using namespace std;
int main(void)
{
    char ch[20] = "Test.dsw";
    HANDLE handle;
    LPWIN32_FIND_DATA fd;
    handle = FindFirstFile(ch,fd);
    if(handle != INVALID_HANDLE_VALUE)
        cout<<"ok"<<endl;
    else
        cout<<"no"<<endl;
    return 0;
}

MFC源代码:

#ifdef STRICT
typedef void *HANDLE;(类型定义 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

DECLARE_HANDLE(HMODULE);
DECLARE_HANDLE(HINSTANCE);
DECLARE_HANDLE(HLOCAL);
DECLARE_HANDLE(HGLOBAL);
DECLARE_HANDLE(HDC);
DECLARE_HANDLE(HRGN);
DECLARE_HANDLE(HWND);
DECLARE_HANDLE(HMENU);
DECLARE_HANDLE(HACCEL);
DECLARE_HANDLE(HTASK);

HANDLE就是PVOID,也就是无类型指针(不指向任何类型数据的指针)句柄表面上是指针,但是他和指针最大的不同是,给你一个指针,你可以通过这个指针做任何事情,也许是做好事,也许是通过这个指针破坏内存,句柄就没有这个缺点,通过句柄,你能干一些windows让你干的事情,没有了指针的坏处,在windows程序设计中,句柄是无法精确定义的术语,但是在程序设计中,句柄无所不在,窗口有窗口的句柄HWND,线程和进程也有句柄HANDLE,甚至有人把套接字也称为句柄,简而言之,句柄是处理对象的一个接口,你可以通过句柄去操作程序中所涉及的对象。在windows中,句柄是和对象一一对应的32位无符号整数值,对象可以映射到唯一的句柄,句柄也可以映射到唯一的对象windows需要向程序员提供必要地编程接口,在这些接口中,允许程序员访问,创建和销毁对象,但是,出于封装的考虑,windows并不想向程序员返回指针,指针包含了太多的信息

2.使用HANDLE的好处:

(1).首先指针给出了对象存储的确切位置

(2).其次,要操作一个指针,程序员必须知道指针所指对象的内部结构特片,也就是说windows必须向程序员暴露相应的数据结构,而这些数据结构也许是操作系统想向程序员隐藏的。如果说COM技术向用户隐藏了数据,只暴露了接口并只允许按接口定义的方法操 作数据的话,句柄这种方式则允许你按自己的方式直接操作数据,但windows又不向 你直接暴露数据。直接操作数据是程序员需要的,不暴露数据是windows所需要的, 句柄封装方式实现了各取所需。(很好的找到了满足双方需且的平衡点)

3.句柄与对象的映射:

封装背后,必须有一个地方可以实现解码,以实现句柄和对象的相互转换。在 windows中,存在两种映射方式
(1).全等映射。也即,句柄本身就是一个指针。映射在这里只是类型转换而已。 这种情况有,进程实例句柄或模块句柄,以及资源句柄等等。(不明白) 
(2). 基于表格的映射。这是对象指针与句柄之间最普通的映射机制。操作系统创 建表格,并保存所有要考虑的对象。需要创建新对象时,要先在表格中找到空入口 ,然后把表示对象的数据添入其中。当对象被删除时,它的数据成员和其在表中的 入口被释放。

4.句柄的定义和实现 :

我们以GDI对象为例进行讨论。创建了GDI对象,就会得到该对象的句柄。句柄 的对象可能是HBRUSH、HPEN、HFONT或HDC中的一种,这依赖于你创建 的GDI对象类 型。但是最普通的GDI对象类型是HGDIOBJ。HGDIOBJ被定义成空指针。 HPEN的实际编译类型定义随编译时间宏STRICT的不同而不同。如果STRCIT已经 被定义了,HPEN是这样的

struct HPEN__ {int unused};
typedef struct HPEN__* HPEN

如果STRICT没有定义,HPEN是这样定义的:

typedef void *HANDLE;
typedef HANDLE HPEN;

上面这段代码是一个注重细节的程序员最接近句柄的地方,因此我们重点分析 一下。这里有一点点技巧。如果定义了STRICT宏,HPEN是指向有单个未使用字段的 结构的指针,否则HPEN是空指针。C/C++编译器允许把任何类型的指针作为空指什传 
递,反之则不可以。两个不同类型的非空指针是互不兼容的。在STRICT版本中,编译对GDI对象句柄的不正确混用将给出警告,对于非GDI句柄,如HWND、HMENU的不正 确混用也会给出警告,从而使程序在编译器得到更STRICT的检查。接下来的分析可能不那么令你感兴趣,但它更深刻地揭示了句柄。对GDI句柄来 说,尽管windows头文件把它定义成指针,但如果你仔细检查这些句柄的值,它根本就不像指针,这也是为什么我说它只是一个32位无符整数值的原因。对句柄就是指针的情况,这句话也仍然适用。让我们随意地生成一些句柄,比如你用GetStockObject()以得到一些句柄,你会发现,它们的值总在区间0x01900011到0xba040389。 前者指向用户区中的未分配的无效区域,后者指向内核地址空间。另外你可能发现,两个句柄之间的值可能只差数值1,这也说明GDI句柄不是指针。和多数人想象的不一样,句柄也不是一个单纯的索引值。对GDI对象句柄来说,GDI句柄由8位 、1位堆对象标记(表明对象是否创建在堆中)、7位对象类型信息和高4位为0的16位索引组成,如图:

3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1|1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 
1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 
| 8 位引用计数 |堆 | 对象类型7 | 16位索引 | 

注:在这里你可以看到,对GDI来说,它只使用了16位作为索引。这意味着一个进程最多只 可以创建小于64K个句柄,实际上受其他一些限制,整个windwos系统中大概可以容纳约16384(0x4000)个GDI对象。

进一步解释:

所谓句柄实际上是一个数据,是一个Long (整长型)的数据。

    句柄是WONDOWS用来标识被应用程序所建立或使用的对象的唯一整数,WINDOWS使用各种各样的句柄标识诸如应用程序实例,窗口,控制,位图,GDI对象等等。WINDOWS句柄有点象C语言中的文件句柄。 

    从上面的定义中的我们可以看到,句柄是一个标识符,是拿来标识对象或者项目的,它就象我们的姓名一样,每个人都会有一个,不同的人的姓名不一样,但是,也可能有一个名字和你一样的人。从数据类型上来看它只是一个16位的无符号整数。应用程序几乎总是通过调用一个WINDOWS函数来获得一个句柄,之后其他的WINDOWS函数就可以使用该句柄,以引用相应的对象。

    如果想更透彻一点地认识句柄,我可以告诉大家,句柄是一种指向指针的指针。我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是住留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如果您真的这样认为,那么您就大错特错了。我们知道,Windows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,依此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果地址总是如此变化,我们该到哪里去找该对象呢?

    为了解决这个问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应用对象在内存中的地址变化,而这个地址(存储单元的位置)本身是不变的。Windows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载(Load)时由系统分配给的,当系统卸载时(Unload)又释放给系统。

    句柄地址(稳定)→记载着对象在内存中的地址————→对象在内存中的地址(不稳定)→实际对象
 
    本质:WINDOWS程序中并不是用物理地址来标识一个内存块,文件,任务或动态装入模块的,相反的,WINDOWS API给这些项目分配确定的句柄,并将句柄返回给应用程序,然后通过句柄来进行操作。 

但是必须注意的是程序每次从新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄,而且绝大多数情况的确不一样的。假如我们把进入电影院看电影看成是一个应用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电影院售给我们的门票总是不同的一个座位是一样的道理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

句柄Handle的含义及使用 的相关文章

随机推荐

  • 头文件与类的声明

    我们在开始学习C 43 43 时 xff0c 就应该养成规范大气的编程习惯 xff0c 头文件 header 的布局就是其中很重要的一个点 我们需要知道头文件中的防卫式声明 ifndef COMPLEX define COMPLEX 前置声
  • ubuntu C++ 和windows C# socket TCP通信

    TCP客户端代码 windows C https www cnblogs com sunev archive 2012 08 05 2604189 html using System using System Net using Syste
  • 多进程和多线程的优缺点

    在Linux下编程多用多进程编程少用多线程编程 IBM有个家伙做了个测试 xff0c 发现切换线程context的时候 xff0c windows比linux快一倍多 进出最快的锁 xff08 windows2k的 critical sec
  • 【C语言】链表及单链表基本操作

    1 什么是链表 xff1f 链表的分类 xff1f 链表是一种物理存储结构上非连续 非顺序的存储结构 xff0c 数据元素的逻辑顺序是通过链表中的指针链接次序实现的 数据结构中 xff1a 2 链表的分类 共有8种链表结构 3 单链表的基本
  • 14串聚合物锂电池保护板和电路图(带均衡功能)

    转载自 xff1a http bbs mydigit cn read php tid 61 746827 之前发过14串三元锂组装的帖子 xff0c 有坛友对保护板感兴趣 xff0c 还有的说串联充电 xff0c 早死早超生 xff0c 哈
  • HTTP的长连接和短连接

    一 什么是长连接 HTTP1 1规定了默认保持长连接 xff08 HTTP persistent connection xff0c 也有翻译为持久连接 xff09 xff0c 数据传输完成了保持TCP连接不断开 xff08 不发RST包 不
  • libcurl库的异步用法

    multi接口的使用会比easy 接口稍微复杂点 xff0c 毕竟multi接口是依赖easy接口的 xff0c 首先粗略的讲下其使用流程 xff1a curl multi init初始化一个multi curl对象 xff0c 为了同时进
  • 在ubantu16.04 配置ROS开发realsense435功能包

    本文主要对intel的一款实感相机realsense435 进行配置 xff0c 将ROS对realsense435支持的功能包进行安装 一 安装SDK 1 简要介绍 鉴于自己安装过程中出现的问题 xff0c 现在在安装之前先确认几件事情
  • 提高C++程序运行效率,减少运行时间的方法

    大致方法 xff1a 1 优化业务逻辑 xff0c 尽量少做事情 2 减少网络访问 xff0c IO等对外操作 3 如果有数据库 xff0c 优化SQL和数据库结构 4 优化算法 xff0c 比如冒泡排序改成快排等 5 优化代码的编写 这个
  • 常量引用、非常量引用、临时对象

    转载自 xff1a https www cnblogs com littleant archive 2012 08 01 2618846 html https www cnblogs com BensonLaur p 5234555 htm
  • 字符串string中“\0“与‘\0‘的打印、拼接问题

    1 34 0 34 为字符串长度为0的字符串指针 xff0c 它与 34 34 等价 2 打印 34 0 34 与 39 0 39 相同 xff0c cout输出时都会显示为空 39 0 39 在string的初始化和拼接中size大小的不
  • Direct3D的初始化

    1 获取接口IDirect3D9 的指针 xff0c 该接口用于获取系统中物理硬件设备的信息并创建接口IDirect3DDevice9 xff0c 该接口是一个C 43 43 对象 xff0c 代表了我们用来显示3D图形的物理硬件设备 使用
  • 绘制流水线

    顶点结构与顶点格式 在Direct3D中 xff0c 顶点除了包含空间信息外 xff0c 还可以包含其他的附加属性 xff0c 例如顶点可以有颜色属性 xff0c 也可以有法线属性 xff0c Direct3D赋予了我们自定义顶点格式的自由
  • Direct3D中的绘制

    顶点缓存与索引缓存 一个顶点缓存是一个包含顶点数据的连续内存空间 xff0c 一个索引缓存是一个包含索引数据的连续内存空间 xff0c 之所以使用顶点缓存和索引缓存而非数组来存储数据 xff0c 是因为顶点缓存和索引缓存可以被放置在显存 中
  • 结构体内的指针

    利用 结构体对象 对 结构体内的指针 赋值 include 34 stdafx h 34 include lt string h gt include lt stdlib h gt struct Student char name 从节省空
  • 自制廉价难度低性能较好的锂电充电器(转)

    转载自 xff1a http www geek workshop com thread 53 1 1 html 自制廉价难度低性能较好的锂电充电器 附电压平衡器第一张 电路全图 这张有点模糊 别急 先看大致位置 等一下再分区介绍 这一部份
  • 子类调用父类中的友元函数

    由于友元函数并非类成员 xff0c 因此不能被继承 xff0c 在某种需求下 xff0c 可能希望子类的友元函数能够使用父类中的友元函数 为此可以通过强制类型转换 xff0c 将子类的指针或是引用强转为父类的引用或是指针 xff0c 然后使
  • 判断一个字符是否是十六进制

    判断一个字符是否是十六进制 十六进制 xff08 hexadecimal xff09 是计算机中数据的一种表示方法 xff0c 意思是逢十六进一 十六进制数以16为基数 xff0c 采用的数码是0 1 2 3 4 5 6 7 8 9 A B
  • 库文件、静态库(lib)与动态库(dll)的生成与使用

    静态库 程序编译一般需经预处理 编译 汇编和链接几个步骤 在应用中 xff0c 有一些公共代码是需要反复使用 xff0c 就把这些代码编译为 库 文件 xff1b 在链接步骤中 xff0c 连接器将从库文件取得所需的代码 xff0c 复制到
  • 句柄Handle的含义及使用

    句柄Handle的含义及使用 1 句柄 xff1a 头文件 xff1a winnt h 也可以使用windows h头文件 xff0c 如果winnt h和windows h一起包含时 xff0c 如果先后顺序不当 xff0c 会造成错误