什么是 Windows 句柄?

发布于 2024-07-21 22:10:25 字数 44 浏览 6 评论 0原文

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

What is a "Handle" when discussing resources in Windows? How do they work?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

以可爱出名 2024-07-28 22:10:25

它是资源的抽象引用值,通常是内存或打开的文件或管道。

正确地说,在 Windows 中(通常在计算中),句柄是一种抽象,它向 API 用户隐藏真实的内存地址,从而允许系统对程序透明地重新组织物理内存。 将句柄解析为指针会锁定内存,释放句柄会使指针无效。 在这种情况下,可以将其视为指针表的索引...您可以使用系统 API 调用的索引,系统可以随意更改表中的指针。

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

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

It's an abstract reference value to a resource, often memory or an open file, or a pipe.

Properly, in Windows, (and generally in computing) a handle is an abstraction which hides a real memory address from the API user, allowing the system to reorganize physical memory transparently to the program. Resolving a handle into a pointer locks the memory, and releasing the handle invalidates the pointer. In this case think of it as an index into a table of pointers... you use the index for the system API calls, and the system can change the pointer in the table at will.

Alternatively a real pointer may be given as the handle when the API writer intends that the user of the API be insulated from the specifics of what the address returned points to; in this case it must be considered that what the handle points to may change at any time (from API version to version or even from call to call of the API that returns the handle) - the handle should therefore be treated as simply an opaque value meaningful only to the API.

I should add that in any modern operating system, even the so-called "real pointers" are still opaque handles into the virtual memory space of the process, which enables the O/S to manage and rearrange memory without invalidating the pointers within the process.

十年九夏 2024-07-28 22:10:25

HANDLE 是上下文特定的唯一标识符。 通过特定于上下文,我的意思是从一个上下文获得的句柄不一定可以在也适用于 HANDLE 的任何其他任意上下文中使用。

例如,GetModuleHandle 返回当前加载模块的唯一标识符。 返回的句柄可以在接受模块句柄的其他函数中使用。 它不能提供给需要其他类型句柄的函数。 例如,您无法将从 GetModuleHandle 返回的句柄提供给 HeapDestroy 并期望它执行一些明智的操作。

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

HANDLE 是故意不透明的表示,提供对内部 Win32 资源的封装和抽象。 这样,Win32 API 可能会更改 HANDLE 背后的基础类型,而不会以任何方式影响用户代码(至少是这样的想法)。

考虑我刚刚编写的 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 * 称为 HANDLE 。 也许这会阻碍用户代码尝试弄清楚 void * 到底指向什么。

为什么要经历这个麻烦呢? 考虑同一 API 的新版本的第四个示例:

typedef void * HANDLE;

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

    w = findImprovedWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

请注意,该函数的接口与上面的第三个示例相同。 这意味着用户代码可以继续使用这个新版本的 API,无需任何更改,即使“幕后”实现已更改为使用 NewImprovedWidget 结构。

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

A HANDLE is a context-specific unique identifier. By context-specific, I mean that a handle obtained from one context cannot necessarily be used in any other aribtrary context that also works on HANDLEs.

For example, GetModuleHandle returns a unique identifier to a currently loaded module. The returned handle can be used in other functions that accept module handles. It cannot be given to functions that require other types of handles. For example, you couldn't give a handle returned from GetModuleHandle to HeapDestroy and expect it to do something sensible.

The HANDLE itself is just an integral type. Usually, but not necessarily, it is a pointer to some underlying type or memory location. For example, the HANDLE returned by GetModuleHandle is actually a pointer to the base virtual memory address of the module. But there is no rule stating that handles must be pointers. A handle could also just be a simple integer (which could possibly be used by some Win32 API as an index into an array).

HANDLEs are intentionally opaque representations that provide encapsulation and abstraction from internal Win32 resources. This way, the Win32 APIs could potentially change the underlying type behind a HANDLE, without it impacting user code in any way (at least that's the idea).

Consider these three different internal implementations of a Win32 API that I just made up, and assume that Widget is a 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);
}

The first example exposes the internal details about the API: it allows the user code to know that GetWidget returns a pointer to a struct Widget. This has a couple of consequences:

  • the user code must have access to the header file that defines the Widget struct
  • the user code could potentially modify internal parts of the returned Widget struct

Both of these consequences may be undesirable.

The second example hides this internal detail from the user code, by returning just void *. The user code doesn't need access to the header that defines the Widget struct.

The third example is exactly the same as the second, but we just call the void * a HANDLE instead. Perhaps this discourages user code from trying to figure out exactly what the void * points to.

Why go through this trouble? Consider this fourth example of a newer version of this same API:

typedef void * HANDLE;

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

    w = findImprovedWidget(name);

    return reinterpret_cast<HANDLE>(w);
}

Notice that the function's interface is identical to the third example above. This means that user code can continue to use this new version of the API, without any changes, even though the "behind the scenes" implementation has changed to use the NewImprovedWidget struct instead.

The handles in these example are really just a new, presumably friendlier, name for void *, which is exactly what a HANDLE is in the Win32 API (look it up at MSDN). It provides an opaque wall between the user code and the Win32 library's internal representations that increases portability, between versions of Windows, of code that uses the Win32 API.

舂唻埖巳落 2024-07-28 22:10:25

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

句柄只是一种识别要使用 Win32 API 处理的特定资源的方法。

例如,如果您想创建一个窗口,并将其显示在屏幕上,您可以执行以下操作:

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

// Show the window.
ShowWindow(hwnd, SW_SHOW);

在上面的示例中,HWND 表示“窗口句柄”。

如果您习惯了面向对象的语言,您可以将 HANDLE 视为没有方法的类的实例,其状态只能由其他函数修改。 在这种情况下,ShowWindow函数修改窗口句柄的状态。

有关详细信息,请参阅句柄和数据类型

A HANDLE in Win32 programming is a token that represents a resource that is managed by the Windows kernel. A handle can be to a window, a file, etc.

Handles are simply a way of identifying a particulate resource that you want to work with using the Win32 APIs.

So for instance, if you want to create a Window, and show it on the screen you could do the following:

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

// Show the window.
ShowWindow(hwnd, SW_SHOW);

In the above example HWND means "a handle to a window".

If you are used to an object oriented language you can think of a HANDLE as an instance of a class with no methods who's state is only modifiable by other functions. In this case the ShowWindow function modifies the state of the Window HANDLE.

See Handles and Data Types for more information.

菊凝晚露 2024-07-28 22:10:25

句柄是 Windows 管理的对象的唯一标识符。 它像一个指针,但不是指针,因为它不是一个可以被用户代码取消引用以访问某些数据的地址。 相反,句柄将传递给一组函数,这些函数可以对句柄标识的对象执行操作。

A handle is a unique identifier for an object managed by Windows. It's like a pointer, but not a pointer in the sence that it's not an address that could be dereferenced by user code to gain access to some data. Instead a handle is to be passed to a set of functions that can perform actions on the object the handle identifies.

为人所爱 2024-07-28 22:10:25

将 Windows 中的窗口视为描述它的结构。 该结构是 Windows 的内部部分,您不需要了解它的详细信息。 相反,Windows 为指向该结构的结构的指针提供了 typedef。 这是你可以抓住窗户的“把手”。

Think of the window in Windows as being a struct that describes it. This struct is an internal part of Windows and you don't need to know the details of it. Instead, Windows provides a typedef for pointer to struct for that struct. That's the "handle" by which you can get hold on the window.,

没有你我更好 2024-07-28 22:10:25

因此,在最基本的层面上,任何类型的 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。

现在,如果我们将 foo 更新为:

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

printf 有机会打印更新后的值 2。但 foo 也有可能会导致某种形式的内存占用腐败或异常。

原因是,当您现在使用指针将 obj 传递给函数时,您还分配了 2 MB 内存,这可能会导致操作系统移动内存以更新 obj 的位置。 由于您已经按值传递了指针,因此如果 obj 被移动,那么操作系统会更新指​​针,但不会更新函数中的副本,这可能会导致问题。

对 foo 的最终更新:

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

这将始终打印更新的值。

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

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

So at the most basic level a HANDLE of any sort is a pointer to a pointer or

#define HANDLE void **

Now as to why you would want to use it

Lets take a setup:

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);
}

So because obj was passed by value (make a copy and give that to the function) to foo, the printf will print the original value of 1.

Now if we update foo to:

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

There is a chance that the printf will print the updated value of 2. But there is also the possibility that foo will cause some form of memory corruption or exception.

The reason is this while you are now using a pointer to pass obj to the function you are also allocating 2 Megs of memory, this could cause the OS to move the memory around updating the location of obj. Since you have passed the pointer by value, if obj gets moved then the OS updates the pointer but not the copy in the function and potentially causing problems.

A final update to foo of:

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

This will always print the updated value.

See, when the compiler allocates memory for pointers it marks them as immovable, so any re-shuffling of memory caused by the large object being allocated the value passed to the function will point to the correct address to find out the final location in memory to update.

Any particular types of HANDLEs (hWnd, FILE, etc) are domain specific and point to a certain type of structure to protect against memory corruption.

你是我的挚爱i 2024-07-28 22:10:25

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

编辑1:好吧,为什么投反对票,主键唯一标识数据库记录,Windows系统中的句柄唯一标识窗口,打开的文件等,这就是我所说的。

A handle is like a primary key value of a record in a database.

edit 1: well, why the downvote, a primary key uniquely identifies a database record, and a handle in the Windows system uniquely identifies a window, an opened file, etc, That's what I'm saying.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文