← Learn

Introduction to Windows API

Author
Moise Medici
Updated
23 Jan 2026 · Completed

Handles

A HANDLE type is nothing more than a pointer. It is defined as a PVOID meaning a pointer to void (void * in C). How can something be a pointer to void? This is a way to say that the pointer returned can be of any type, depending on the function that returned it. Internally it is mapped to a specific kernel object that the user does not need to care about. Of course, that might make it a bit harder to understand which type of HANDLE needs to be passed to a function; however, the documentation does a decent job in explaining it. A couple of examples of functions returning a HANDLE are:

HANDLE OpenProcess(
[in] DWORD dwDesiredAccess,
[in] BOOL bInheritHandle,
[in] DWORD dwProcessId
);
HANDLE CreateMutexA(
[in, optional] LPSECURITY_ATTRIBUTES lpMutexAttributes,
[in] BOOL bInitialOwner,
[in, optional] LPCSTR lpName
);
HANDLE CreateFileA(
[in] LPCSTR lpFileName,
[in] DWORD dwDesiredAccess,
[in] DWORD dwShareMode,
[in, optional] LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[in] DWORD dwCreationDisposition,
[in] DWORD dwFlagsAndAttributes,
[in, optional] HANDLE hTemplateFile
);

Three completely different meanings for the same object. Handles can be used to track down chains of execution; for example, if a sample opens multiple files and performs operations on them, the execution path might become hard to follow. However, looking at the HANDLE ID in the debugger usually helps.

Using a single unified object (HANDLE) for all the API calls that return a pointer type is a newer approach from Microsoft; previously, each function was returning a different HANDLE type, and some still do for backwards compatibility. For example RegCreateKeyExA 6, which returns a HKEY handle.

LSTATUS RegCreateKeyExA(
[in] HKEY hKey,
[in] LPCSTR lpSubKey,
DWORD Reserved,
[in, optional] LPSTR lpClass,
[in] DWORD dwOptions,
[in] REGSAM samDesired,
[in, optional] const LPSECURITY_ATTRIBUTES lpSecurityAttributes,
[out] PHKEY phkResult,
[out, optional] LPDWORD lpdwDisposition
);

Once a handle is not needed anymore it is usually closed either with CloseHandle 7 or with a specific close function like InternetCloseHandle 8. There are some cases, like the HANDLE populated by EnumProcessModules that must not be closed, as specified in the documentation 9:

Do not call CloseHandle on any of the handles returned by this function. The information comes from a snapshot, so there are no resources to be freed.

So, when encountering phrases like “a handle to a file” or “a handle to a process”, this can be read as “something that uniquely defines and points to the file/process”. It is possible to act on it; for example WriteFile 10 expects as first argument a HANDLE representing the file it can write to:

BOOL WriteFile(
[in] HANDLE hFile,
[in] LPCVOID lpBuffer,
[in] DWORD nNumberOfBytesToWrite,
[out, optional] LPDWORD lpNumberOfBytesWritten,
[in, out, optional] LPOVERLAPPED lpOverlapped
);