在讨论Windows中的资源时,“句柄”是什么?它们是如何工作的?
它是对资源的一个抽象引用值,通常是内存、打开的文件或管道。
准确地说,在Windows中(以及计算机领域一般),句柄是一个抽象层,将真实的内存地址隐藏起来,让API用户无法直接访问,从而允许系统透明地重新组织物理内存以适应程序。将句柄解析为指针会锁定内存,释放句柄会使指针失效。在这种情况下,可以将其视为指向指针表的索引...您使用索引进行系统API调用,系统可以随意更改指针表中的指针。
或者,当API编写者打算让API用户与返回地址指向的具体内容隔离时,也可以将真正的指针作为句柄给出;在这种情况下,必须考虑到句柄指向的内容可能随时更改(从API版本到版本,甚至从返回句柄的API的每次调用),因此句柄应被视为仅对API有意义的不透明值。
我应该补充说明,在任何现代操作系统中,即使是所谓的“真正的指针”,也仍然是进程虚拟内存空间中的不透明句柄,这使得操作系统可以管理和重新排列内存而不会使进程内的指针失效。
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);
}
GetWidget
返回指向struct Widget
的指针。这有一些后果:
Widget
结构的头文件Widget
结构的内部部分void *
。用户代码不需要访问定义Widget
结构的头文件。void *
称为HANDLE
。也许这会阻止用户代码试图弄清楚void *
指向的确切内容。typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
NewImprovedWidget
结构体。void *
的一个新名称,它是Win32 API中HANDLE
的确切定义(在MSDN上查找)。它提供了一个不透明的壁垒,增加了使用Win32 API的代码在Windows版本之间的可移植性,使其与Win32库的内部表示之间的端口性增加。handle
而不是void *
的主要原因是阻止用户代码尝试确定void *
指向的确切内容。我理解得对吗? - Lion Lai// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
HANDLE
ADT 引用的对象由内核管理。另一方面,您提到的其他句柄类型 (HWND
等) 是用户对象。这些对象不由 Windows 内核管理。 - IInspectable所以从最基本的层面来说,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 是按值传递的(复制并将其传递给函数),因此 printf 将打印原始值 1。
现在,如果我们更新 foo 为:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
有可能printf将打印出更新后的2的值,但是foo也有可能导致某种形式的内存损坏或异常。
原因在于,虽然你现在使用指针将obj传递给函数,但你还分配了2兆字节的内存,这可能会导致操作系统移动内存并更新obj的位置。由于你通过值传递了指针,如果obj被移动,那么操作系统会更新指针但不更新函数中的副本,可能导致问题。
最终对foo的更新:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
这将始终打印更新后的值。
当编译器为指针分配内存时,它将其标记为不可移动的,因此由于分配给函数的大对象引起的任何内存重排都会指向正确的地址,以查找要更新的最终内存位置。
任何特定类型的HANDLE(hWnd,FILE等)都是领域特定的,并指向某种类型的结构以防止内存损坏。
把Windows窗口想象成一个描述它的结构体。这个结构体是Windows的内部组成部分,您不需要知道其详细信息。相反,Windows为该结构体提供了指向结构体的指针的typedef。这就是您可以获取窗口的“句柄”的方式。