什么是Windows句柄?

在讨论Windows资源时,什么是“句柄”? 他们如何工作?

这是对资源(通常是内存或打开文件或pipe道)的抽象引用值。

正确地说 ,在Windows中(通常在计算中),句柄是一种抽象,它隐藏了API用户的真实内存地址,允许系统透明地重新组织物理内存给程序。 将句柄parsing为指针会locking内存,释放句柄会使指针失效。 在这种情况下,把它看作指针表的索引…你使用系统API调用的索引,系统可以随意改变表中的指针。

或者,当API编写者希望API的用户与返回的地址指向的细节绝缘时,可以给出真实指针作为句柄; 在这种情况下,必须考虑句柄指向的内容可能随时发生变化(从API版本到版本,甚至从调用返回句柄的API的调用) – 因此句柄应被视为一个不透明的值仅对 API有意义。

我应该补充说,在任何现代操作系统中,即使是所谓的“实指针”仍然是进程的虚拟内存空间中的不透明句柄,这使得操作系统能够pipe理和重新安排内存而不会使进程内的指针失效。

HANDLE是一个特定于上下文的唯一标识符。 通过上下文相关,我的意思是,从一个上下文中获得的句柄不一定可以用在任何其他同样适用于HANDLE的混合语境中。

例如, GetModuleHandle为当前加载的模块返回一个唯一标识符。 返回的句柄可以在接受模块句柄的其他函数中使用。 它不能被赋予需要其他types的句柄的函数。 例如,你不能给从GetModuleHandle返回到HeapDestroy的句柄,并期望它做一些明智的事情。

HANDLE本身只是一个整体types。 通常但不一定,它是指向某种底层types或内存位置的指针。 例如, GetModuleHandle返回的HANDLE实际上是指向模块的基本虚拟内存地址的指针。 但是没有规则说明句柄必须是指针。 一个句柄也可以是一个简单的整数(它可能被一些Win32 API用作数组的索引)。

HANDLE s是有意不透明的表示,它提供了内部Win32资源的封装和抽象。 这样,Win32 API可能会改变HANDLE后面的底层types,而不会以任何方式影响用户代码(至less这是这个想法)。

考虑这三个不同的Win32 API的内部实现,我刚刚做出来,假设Widget是一个struct

 Widget * GetWidget (std::string name) { Widget *w; w = findWidget(name); return w; } 
 void * GetWidget (std::string name) { Widget *w; w = findWidget(name); return reinterpret_cast<void *>(w); } 
 typedef void * HANDLE; HANDLE GetWidget (std::string name) { Widget *w; w = findWidget(name); return reinterpret_cast<HANDLE>(w); } 

第一个例子公开了关于API的内部细节:它允许用户代码知道GetWidget返回一个指向struct Widget的指针。 这有几个后果:

  • 用户代码必须能够访问定义Widget结构的头文件
  • 用户代码可能会修改返回的Widget结构的内部部分

这两种后果都可能是不可取的。

第二个例子通过返回void *隐藏用户代码的内部细节。 用户代码不需要访问定义Widget结构的头部。

第三个例子和第二个例子完全一样,但是我们只需要调用void * a HANDLE 。 也许这阻止了用户代码试图找出void *指向的内容。

为什么要经历这个麻烦? 考虑这个相同API的更新版本的第四个例子:

 typedef void * HANDLE; HANDLE GetWidget (std::string name) { NewImprovedWidget *w; w = findImprovedWidget(name); return reinterpret_cast<HANDLE>(w); } 

注意函数的接口与上面的第三个例子是一样的。 这意味着即使“幕后”实现改为使用NewImprovedWidget结构,用户代码也可以继续使用此新版本的API,而不进行任何更改。

这些例子中的句柄实际上只是一个新的,可能是友好的, void *名字,这正是Win32 API中的一个HANDLE (查看MSDN )。 它在用户代码和Win32库的内部表示之间提供了一个不透明的隔离墙,可以提高Windows版本之间使用Win32 API的代码的可移植性。

Win32编程中的一个句柄是一个表示由Windows内核pipe理的资源的标记。 句柄可以是窗口,文件等

句柄只是一种识别想要使用Win32 API的微粒资源的方法。

例如,如果你想创build一个窗口,并在屏幕上显示它,你可以执行以下操作:

 // Create the window HWND hwnd = CreateWindow(...); if (!hwnd) return; // hwnd not created // Show the window. ShowWindow(hwnd, SW_SHOW); 

在上面的例子中,HWND的意思是“一个窗口句柄”。

如果你习惯了面向对象的语言,你可以把HANDLE看作是一个没有方法的类的一个实例,它的状态只能被其他函数修改。 在这种情况下, ShowWindow函数修改窗口句柄的状态。

有关更多信息,请参阅句柄和数据types 。

句柄是由Windowspipe理的对象的唯一标识符。 它就像一个指针 ,但不是一个指针 ,它不是一个可以被用户代码解除引用来访问某些数据的地址。 相反,一个句柄被传递给一组函数,这些函数可以对句柄所标识的对象执行操作。

所以在最基本的层面上,任何types的HANDLE都是指针或指针的指针

 #define HANDLE void ** 

现在至于你为什么要使用它

让我们来设置一下:

 class Object{ int Value; } class LargeObj{ char * val; LargeObj() { val = malloc(2048 * 1000); } } void foo(Object bar){ LargeObj lo = new LargeObj(); bar.Value++; } void main() { Object obj = new Object(); obj.val = 1; foo(obj); printf("%d", obj.val); } 

因此,因为obj被赋值(复制并赋值给函数)foo,printf将打印1的原始值。

现在,如果我们更新富:

 void foo(Object * bar) { LargeObj lo = new LargeObj(); bar->val++; } 

有一个机会,printf将打印2的更新值。但也有可能foo会导致某种forms的内存损坏或exception。

原因是当你现在正在使用一个指针传递obj给你正在分配2M内存的函数时,这可能会导致操作系统移动内存来更新obj的位置。 既然你已经通过值的指针,如果obj被移动,然后操作系统更新指针,而不是函数中的副本,并可能导致问题。

foo的最后更新:

 void foo(Object **bar){ LargeObj lo = LargeObj(); Object * b = &bar; b->val++; } 

这将始终打印更新的值。

当编译器为指针分配内存时,它会将它们标记为不可移动的,因此,由大对象分配给函数的值所引起的任何重新洗牌内存都将指向正确的地址,以找出内存中的最终位置更新。

任何特定types的句柄(hWnd,FILE等)都是特定于域的,并指向特定types的结构以防止内存损坏。

句柄就像数据库中logging的主键值。

编辑1:那么,为什么downvote,主键唯一标识一个数据库logging,并在Windows系统中的句柄唯一标识一个窗口,打开的文件等,这就是我所说的。

把Windows中的窗口想象成一个描述它的结构。 这个结构是Windows的内部部分,你不需要知道它的细节。 相反,Windows为该结构的结构指针提供了一个typedef。 这是你可以在窗户上握住的“手柄”。

对象是表示系统资源(如文件,线程或graphics图像)的数据结构。 应用程序不能直接访问对象所表示的对象数据或系统资源。 相反,应用程序必须获得一个对象句柄,它可以用来检查或修改系统资源。 每个句柄在内部维护表中都有一个条目。 这些条目包含资源的地址和标识资源types的手段。