Windows HANDLE是什么

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

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

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

HANDLE的定义

 我们可以在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是不同的,只能在他们对应的函数中使用。

  • 23
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值