Law*_*Dol 158
它是资源的抽象引用值,通常是内存或打开文件或管道.
正确地说,在Windows中(通常在计算中)句柄是一种抽象,它隐藏了API用户的真实内存地址,允许系统透明地重新组织物理内存到程序.将句柄解析为指针会锁定内存,释放句柄会使指针无效.在这种情况下,将其视为指针表的索引...您使用索引进行系统API调用,系统可以随意更改表中的指针.
或者,当API编写者打算将API的用户与返回的地址的具体内容隔离时,可以给出真实指针作为句柄; 在这种情况下,必须考虑句柄指向的内容可能随时发生变化(从API版本到版本,甚至从调用到调用返回句柄的API) - 因此句柄应该被视为简单的不透明值仅对 API 有意义.
我应该补充说,在任何现代操作系统中,即使是所谓的"真实指针"仍然是进程虚拟内存空间的不透明句柄,这使得O/S能够管理和重新安排内存而不会使进程内的指针无效.
Dan*_*ing 92
A HANDLE
是特定于上下文的唯一标识符.根据特定于上下文,我的意思是从一个上下文获得的句柄不一定可以用于任何其他也适用于HANDLE
s的其他aribtrary上下文.
例如,GetModuleHandle
向当前加载的模块返回唯一标识符.返回的句柄可用于接受模块句柄的其他函数.它不能用于需要其他类型句柄的函数.例如,你不能给出一个返回的句柄GetModuleHandle
,HeapDestroy
并期望它做一些明智的事情.
它HANDLE
本身只是一个整体类型.通常,但不一定,它是指向某些底层类型或内存位置的指针.例如,HANDLE
返回的GetModuleHandle
实际上是指向模块的基本虚拟内存地址的指针.但是没有规则说句柄必须是指针.句柄也可以只是一个简单的整数(某些Win32 API可能会将其用作数组的索引).
HANDLE
s是故意不透明的表示,提供内部Win32资源的封装和抽象.这样,Win32 API可能会改变HANDLE背后的基础类型,而不会以任何方式影响用户代码(至少这是个想法).
考虑我刚刚编写的Win32 API的这三种不同的内部实现,并假设它Widget
是一个struct
.
Widget * GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return w;
}
Run Code Online (Sandbox Code Playgroud)
void * GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return reinterpret_cast<void *>(w);
}
Run Code Online (Sandbox Code Playgroud)
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Run Code Online (Sandbox Code Playgroud)
第一个示例公开了有关API的内部细节:它允许用户代码知道GetWidget
返回指向a的指针struct Widget
.这有几个后果:
Widget
结构的头文件Widget
struct的内部部分这两种后果都可能是不可取的.
第二个示例通过仅返回来隐藏用户代码中的内部细节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);
}
Run Code Online (Sandbox Code Playgroud)
请注意,该函数的接口与上面的第三个示例相同.这意味着即使"幕后"实现已更改为使用NewImprovedWidget
结构,用户代码仍可继续使用此新版本的API,而无需任何更改.
这些示例中的句柄实际上只是一个新的,可能更友好的名称void *
,这正是HANDLE
Win32 API中的一个(在MSDN上查找).它在用户代码和Win32库的内部表示之间提供了一个不透明的墙,它增加了Windows版本之间使用Win32 API的代码的可移植性.
Nic*_*dad 34
Win32编程中的HANDLE是一个表示由Windows内核管理的资源的标记.句柄可以是窗口,文件等.
句柄只是一种使用Win32 API识别要使用的粒子资源的方法.
因此,例如,如果要创建一个窗口,并在屏幕上显示它,您可以执行以下操作:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
Run Code Online (Sandbox Code Playgroud)
在上面的例子中,HWND表示"窗口的句柄".
如果您习惯于面向对象的语言,您可以将HANDLE视为一个类的实例,没有方法,其状态只能由其他函数修改.在这种情况下,ShowWindow函数会修改Window HANDLE的状态.
有关更多信息,请参阅句柄和数据类型.
句柄是Windows管理的对象的唯一标识符.它就像一个指针,但不是一个指针,它不是一个可以被用户代码取消引用以获取对某些数据的访问权的地址.而是将句柄传递给一组函数,这些函数可以对句柄识别的对象执行操作.
小智 6
所以在最基本的层面上,任何类型的 HANDLE 都是指向一个指针或
#define HANDLE void **
Run Code Online (Sandbox Code Playgroud)
现在至于为什么要使用它
让我们进行一个设置:
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);
}
Run Code Online (Sandbox Code Playgroud)
因此,因为 obj 是按值传递的(制作一个副本并将其提供给函数)到 foo,所以 printf 将打印原始值 1。
现在,如果我们将 foo 更新为:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
Run Code Online (Sandbox Code Playgroud)
printf 有可能打印更新后的值 2。但 foo 也有可能导致某种形式的内存损坏或异常。
原因是,当您现在使用指针将 obj 传递给函数时,您还分配了 2 Meg 内存,这可能会导致操作系统移动内存以更新 obj 的位置。由于您已按值传递指针,如果 obj 被移动,则操作系统更新指针而不是函数中的副本,并可能导致问题。
foo 的最终更新:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Run Code Online (Sandbox Code Playgroud)
这将始终打印更新的值。
看,当编译器为指针分配内存时,它会将它们标记为不可移动,因此由分配给函数的大对象引起的任何内存重新洗牌都将指向正确的地址,以找出内存中的最终位置更新。
任何特定类型的 HANDLE(hWnd、FILE 等)都是特定于域的,并指向某种类型的结构以防止内存损坏。
将 Windows 中的窗口视为描述它的结构。这个结构体是 Windows 的内部部分,你不需要知道它的细节。相反,Windows 为该结构的结构指针提供了一个 typedef。那是你可以抓住窗户的“把手”。
句柄就像数据库中记录的主键值。
编辑1:好吧,为什么downvote,主键唯一地标识数据库记录,而Windows系统中的句柄唯一地标识窗口,打开的文件等,这就是我要说的。
归档时间: |
|
查看次数: |
120166 次 |
最近记录: |