当前位置 行业新知 正文 下一篇:

句柄(句柄和指针的区别)

本篇文章给大家谈谈句柄(句柄和指针的区别),以及句柄(句柄和指针的区别)对应的知识点,文章可能有点长,但是希望大家可以阅读完,增长自己的知识,最重要的是希望对各位有所帮助,可以解决了您的问题,不要忘了收藏本站喔。

Python中什么是句柄?

谢谢邀请,我主要做java,python不是很了解

hwnd是什么数据类型?

句柄就是系统分配给窗口的编号,就像一个人的身份证号码样.系统桌面本身就是一个窗口, 它的句柄一般是0, 其他打开的窗口都是桌面窗口的子窗口

句柄是指针的指针、或者是指针的索引?以及句柄与MFC对象的关系

1 通俗化理解句柄

句柄(handle)是一个32位(4个字节,64位程序中为8字节)的无符号整数,实际上是Windows在内存中维护的一个对象(窗口等)内存物理地址(32或64位)列表的整数索引。因为Windows的内存管理经常会将当前空闲对象的内存释放掉,当需要访问时再重新提交到物理内存,所以对象的物理地址是变化的,不允许程序直接通过物理地址来访问对象。程序将想访问的对象的句柄传递给系统,系统根据句柄检索自己维护的对象列表就能知道程序想访问的对象及共物理地址了。

我们知道,所谓指针是一种内存地址。应用程序启动后,组成这个程序的各对象是驻留在内存的。如果简单地理解,似乎我们只要获知这个内存的首地址,那么就可以随时用这个地址访问对象。但是,Windos是一个以虚拟内存为基础的操作系统。在这种系统环境下,Windows内存管理器经常在内存中来回移动对象,以此来满足各种应用程序的内存需要。对象被移动意味着它的地址变化了。如果内存总是如此变化,我们该到哪里去找该对象呢?为了解决这个问题,Windows系统为进程分配固定的地址(句柄)来存储进程下的数据对象变化后的地址。Windows操作系统为各应用程序腾出一些内存地址,用来专门登记各应用对象在内存中的地址变化,而这个地址本身是不会变的。Windows内存管理器移动对象在内存中的位置后,把对象新的地址告知这个句柄地址来保存。这样我们只需要记住这个句柄地址就可以间接地知道对象具体在内存中的哪个位置。这个地址是在对象装载时由系统分配的,当系统卸载时又释放给系统。但是,必须注意的是,程序每次重新启动,系统不能保证分配给这个程序的句柄还是原来的那个句柄。而且绝大多数情况下的确不一样。假如我们把进入电影院看电影看成是一个应用程序的启动运行,那么系统给应用程序分配的句柄总是不一样,这和每次电影院售给我们的门票总是不同的座位是一样的道理。

所以,对于winows应用程序中的不同对象和同类中的不同的实例,诸如,一个窗口、按钮、图标、滚动条、输出设备、控件或者文件等,数据对象加载进入内存中之后即获得了地址,但Windows并不允许应用程序直接通过这个地址来访问这些对象,而是通过标识或索引指针的句柄来访问相应的对象的信息。

在Windows环境中,句柄是用来标识项目的,这些项目包括:

*.模块(module)

*.任务(task)

*.实例(instance)

*.文件(file)

*.内存块(block of memory)

*.菜单(menu)

*.控制(control)

*.字体(font)

*.资源(resource),包括图标(icon),光标(cursor),字符串(string)等

*.GDI对象(GDI object),包括位图(bitmap),画刷(brush),元文件(metafile),调色板(palette),画笔(pen),区域(region),以及设备描述表(device context)。

(HINSTANCE(实例句柄),HBITMAP(位图句柄),HDC(设备描述表句柄,相应的,CDC是设备描述表类),HICON(图标句柄)等等,这当中还有一个通用的句柄,就是HANDLE。)

上面的这些项目,通过其指针去定位是无法找到或不稳定的,所以要使用这些项目,必须获取其句柄,句柄才是唯一标识稳定的定位。所以,如果想再通俗一定理解项目的指针与句柄,可以想像有一票供展览货品,数量比较多,储存在某一个公司的仓库里,但仓库的位置是有限的,可能储存了很多票这样的货,这些货因为不同的消耗进度,所以数量的增减会不一样,为此,不同票的货需要在不同的位置移动,以确保位置的最大化利用。怎么做呢?仓库的位置编号,也就是内存地址对应的指针,然后每票货都有一个在整个公司范围内唯一的条码号,这就是句柄。这个公司只需要维护一份条码号与指针对应的索引表格就可以了。货物移动时,将目的地的指针更改到条码号。想要找货物时,只需要在索引表格中找到这个条码号,就可以查找到货位的指针及其具体的位置了。这样既可以保证仓库空间的利用,也有方便地找到货位。

句柄是对象生成时系统指定的,是引用指针的一个标识值或ID号,该标识可以被系统重新定位到一个内存地址上。句柄类似指向指针的指针,仅仅是类似,通过句柄可以找到对应的数据,但是不是二级指针。

句柄与普通指针的区别在于,指针包含的是引用对象的内存地址,而句柄则是由系统所管理的引用标识,该标识可以被系统重新定位到一个内存地址上。这种间接访问对象的模式增强了系统对引用对象的控制。

在内存有限的年代,为了使有限的内存资源得到充分利用,有了虚拟内存和虚拟地址的概念或内存管理方式,相应的,也就有了句柄这样一个编程概念。如今,内存容量的增大和虚拟内存算法使得更简单的指针愈加受到青睐,而指向另一指针的那类句柄受到冷落。

指针标记某个物理内存地址,Windows系统用句柄标记系统资源,隐藏系统的信息。

以上是Windows编程对句柄的理解。而在一般的程序设计中,句柄是一种特殊的智能指针 。当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄。

Windows系统中有许多内核对象(这里的对象不完全等价于”面向对象程序设计”一词中的”对象”,虽然实质上还真差不多),比如打开的文件,创建的线程,程序的窗口,等等。这些重要的对象肯定不是4个字节或者8个字节足以完全描述的,他们拥有大量的属性。为了保存这样一个”对象”的状态,往往需要上百甚至上千字节的内存空间,那么怎么在程序间或程序内部的子过程(函数)之间传递这些数据呢?拖着这成百上千的字节拷贝来拷贝去吗?显然会浪费效率。那么怎么办?当然传递这些对象的首地址是一个办法,但这至少有两个缺点:

所以,Windows操作系统就采用进一步的间接:在进程的地址空间中设一张表,表里头专门保存一些编号和由这个编号对应一个地址,而由那个地址去引用实际的对象,这个编号跟那个地址在数值上没有任何规律性的联系,纯粹是个映射而已。

所以,Windows操作系统就采用进一步的间接:在进程的地址空间中设一张表,表里头专门保存一些编号和由这个编号对应一个地址,而由那个地址去引用实际的对象,这个编号跟那个地址在数值上没有任何规律性的联系,纯粹是个映射而已。

在Windows系统中,这个编号就叫做”句柄”。

从广义上,能够从一个数值拎起一大堆数据的东西都可以叫做句柄。句柄的英文是”Handle”,本义就是”柄”,只是在计算机科学中,被特别地翻译成”句柄”,其实还是个”柄”。从一个小东西拎起一大堆东西,这难道不像是个”柄”吗?

Handle本身是一个32或64位的无符号整数,它用来代表一个内核对象。它并不指向实际的内核对象,用户模式下的程序永远不可能获得一个内核对象的实际地址(一般情况下)。那么Handle的意义何在?它实际上是作为一个索引在一个表中查找对应的内核对象的实际地址。那么这个表在哪里呢?每个进程都有这样的一个表,叫句柄表。该表的第一项就是进程自己的句柄,这也是为什么你调用GetCurrentProcess()总是返回0x7FFFFFFF原因。

简单地说,Handle就是一种用来”间接”代表一个内核对象的整数值。你可以在程序中使用handle来代表你想要操作的内核对象。这里的内核对象包括:事件(Event)、线程、进程、Mutex等等。我们最常见的就是文件句柄(file handle)。

另外要注意的是,Handle仅在其所属的进程中才有意义。将一个进程拥有的handle传给另一个进程没有任何意义,如果非要这么做,则需要使用DuplicateHandle()在多个进程间传递Handle。

MFC Object和Windows Object的关系

MFC是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。

所谓Windows Object(Windows对象)是Win32下用句柄表示的Windows操作系统对象;所谓MFC Object (MFC对象)是C++对象,是一个C++类的实例,这里MFC Object是有特定含义的,指封装Windows Object的C++ Object,并非指任意的C++ Object。

MFC Object 和Windows Object是不一样的,但两者紧密联系。以窗口对象为例:

一个MFC窗口对象是一个C++ CWnd类(或派生类)的实例,是程序直接创建的。在程序执行中它随着窗口类构造函数的调用而生成,随着析构函数的调用而消失。而Windows窗口则是Windows系统的一个内部数据结构的实例,由一个“窗口句柄”标识,Windows系统创建它并给它分配系统资源。Windows窗口在MFC窗口对象创建之后,由CWnd类的Create成员函数创建,“窗口句柄”保存在窗口对象的m_hWnd成员变量中。Windows窗口可以被一个程序销毁,也可以被用户的动作销毁。MFC窗口对象和Windows窗口对象的关系如图2-1所示。其他的Windows Object和对应的MFC Object也有类似的关系。

MFC给这些类定义了一个成员变量来保存MFC Object对应的Windows Object的句柄。对于设备描述表CDC类,将保存两个HDC句柄。

MFC Object通过构造函数由程序直接创建;Windows Object由相应的SDK函数创建。

MFC中,使用这些MFC Object,一般分两步:

首先,创建一个MFC Object,或者在STACK中创建,或者在HEAP中创建,这时,MFC Object的句柄实例变量为空,或者说不是一个有效的句柄。

句柄(句柄和指针的区别)插图

然后,调用MFC Object的成员函数创建相应的Windows Object,MFC的句柄变量存储一个有效句柄。

(句柄经常做为函数返回值或函数参数而存在。)

MFC Object和Windows Object的对应关系:

描述Windows句柄MFC Object窗口HWNDCWnd and CWnd-derived classes设备上下文HDCCDC and CDC-derived classes菜单HMENUCMenu笔HPENCGdiObject类,CPen和CPen-derived classes刷子HBRUSHCGdiObject类,CBrush和CBrush-derived classes字体HFONTCGdiObject类,CFont和CFont-derived classes位图HBITMAPCGdiObject类,CBitmap和CBitmap-derived classes调色板HPALETTECGdiObject类,CPalette和CPalette-derived classes区域HRGNCGdiObject类,CRgn和CRgn-derived classes图像列表HimageLISTCimageList和CimageList-derived classes套接字SOCKETCSocket,CAsynSocket及其派生类

可以从一个MFC Object得到对应的Windows Object的句柄;一般使用MFC Object的成员函数GetSafeHandle得到对应的句柄。

可以从一个已存在的Windows Object创建一个对应的MFC Object; 一般使用MFC Object的成员函数Attach或者FromHandle来创建,前者得到一个永久性对象,后者得到的可能是一个临时对象。

从指针到句柄 :

CWnd *pWnd = GetDlgItem(ID_***); // 取得控件的指针,CWnd是窗口类

HWND hwnd = pWnd->GetSafeHwnd(); // 取得控件的句柄

hdc=GetDC(hwnd);

有了DC绰号hdc,就可以画图了:

MoveToEx( hdc,0, y_shift-(draw_yu-y_min)*ry, NULL );

LineTo(hdc,900,y_shift-(draw_yu-y_min)*ry);

CPaintDC dc(this); // device context for painting

//CPaintDC是CDC的一个派生类,只是在OnPaint()函数中使用;

CPen pen1(PS_SOLID, 1, RGB(198, 198, 198));

//把画笔选到设置描述表当中.覆盖默认画笔.

CDC* pDC = pWnd->GetWindowDC(); //取得CDC的指针

//或CDC *pDC = GetDC();

dc.SelectObject(&pen1);

dc.MoveTo(0,455);//移动到该坐标

dc.LineTo(917,455); //画线画到这个点

pDC->TextOut(92,50,”(如:)”);

从句柄到指针 :

CComBox* com; //声明一个CComBox类的指针

HWND hwnd = GetDlgItem(hwnd,IDC_XX)->GetSafeHwnd();

com = (CComBox*)FromHandle(hwnd);

相当于

CWnd* pCtrl = this->FromHandle(::GetDlgItem(hwnd,IDC_XX));

注意这里FromHandle是CWnd的一个静态函数,也就是说只能用在CWnd或者CWnd的子类,如CDIalog,或者你自己的继承自CWnd的类中。

使用控件指针:

CWnd *pWnd = GetDlgItem(ID_***); // 取得控件的指针,CWnd是窗口类

HWND hwnd = pWnd->GetSafeHwnd(); // 取得控件的句柄

CRichEditCtrl* pRichEdit = (CRichEditCtrl*)(CWnd::FromHandle(hwnd));//hwnd是上面两句获得的句柄,转化为对应控件的指针

CString szRichMsg;

szRichMsg.Format(“%s”,msg.c_str());

CString szUname;

std::string uname=stanza->from().username();

szUname.Format(“%s”,uname.c_str());

pRichEdit->ReplaceSel((szUname+”\t”+m_Time)+”\n”+szRichMsg+”\n”);//使用控件指针

以下用控件指针也是可以操作控件的:

CEdit *edit1=(CEdit*)GetDlgItem(IDC_EDIT2);

edit1->GetWindowText(m_text);

ID–句柄–指针三者之间的互相转换

id->句柄———–hWnd = ::GetDlgItem(hParentWnd,id);

id->指针———–CWnd::GetDlgItem();

句柄->id———–id = GetWindowLong(hWnd,GWL_ID);

句柄->指针——–CWnd *pWnd=CWnd::FromHandle(hWnd);

指针->ID———-id = GetWindowLong(pWnd->GetSafeHwnd,GWL_ID);

GetDlgCtrlID();

指针->句柄——–hWnd=cWnd.GetSafeHandle() or mywnd->m_hWnd;

HICON->ID——–HICON hIcon = AfxGetApp()->LoadIcon(nIconID);

HICON hIcon = LoadIcon(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(nIconID));

3 再说Windows窗口句柄与MFC窗口类

Windows窗口句柄是用来确定Windows窗口的一个值,在win32编程中,都是很常见的。但是MFC为了简化编程就提供了CWnd窗口类将其包装了,然后对API函数封装后,就在创建对象时将窗口句柄关联到对象,然后提供的成员函数就可以省掉窗口句柄参数了。所以,在基于CWnd继承而来的所有类中,都有一个公有的成员变量m_hWnd,这个成员变量就是窗口对象关联的windows窗口句柄。我们在类中可以直接使用这个窗口句柄成员变量。这个窗口对象就是标准的C++对象。

其实MFC窗口类并不神奇,就是包装了一下API而已。m_hWnd的类型就是HWND。因为窗口类都是一个C++对象,而C++对象就是我们平时使用C++时用的类实例化而来的。这么一说,其实窗口类和窗口对象其实就是将窗口句柄作为C++对象的一个成员,然后创建时关联的而已。在CreateWindow函数中,传入了窗口的ID,这样就让窗口类可以获得这个窗口ID的窗口句柄,进而保存到窗口对象成员m_hWnd中。这样就形成了关联。这样就创建了一个窗口对象。

MFC给这些类定义了一个成员变量来保存MFC Object对应的Windows Object的句柄。对于设备描述表CDC类,将保存两个HDC句柄。

CDC类有两个成员变量:m_hDC,m_hAttribDC,它们都是Windows设备描述表句柄。CDC的成员函数作输出操作时,使用m_Hdc;要获取设备描述表的属性时,使用m_hAttribDC。

void CMyView::OnDraw(CDC *pDC)

{

CPen penBlack; //构造MFC CPen对象

if (penBlack.CreatePen(PS_SOLID, RGB(0, 0, 0)))

{

CPen *pOldPen = pDC->SelectObject(&penBlack)); //选进设备表,保存原笔

pDC->SelectObject(pOldPen); //恢复原笔

}else

{

}

}

pDC->SelectObject(&penBlack)返回了一个CPen *指针,也就是说,它根据原来PEN的句柄创建了一个MFC CPen对象。

-End-

句柄的理解及窗体句柄的应用

昨天小编只是在微头条随手发了一条关于句柄的内容,没想到居然很多人感兴趣,由于微头条的结构限制并不能很好的将句柄讲清楚,今天特地在这里将小编所掌握的关于句柄的知识分享给大家。

句柄是什么

我们先看权威的解释:句柄是整个Windows编程的基础。一个句柄是指使用的一个唯一的整数值,即一个4字节(64位程序中为8字节)长的数值,来标识应用程序中的不同对象或实例,诸如,一个窗口,按钮,图标,滚动条,输出设备,控件或者文件等。应用程序能够通过句柄访问相应的对象的信息;

句柄控制对象的原则是:程序在运行时系统会在为进程分配一块内存,用来存储32位无符号整数,这些数值就是句柄,每个句柄在此处可以理解为一个指针变量,指向系统中一块在进程期间固定的内存。而这块内存不存别的东西,全部储存的是一个个地址,这个地址就是在进程运行期间各对象所在内存的地址。通过这个地址我们就能获取到对应的对象;Windows是一个以虚拟内存为基础的操作系统,在这种环境下,Windows内存管理器经常在内存中来回移动对象,以此来满足各种应用程序的需要。对象被移动意味着它的地址变化了,所以在运行过程中我们需要一个唯一且不变的标记来与每个对象对应,已达到不失去对对象的控制权的目的;如下图所示:

理解句柄

上面说的还是有点绕,那么我们该怎么去理解才能够更容易的掌握呢?我觉得用身份证来进行印证理解是最贴切的。身份证和句柄一样都是唯一的,且都代表一个具体的实体(对象)。每个人拥有一个唯一的身份证号码,通过身份证号码我们有可以去警察局处查住址登记簿,不管你这个人住址怎么变,只要你每次变更住址都会到警察局登记更新你的新住址,这样我们都能通过身份证号码在住址登记簿找到你的住址,从而找到你这个人。

或者你也可以用二级指针的概念来理解句柄,只不过句柄不具备很多指针的功能,比如赋值、获取属性的,在这里只是用来印证理解。

查看窗体句柄的工具

这里介绍一个可以快速查看窗体句柄的一个工具,大家在开发中可以通过这个工具辅助自己开发。这个工具就是spy++

spy++

这个软件如果大家是用vs开发的话,安装vs一般是默认安装这个软件的,大家按下图所示找一下就可以:

窗体句柄有什么用

关于窗体句柄的作用,这里给大家举一个最简单的例子,大家就会有很大的冲动想去学习句柄的使用了。假设现在有一个别人开发好的软件,现在需要向这个软件发一些命令,但是它又没有提供接口怎么办呢?这时候我们可以通过句柄来获取窗体控件对象,并通过windows的API去给对应的对象直接发消息;

这里通过windows定义的系统消息WM_GETTEXT(0x000D)来获取控件的text值;当然你还可以干很多事,只要是windows系统有定义的命令都可以发送给控件对象,如果让button点击一下,输入框的值改变等等;获取text值的代码如下(这边是用CSharp实现的,因为开发速度快~~):

这里就不详细讲user32这里的系统API的用法了,因为内容太多,这里只是给大家演示一下windows窗体句柄的一个作用,让大家有一定的了解;

如果大家对操控别人的窗体程序有兴趣可以留言给我,我可以专门写一篇关于这个的文章;

END

纯手KEY,希望大家多多支持小编,点个关注~~~

Windows系统中的句柄,到底是什么

任务管理器截图

上图中的内容相信大家都见过,红色框框中的句柄到底是什么,不知道大家有没有产生过疑问。今天我就来讲讲句柄。

由于Windows是支持虚拟内存机制的(不明白的请先行补充一下),这就导致某对象在一次换进患出后的地址几乎不可能一致。为解决这个问题,Windows引入了句柄。

系统为每个进程分配一定大小的内存区域来存放句柄,即一个个64bit的无符号整数值。每个无符号整数值相当于一个指针,指向内存中的另一个区域(设为area),当对象的位置发生变化时,area的值被更新为此刻对象在内存中的地址。这样,只要我们掌握了句柄的值就可以找到区域area,进而找到对象。而句柄的值在程序的一次运行过程中是不会改变的,操作系统以句柄来寻找对象。

我们从一些头文件,以及一些windows早期的代码中看一看句柄的定义

在Winnt.h头文件中定义了通用句柄:

#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;

在Windef.h种定义来特殊句柄:

#if !defined(_MAC) || !defined(GDI_INTERNAL) DECLARE_HANDLE(HFONT); #endif DECLARE_HANDLE(HICON); #if !defined(_MAC) || !defined(WIN_INTERNAL) DECLARE_HANDLE(HMENU); #endif DECLARE_HANDLE(HMETAFILE); DECLARE_HANDLE(HINSTANCE); typedef HINSTANCE HMODULE; /* HMODULEs can be used in place of HINSTANCEs */ #if !defined(_MAC) || !defined(GDI_INTERNAL) DECLARE_HANDLE(HPALETTE); DECLARE_HANDLE(HPEN); #endif DECLARE_HANDLE(HRGN); DECLARE_HANDLE(HRSRC); DECLARE_HANDLE(HSTR); DECLARE_HANDLE(HTASK); DECLARE_HANDLE(HWINSTA); DECLARE_HANDLE(HKL);

内存与句柄

可以看出,通用句柄时一个void指针,显然是一个马甲,微软并不想泄露句柄的真实类型。当然,微软还是一不小心在其他地方泄露来句柄的本质。如果你定义一个强制类型检查STRICT,又定义了特殊类型句柄DECLARE_HANDLE,对于诸如DECLARE_HANDLE(HMENU)定义如下:

typedef struct HMENU__ { int unused; } *HMENU;

守得云开见日出了,句柄实际上是一种指向结构题的指针。一些大神猜测Windows的句柄结构类似如下:

struct { int pointer; //指针段 int count; //内核计数段 int attribute; //文件属性段:SHARED等等 int memAttribute; //内存属性段:MOVABLE和FIXED等等 … };

内存管理器工作原理

在Windows系统中,内存管理器管理的直接对象就是句柄,以句柄管理指针。当Windows系统内存整理时检测内存属性端段,当可移动时,就移动逻辑地址,移动完之后更新新的地址到对应句柄的指针段中,当使用MOVABLE地址时必须LOCK,计数器将+1,内存管理器检测到计数器>0则不移动逻辑地址,此时才可获得固定的逻辑地址来操作无力内存,使用完之后再UNLOCK进行操作,内存管理器就可以再次移动逻辑地址来,所以在虚拟内存管理机制不会出现访问混乱的情况

感谢图片提供者

如果你还想了解更多这方面的信息,记得收藏关注本站。

本文来自网络,不代表立场,转载请注明出处:http://www.yuzhuoseo.com/news/19434.html