版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、内存句柄与指针的区别句柄其实就是指针,但是他和指针最大的不同是 : 给你一个指针,你可以通 过这个指针做任何事情, 也许是好事, 也许是通过这个指针破坏内存, 干一些捣 乱的事情。这个我想大家都会碰到过,因为乱用指针导致程序崩溃句柄就没有这个缺点, 通过句柄, 你只能干一些 windows 让你干的事情 (调 用一些 api 函数等等),没有了指针的坏处。句柄是一些表的索引也就是指向指针的指针 , 句柄和指针都是地址,句柄是 Windows编程的一个关键性的概念,编写Windows应用程序总是要和各种句柄打 交道。所谓句柄,就是一个唯一的数,用以标识许多不同的对象类型,如窗口、菜 单、内存、画
2、笔、画刷等。在Win32里,句柄是指向一个“无类型对象” (void*) 的指针,也就是一个 4 字节长的数据。无论它的本质是什么, 句柄并不是一个真正意义上的指针。 从构造上看, 句 柄是一个指针, 尽管它没有指向用于存储某个对象的内存位置。 事实上, 句柄指 向一个包含了对该对象进行的引用的位置。句柄的声明是这样的:typedef void *HANDLE由于Windows是一个多任务操作系统,它可以同时运行多个程序或一个程序 的多个副本。 这些运行的程序称为一个实例。 为了对同一程序的多个副本进行管 理,Windows引入了实例句柄。Windows为每个应用程序建立一张表,实例句柄 就好
3、象是这张表的一个索引。不同在于:1 、句柄所指的可以是一个很复杂的结构, 并且很有可以是与系统有关的, 比如说上面所说的线程的句柄, 它指向的就是一个类或者结构, 他和系统有很密 切的关 系,当一个线程由于不可预料的原因,而终止时在系统就可以回它所占用的资料,如CPU内存等等,反过来想可以知道,这个句柄中的某一些项,是 与系统进行 交互的。由于 Windows系统,是一个多任务的系统,它随时都可能 要分配内存,回收内存,重组内存。2 、指针它也可以指向一个复杂的结构,但是通常是用户定义的,所以的必需的工作都要用户完成,特别是在删除的时候。但在 VC 6.0 中也有一些指针,它们都是处理一些小问
4、题才用的,如最常见的字符的指针 , 它也是要用户 处理的如果你动态分配了内存;但是 Cstring 就不要用户处理了,它其实是 VC 中的一个类,所以的操作都由成员函数完成,产生(分配)由构造函数,删 除(回收)由析构函数完成。附注:获得窗口句柄三种方法1. HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)HWND FindWindowEx(HWND hwndParent, HWND hwndChildAfter,LPCTSTR lpClassName, LPCTSTR lpWindowName)2. HWND WindowF
5、romPoint(POINT& Point)/ 获得当前鼠标光标位置的窗口 HWND3. 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 l
6、Param)指针 句柄之间的转换a. 由指针获得句柄CWnd * pWnd;CWnd HWnd;HWnd = pWnd->GetSafeHWnd();b. 由句柄得到指针 :CWnd* pWnd=FromeHandle(hMyHandle); pWnd->SetWindowText("Hello World!");or CWnd* pWnd; pWnd->Attach(hMyHandle);MFC类中有的还提供了标准方法,比如Window句柄:static CWnd* PASCAL FromHandle( HWND hWnd );HWND GetSafeH
7、wnd( ) const;对于位图:static CBitmap* PASCAL FromHandle( HBITMAP hBitmap );static CGdiObject* PASCAL FromHandle( HGDIOBJ hObject ); HGDIOBJ GetSafeHandle( ) const;有人说句并就是一个标示, 一个 ID 号,是错误的。 一个 ID 号可以包括多个 资源,比如说单文档中的IDR_MAINFRAMEH般是指在硬盘上的资源。但 是当 把硬盘上的资源调入内存以后, 将有一个句柄指向它, 但是句柄只能指向一个资 源。而且句柄知道所指的内存有多大。还有指针
8、,指针指向地址,它不知道分配 的内存有多大。但是如果你定义一个句柄,然后在VC里面右击鼠标,选择"go to definition of HANDLE你会发现它的本质就是一个指针,但是它的作用不同于指针。句柄是个指针,指向一块内存, 但至于这块内存跟句柄所标识的对象是怎么 联系起来的,调用者不需要清楚, 调用者只需要知道, 这个句柄联系着一个 win32 对象。句柄是物理地址,可以跨进程传递,例如,HANDLE h进程A的一个窗口,你可以在进程B中利用一个跟ha相等的值(相等就是说它们强制转成int32的 值相等)初始化一个句柄,利用这个句柄你可以对进程 A 的那个对象进行操作, 例
9、如 MoveWindow,ShowWindo等。句柄包含了一些引用计数之类的东西, 所以我的上一点说的给句柄赋值是不 安全的,Windows API提供了一些函数,可以对句柄进行操作。句柄就是受限的指针。 它是由操作系统管理的,你不能通过它存取操作系统创建的数据结构。操作系统在创建一个对象(如 GDI, FILE )等的时候,它会为这个对象CONTEXT 保留一块数据结构,然后把它放在一张全局表中。句柄就是 这块数据结构在表中的索引指针对应着一个数据在内存中的地址,得到了指针就可以自由地修改该数 据。Windows并不希望一般程序修改其内部数据结构,因为这样太不安全。所以 Windows给每个
10、使用GlobalAlloc等函数声明的内存区域指定一个句柄(本质上 仍是一个指针, 但不要直接操作它 ) ,平时你只是在调用 API 函 数时利用这个句 柄来说明要操作哪段内存。当你需要对某个内存进行直接操作时,可以使用 GlobalLock 锁住这段内存并获得指针来直接进行操作。lshgao 的意见:句柄是指针的“指针”,使用句柄主要是为了利于win dows在进程内存地址 空间移动分配的内存块, 以防止进程的内存空间被撕的四分五裂而存在过多的碎 片。阿城的意见:句柄是一些表的索引也就是指向指针的指针。间接的引用对象,win dows可以修改对象的 " 物理" 地址和描述
11、器的值,但是句柄的值是不变的。刘志用的意见:句柄和指针都是地址,不同在于:1 ,句柄所指的可以是一个很复杂的结构,并且很有可以是与系统有关的, 比如说上面所说的线程的句柄, 它指向的就是一个很类或者结构, 他和系统有很 密切的关 系,当一个线程由于不可预料的原因,而终止时在系统就可以回它所 占用的资料,女口 CPU内存等等,反过来想可以知道,这个句柄中的某一些项, 是与系统进行 交互的。由于 Windows系统,是一个多任务的系统,它随时都可 能要分配内存,回收内存,重组内存。2 ,指针它也可以指向一个复杂的结构,但是通常是用户定义的,所以的必需的工作都要用户完成,特别是在删除的时候。但在 V
12、C 6.0 中也有一些指针, 它们都是处理一些小问题才用的, 如最常 见的字符的指针 , 它也是要用户处理的如果你动态分配了内存; 但是 Cstring 就 不要用户处理了,它其实是 VC+ +中的一个类,所以的操作都由成员函数完成, 产生(分配)由构造函数,删除(回收)由析构函数完成。zjf 问:你好,我在学习用 vc+6.0 编译多线程程序中遇到了很多句柄, 但是不明白 他的具体作用以及如何使用句柄,希望您能给我举几个具体实例,不甚感激!比如说:HANDLE hThread它是怎样具体使用的?答:你使用CreateThead后函数会返回一个句柄,它代表这个线程。你可能 会调用SetThre
13、adPriority 去修改线程的优先级,使用ResumeThread去重新开 始一个线程的运行,在调用这些函数时你都需要告诉系统你到底要操作哪个线 程,而刚才返回的句柄派上用处了,这些函数的第 一个参数就是线程的句柄。看VC中总是出现这个句柄的概念,以前一直以为就是指指针,但是越看越觉得不是这么简单, 于是本着有问题百度一下的原则, 看到如下解释, 很是经典:csdn 上有人说过:牧童遥指杏花村牧童的手为指针,杏花村的牌子为句柄,杏花村酒店为对象的实例句柄就是烤叉,用烤炉烤过鸭,鸡,牛,羊,狗么? 炉子里的东西是看不见, 摸不到的, 但你能用叉子去控制, 至于叉子上的是 什么,你放进去前应该
14、记住。呵呵句柄有时是指针, 有时是索引, 但他绝对是一把钥匙, 内核句柄 110的钥匙,GDI句柄是您的钥匙,只对您有效。单从概念上讲, 句柄指一个对象的标识, 而指针是一个对象的首地址。 从实 际处理的角度讲, 即可以把句柄定义为指针, 又可以把它定义为同类对象数组的 索引,这 两种处理方法都有优缺点, 至于选用哪种方式, 完全应该看实际需要, 这可以说是一种程序设计上的技巧。 那种单纯认为句柄是指针或索引的想法都是 机械的、不确 切的。其实,在 Windows中类似的处理是很多的、很灵活的。再 具个相似的例子:我们知道,在Windows中有个函数叫做CallWindowProc。故名思义,
15、 它的作用就是向指定的窗口过程传递一个消息。 你也许会想, 既 然我已经有了窗口过程的指针,为什么我不可以直接通过这个指针调用该函数(这是C语 言的内建功能)?事实上,在 Win 16中确实可以这么做,因为 GetWindowLong返回的确实是该函数的指针。但在 Win32下,GetWindowLong返 回的并不是该函数的指针,而是一个包含函数指针的数据结构的指针(MSD上说返回的是一个窗口函数地址或它的句柄,就 是指的这种情况)。该数据结构 是可变的,但只要你使用 CallWindowProc 来调用的话是不会出错的。这里我们又看到使用句柄处理带来的好处。(补 充说明一点:微软在这里之所
16、以这么处理,是为了解决16位132位以及ANSI/UNICOD的转化问题)看来,句柄很多时候是一个用于描述和标记一个资源的数据结构的指针, 而 不是资源本身的指针, 句柄中可能包含资源的指针, 但是根多时候不仅仅是这样。句柄 vs 指针句柄是一种指向指针的指针。 我们知道, 所谓指针是一种内存地址。 应用程 序启动后, 组成这个程序的各对象是住留在内存的。 如果简单地理解, 似乎我们 只要获知 这个内存的首地址,那么就可以随时用这个地址访问对象。但是,如 果您真的这样认为,那么您就大错特错了。我们知道,Win dows是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器
17、经常在内存中 来回移动对象, 依此来满足各种应用程序的内存需要。 对象被移动意味着它的地 址变化 了。如果地址总是如此变化, 我们该到哪里去找该对象呢 ?为了解决这个 问题,Windows操作系统为各应用程序腾出一些内存储地址,用来专门登记各应 用对 象在内存中的地址变化,而这个地址 (存储单元的位置 )本身是不变的。 Win dows内存管理器在移动对象在内存中的位置后,把对象新的地址告知这个句柄地 址来保存。这样我们只需记住这个句柄地址就可以间接地知道对象具体在 内存中的哪个位置。 这个地址是在对象装载 (Load) 时由系统分配给的, 当系统卸 载时(Unioad)又释放给系统。句柄地址
18、(稳定)-记载着对象在内存中的地址- 对象在内存中的地址(不稳定)-实际对象。但是,必须注意的是程序每次从新 启 动,系统不能保证分配给这个程序的句柄还是原来的那个句柄, 而且绝大多数情 况的确不一样的。假如我们把进入电影院看电影看成是一个应用程序的启动运 行, 那么系统给应用程序分配的句柄总是不一样,这和每次电影院售给我们的 门票总是不同的一个座位是一样的道理。指针与句柄的区别对于Wind32 API,尽管为每个对象分配了数据块,但是微软不想向用户程 序返回指针。 对于一个聪明的程序员来说, 指针包含了太多的信息。 它给出了对 象存储的确切位置。 指针一般允许对对象的内部表示进行读写操作,而
19、这些内 部表示也许正是操作系统想隐瞒的。 指针还使越过进程地址空间共享对象变得困 难。为了对程序员进一步隐 藏信息,Win32对象创建程序实例一般会返回对象 句柄。对象可以映射到唯一句柄, 句柄也可由映射到唯一的对象。 为了保证句柄 能够完成信息隐藏的的任务, 对象和句柄之间的映射没有任何文档记载,不保 证固定不变,而已仅有微软知道这种映射, 或者还有少数系统级工具开放商知道。对象指针和句柄之间的映射可以由函数 Encode和Decode来实现,原型如下: HANDLE Encode(void* pObject);Void* Decode(HANDLE hObject);在极端情况下,句柄可以
20、和对象指针相同,Encode和Decode只需做类型转换,对象和句柄之间的映射主要是全等映射。在 Win32 API中,实例句柄(HINSTANCE或者模块句柄(HMODUL是指向映射 到内存的PE文件映像的指针。LockResource用来锁住全局 资源句柄得到指针, 但实际上它们的值相同。LockResource返回的资源句柄只是伪装后的内存映射 资源的指针。通常情况下,对象指针和句柄之间的映射是基于表格的映射。操作系统创建表格或者是一级表示保存所有要考虑的对象。需要创建新对象时,首先要在表格中找到空 入口。然后就把表示对象的数据添入其中。当对象被删除时,它的数 据成员和它在中的入口被释放
21、,以便再利用入口。用这种基于表的对象管理方法, 表中的索引可以很好的组成对象的句柄,编码和解码也很简单。(在Win32 API中,内核对象是用进程表实现的。为了容纳大量内核对象, 每个进程都有自己的内核对象表。NT/2000内核执行体中一部分是对象管理器, 它只管理内 核对象。对象管理器提供函数 ObReferenceObjectByHandle。根据 DDK( Driver Develepment Kits )文档,它提供对象指针的解码全过程,如果存 取操作被批准,则会返回对象体相应的指针。因此对于一个把对象句柄翻译称为 对象指针的解码全程来说,额外的安全检查很重要。上面有个非常好的工具 H
22、andleEx,它能够列出 Windows NT/2000的内核对象。只有句柄是不够的,尽管句柄提供了近乎完美的抽象, 信息隐藏和保护,但 是它也是程序员遭受挫折的地方。在像 Win32 API这样以句柄为中心的API中, 微软没有任何文档记载对象的内部表示以及对象是如何管理的,也没有提供参考实现,程序员只有函数原型,微软文档和或多或少基于微软文档的书籍。程序员面临的首要问题包括系统资源。当对象被创建,对象的句柄被返回时,谁都不 知道对象用了什么资源,因为对象的内部表示是不知道的。程序员是应该保护该对象还是应该在对象没有用时尽快把它删除呢?GDI支持的三种位图,为了减少系统资源消耗,应该使用哪
23、一种呢?CPU寸间时计算机的主要资源。当内部表示对程序员隐藏时,程序员就很难在复杂的算法设计中判断这种操作的复杂性 如果你用GDI组成复杂区域,算法的复杂度是 O(n)(问 题规模n),O()(问 题规模)还是O()。随着程序的隐藏,调试也成问题。程序运行 5分钟显示 了一些垃圾数据,猜测由资源泄漏,但是泄漏在哪儿?怎么解决?如果是处理系 统中几百个应用程序的管 理员,当系统资源很少时,如果找出问题?唯一可以 用的资源泄漏工具是BoundsChecker,它依赖API窥视技术查出对象创建和删除 之间的不匹配之 处。最让人受挫的地方可能是程序的兼容性。程序为什么能在Windows95下把GDI对
24、象从一个进程传递到另外一个进程,而 Windows NT/2000 不行?为什么 Windows95不能处理大的设备无关图?以GDI对象为例子,创建了 GDI对象,就会得到该对象的句柄。句柄的类型 有可能是HPEN HBRUSH,HFO或者是HDC中的一种。但最普通的 GDI对象类型 是HGDIOBJ它被定义成为空指针。HPEN勺实际编译类型是随着时间宏 STRICT 的不同而不同。不同GDI句柄的定义模仿了 GDI对象不同类的类层次结构,但 是没有真正的类层次结构。GDI对象一般有多个创建函数和一个接受 HGDIOBJ勺 析构函数DeleteObject。也可以用GetStockObject
25、取得预先创建好的 GDI对象句柄,无论GetStockObject调用顺序是如何,它返回的句柄看起来总是常 数。甚 至当运行一个程序的两个实例时,它在每个进程中返回相同的,唯一解 释是对象句柄堆是不变的,系统初始化,堆对象被创建并被所有进程重复使用。尽管Windows头文件把GDI句柄定义成为指针,但是检查这些句柄的值时,它 们根本不像指针。生成几个GDI对象句柄并看一下返回句柄的十六进制显示,就会发现结果从0x01900011变化到0xba04038Q如果HGDIOB像在 Windows头文 件里面定义的那样是指针,则前者是指向用户地址 空间中未分配的无效指针, 而后者是执行内核地址空间。这
26、就暗示 GDI 句柄不是指针。另一个发现是 GetStockObject ( BLACK_PEN和 GetStockObject(NULL_PEN)返回值只相差一, 如果句柄真的是指针的话,这不可能是存储内部GDI对象的空间,因此可以肯定 的说GDI对象句柄不是指针。系统 GDI句柄数限制为16384个,进程GDI句柄 数限制为12000个。这样单独的进程不会搞乱整个 GDI系统。但是Win dows 2000 第一版没有对每个进程加以限制。现在在同一个系统下运行两个 GDIHandles, 在每一个进程中调用8192次CreatePen。第一个很好 的创建了对象,第二个在 7200左右会停止
27、。第二个进程失败后,整个屏幕一团糟,这个试验表示GDI对象同样是从同一个资源池分配的。系统中的进程使用 GDI 资源时会互相影响。把8192和7200相加。考虑到GDIHandle属性页面和其它进程的页面使用的 GDI 对象句柄,可以猜测,GDI句柄数目 有系统范围限制:16384。GDI对象存储于 系统范围内的固定大小的对象表中, 称之为对象句柄表。 它是一个大小固定的表, 而不是一个会动态增长的数据结 构。这就意味着简明和效率。 但是 缺点就 是前面说的,限制了 GDI句柄数:16384个。下面看看HGDIOB的构成,Windows NT/2000下,GDI返回的对象句柄是32位值,组成8
28、位十六进制数。如果用 GDIHandles创建很多GDI对象,注意到其中显示的双字句柄的低位字,会发现它们都在0x000到0x3FFF之间。低位字在进程中总是唯一的,出了堆对象外, 低位字甚至在进程中也是唯一的。句柄的低位有时候按照递增 的顺序排列,有 时候又递减。在进程间也是这样。例如,某些情况下, CreatePen 在低位返回 0x03C1,另一个进程中的下一个 CreatePen在低位返回0x03C3对这些现象的 解释是HGDIOB的低位字是对系统范围的16384个GDI对象所组成的表的索引。 再来关注高4位的十六进制数。创建几个画刷,几个画笔,几个字体,DC等。不难看出相同类型的GD
29、I对象句柄有个共同特点:相同类型的对象句柄的第三位 和第四位十六进制数几乎都是相同 的。画刷句柄的第三位和第四位总是 0x90 和0x10,画笔总是0x30和0xb0等等。最高位是1 (二进制)的对象句柄都是堆 对象。因此可以有足够的证 据说对象句柄的第三位和第四位十六进制数是对象 类型的编码和堆对象标记。在 32位GDI句柄值中余下的两个十六进制位暂时还 没找到有意义的模式。总结一 下,GDI对象句柄由8位位置高位,一位堆对象 标记,7位对象类型信息和高四位为0的16位索引组成。因为GDI对象表是由 系统中所有过程和系统DLL所 共享的,桌面,浏览器,字处理器以及 DirectX 游戏都在为同一个 GDI句柄的储存区而竞争。而在 Windows 2000中,DirectX 则使用一个独立的对象句柄表。GDI句柄表一般存储在内核模式的地址空间里以 使图形引擎能很容易访问它,通过一定技巧,将为每 个使用GDI的进程在用户 模式存储空间里面建立表
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 广东省2026年成人高考艺术概论练习题及答案
- 防城港市注册测绘师资格考试(测绘综合能力)题库及答案(2026年)
- 2026年重庆市风景园林职称(风景园林工程与技术)综合试题及答案
- 2026年上半年湛江全国英语等级考试(PETS)一级B全真模拟试题及答案解析
- 2026年卫生高级职称面审答辩(放射医学技术)在线题库及答案(正高面审)
- 2026年中级经济师试题人力资源管理专业知识与实务测试题及答案
- 社区卫生服务中心驾驶员装卸作业安全操作规程
- 有限空间作业单位安全管理员定期维护安全操作规程
- 延安中考政治试题及答案
- 2025年7月公共营养师基础知识测试题+答案
- 标准预防与额外预防
- 双重预防体系培训
- 惠民政策宣讲
- 电力应急救援队伍建设与管理方案
- 医学院《病历书写》评分表
- 全2023年叉车理论考试参考题库及答案3套
- 电线电缆基础知识培训讲义
- 移模管理办法
- GB/T 13247-2019铁合金产品粒度的取样和检测方法
- 七年级数学-绝对值化简专题训练
- 生态治理技术课件
评论
0/150
提交评论