首页 > 其他分享 >《语法篇》HANDLE句柄

《语法篇》HANDLE句柄

时间:2023-07-27 14:55:30浏览次数:30  
标签:Widget HANDLE name 句柄 用户 语法 我们 定义

HANDLE是什么

简单理解:HANDLE是一个void指针,作为资源对象的标识号,为什么要用标识号不用地址?因为操作系统不想让我们知道资源对象的地址

详细介绍看下面。

参考链接:https://blog.csdn.net/maowei117/article/details/55254855

最近在接触windows编程,在多线程编程中遇到了这样的语句:

HANDLE mMutex;
mMutex = CreateMutex(NULL, FASLE, NULL);

 这里的HANDLE被翻译为句柄,句柄这个词太抽象了,那么到底什么是一个句柄呢?在MSDN中我们可以看到HANDLE的解释类似于一种能够访问线程、文件、图片等系统资源的指针。但是HANDLE和我们平常的指针又有什么区别呢,为什么又要特意定义一个HANDLE呢?本篇主要讲HANDLE是什么。

HANDEL的定义

我们可以在windows.h中找到HANDLE的定义

typedef void *HANDLE;

可以看到HANDLE的定义很简单,就是一个void * ,但是我们知道void *可以通过强转为任意类型的指针,所以在使用HANDLE的时候,可以用HANDLE指向所有的数据结构(基本数据类型、结构体、类),其实这就是HANDLE使用得很多的原因,因为任何的数据结构都可以用HANDLE来表示。

如果不使用HANDLE

在这里我借用stackoverflow上面的一个例子来说明这个问题。首先我自己需要定义一个结构体Widget,这个结构体中包含了一个id和一个name来存储信息。

struct Widget {
	int id;
	char *name;
};

其他的程序员需要获得其中的id和name信息。那么就需要一个GetWidget方法,来获得某个特定的Widget,这样就能得到其中的id和name信息了。如果没有HANDLE,我们很可能这样写:

Widget * GetWidget (std::string name)
{
	Widget *w;

	w = findWidget(name);

	return w;
}

别人调用我们的方法如下:

Widget *mWidget;
mWidget = GetWidget("first");
mWidget.setId(10);
mWidget.setName("second");

乍看起来好像这样调用没有什么问题,但是实际上存在着很多的隐患。

隐患1

 注意到这个Widget是我们自己定义的一个结构体,比如我们将上面的定义写在Widget.h中,那么用户在使用我们的程序的时候就需要include”Widget.h”,用户需要得到我们定义Widget的头文件才能使用。而且这只是其中的一个结构体,如果我们定义了很多的结构体,分别在不同的头文件中,那么我们不得不把所有的头文件都打个包让用户能下载。很多情况下我们并不希望把自己的设计暴露给用户,可是现在我们不得不这样。

隐患2

 但是如果我们将Widget的头文件暴露给了用户,就意味着用户知道了我的Widget是如何定义的,他们知道在Widget这个结构体中,前4个字节代表id,然后接下来是一个指向name的char *。这种情况下很可能有些不听话的用户会想:“为什么我要按照你给我的函数来得到我需要的数据呢?我现在都知道我要什么数据了,我要这么干:”

Widget *mWidget;
mWidget = GetWidget("first");
mWidget.id = 10;
mWidget.name = "mw";

 在这种情况下,用户直接就绕开了我们为其准备的setId和setName,同样达到了他的目的。但是他却不知道我们的setId和setName除了修改了id,还做了一些别的事情(比如将原来的id和name存储在某个队列中)。这样我们便无法控制用户的操作,很容易导致程序乱套。

隐患3

 因为我们将Widget的定义暴露给了用户,那我们就无法保证他们不会对我们的头文件动手动脚。如果用户不小心将Widget的定义进行了修改,那么程序基本上很难按照我们想象中去运行了。

隐患4

 有一天我们想到了一个更好的方法去定义这个Widget,并且通过这种方式能让软件的性能提升20%!没有程序员能够拒绝这样的诱惑,所以我们当然铆足干劲去重构,去让代码变得更完美,于是我们的Widget变成了这样:

struct newWidget {
	...
};

当然随着我们这个Widget的修改,对于Widget的操作也进行了修改,所以我们有了新的GetWidget:

newWidget GetWidget (std::string name)
{
	newWidget *w;

	w = findWidget(name);

	return w;
}

我们看着自己的重构,看着20%效率的提升,夸着自己是个天才。想到用户要是用上这个版本的程序,会是怎样一副惊讶的表情…等等,用户?用户那里保存的是前一个版本的Widget,但是现在我已经把它改成了newWidget了,那岂不是所有的用户都必须要重新下载新的头文件?明明我所有的接口函数的名字都没有修改,但是因为我修改了我背后的实现,就需要用户重新去下载新的头文件,那这样岂不是每次修改都要全部用户重新下载?想到这里,你只能默默把版本倒回去,所有的修改都泡了汤。

使用了HANDLE

好在有HANDLE这样一个东西。这个时候我定义这个GetWidget就可以这样定义了:

typedef void *HANDLE
HANDLE GetWidget (std::string name)
{
	Widget *w;

	w = findWidget(name);

	return reinterpret_cast<HANDLE>(w);
}

发现了么,不管我们定义了多少结构体,我们都统一使用HANDLE来访问它们。现在我们不需要再为用户提供Widget的定义,而只需要提供HANDLE的定义。Widget的实现被隐藏在内部,当然用户没有办法修改Widget的定义。而且因为HANDLE的定义只是一个void ,用户也不知道这个void 到底指向了怎样的一块区域,也就不敢轻易直接对指向的存储进行改动。
 更加重要的一点是,当我们想修改widget的定义的时候:

typedef void * HANDLE;

HANDLE GetWidget (std::string name)
{
	NewImprovedWidget *w;

	w = findImprovedWidget(name);

	return reinterpret_cast<HANDLE>(w);
}

由于隐藏了实现细节,我们可以根据自己的需要对自己定义的结构体做出改动了。

总结

1、HANDLE提供了一种统一的方式去获得系统资源,并对其进行操作。
2、HANDLE使得程序设计的细节得以被隐藏,从而能够更加方便地对细节的实现进行修改。
3、由于不知道HANDLE所指向的具体的数据结构,所以我们必须根据源码提供者的API使用HANDLE,而且不同种类的HANDLE是无法混用的。例如:GetModuleHandle返回的HANDLE和GetFileHandle返回的HANDLE是不同的,只能在他们对应的函数中使用。

HANDLE的理解

参考链接:https://blog.csdn.net/weixin_45758146/article/details/107050622

HANDLE:句柄,是WINDOWS用来表示对象的,是一个通用句柄表示。
在WINDOWS程序中,有各种各样的资源(窗口、图标、光标等),系统在创建这些资源时为他们分配内存,并返回标示这些资源的标示号,即句柄。
但是如果这些资源的位置变了呢?
HANDLE是固定的,不会变,但是对象的地址会变,当对象在内存中的位置发生改变后,我们不能通过之前的对象指针找到对象。HANDLE能用来记录对象的最新地址。
也就是说,HANDLE像是中间商,联络着WINDOWS API和看不见的对象,所以可以通过HANDLE让对象做事。(不能让我们知道对象的内存地址是因为操作系统怕受到不利操作)。

各种HANDLE的定义,如HDC,HPEN,HINSTANCE等等,你会发现有这样一个声明:

DECLARE_HANDLE(HDC);

再把DECLARE_HANDLE这个宏展开:

#define DECLARE_HANDLE(name) struct name##__ { int unused; }; 
typedef struct name##__ *name

用HDC替换上面的name:

struct HDC__

{

int unused;

};

typedef struct HDC__ *HDC

所以句柄就是一个指向某一结构的指针,这个结构体只有一个成员,它是一个整数。
HANDLE的定义,在winnt.h头文件中:

typedef PVOID HANDEL;

PVOID是什么?

typedef void *PVOID;

HANDLE不过是一个指向void型,即无类型的指针,嗯,目前的指针是32位的吧.其实也不能说HANDLE是一种指针,它只充当一种索引的作用。

标签:Widget,HANDLE,name,句柄,用户,语法,我们,定义
From: https://www.cnblogs.com/fusio/p/17584948.html

相关文章

  • 5.2条件测试的语法
     ......
  • Js基础语法
    操作DOM节点例如:fuctiona(){//获取某个标签varnode=documrnt.getElementById("node");//获取标签里的内容(包括子标签和文本)varhtm=node.innerHTML;alert(htm);//修改该节点里的内容node.innerHTML="<p>修改后的内容</p>";} ......
  • java MethodHandle与Reflection 效率
    JavaMethodHandle与Reflection效率在Java编程领域中,反射(Reflection)是一种强大的功能,它允许我们在运行时检查和修改类、方法、字段等的信息。然而,反射也因为其相对较慢的性能而备受诟病。为了解决这个问题,Java7引入了MethodHandle,这是一种新的方法调用机制,它可以提供比反射更高效......
  • 1、java基础语法
    1、swicth(表达式){}表达式的值可以的数据类型:byteshortintcharString枚举( jdk1.8新特性)......
  • vue指令及模板语法
    说实话,看了这两节之后,改变认知的,突然发现自己落后了这么多,真不应该v- 这个指令集的确666,把许多东西的实现简化了,真心学到了不少,菜鸟这方面讲的真是不错https://www.runoob.com/vue3/vue3-directives.html我在这就不献丑了,而且里面的各种试例的可运行代码环境我非常喜欢,可以......
  • ltsql 增加语法兼容类型提示
    背景lightdb目前兼容mysql,oracle语法。为了提醒用户正在使用的是哪种类型的数据库,lightdb在ltsql端增加了连接提示。用户在初次连接数据库或在psql内切换数据库时,会打印一条消息提示。该功能出现在lightdb版本:LightDB1.0.V202303.00.000测试用例$#不带参数运行,......
  • CMake语法基础
    基本表达式if—CMake3.27.1DocumentationCmake中的条件判断if/elseif/else-简书if(<constant>)Trueiftheconstantis1,ON,YES,TRUE,Y,oranon-zeronumber(includingfloatingpointnumbers).Falseiftheconstantis0,OFF,NO,FALSE,N,IGNORE,NOT......
  • python学习01:Python基础语法与数据类型
    一、Python注释通常用于解释代码,这段打开主要是想表达什么意思,注释后的代码不会再代码中运行,例如:#打印HelloWorldprint("HelloWorld")注释的方式:#python注释(快捷键:Ctrl+/(选中你想注释的代码就可全部注释掉))=========>单行注释''''print('hello') ''''''�......
  • python 根据句柄获取窗体截图
    Python根据句柄获取窗体截图概述在开发过程中,我们经常会遇到需要获取窗体截图的需求。本文将教你如何使用Python根据句柄获取窗体截图。步骤下面是整个流程的步骤:步骤描述步骤1导入相关模块步骤2获取窗体句柄步骤3获取窗体位置和大小步骤4根据窗体位置......
  • markdown语法
    超链接链接文本放在中括号内,链接地址放在后面的括号中,链接title可选超链接Markdown语法代码:[超链接显示名](超链接地址"超链接title")markdown语法网站:网站链接图片使用感叹号(!),然后在方括号增加替代文本,图片链接放在圆括号里,括号里的链接后可以增加一个可选的图片标......