What does it mean when the bottom bit of my HMODULE is set?

ibobev1 pts0 comments

What does it mean when the bottom bit of my HMODULE is set? - The Old New Thing

Skip to main content

Dev Blogs

AI

All .NET posts

.NET MAUI<br>ASP.NET Core<br>Blazor<br>Entity Framework

C++<br>C#<br>F#<br>TypeScript

NuGet<br>Servicing<br>.NET Blog in Chinese

Microsoft for Developers<br>Agent Framework<br>Develop from the cloud<br>Xcode<br>ISE Developer<br>TypeScript<br>PowerShell<br>Python<br>Java<br>Java Blog in Chinese<br>Go<br>Microsoft Edge Dev<br>Microsoft 365 Developer<br>Microsoft Entra Identity Developer<br>Microsoft Entra PowerShell

Visual Studio<br>Visual Studio Code<br>Aspire

All things Azure<br>Azure SDK<br>Azure VM Runtime Team<br>Microsoft Azure<br>Azure Cosmos DB<br>Azure DocumentDB<br>Azure Data Studio<br>Azure SQL<br>DevOps<br>DirectX<br>Microsoft Foundry<br>Power Platform

OData<br>Unified Data Model (IDEAs)

Windows Command Line<br>#ifdef Windows<br>Inside MSIX<br>MIDI and music<br>React Native<br>The Old New Thing<br>Windows Developer

Raymond Chen

The numeric value of an HMODULE is normally the base address of the DLL or EXE it represents. These base addresses are always multiples of 64KB, so the bottom 16 bits are all zero. Yet you may run across one with the bottom bit set. What does that mean?

Normally, when you load a DLL, it gets an entry in the table of loaded modules. This table is consulted by functions like Get­Module­Handle and Enum­Process­Modules to identify all the DLLs that have been loaded. It also is used to keep track of how many times each DLL has been loaded, so that the DLL is removed from memory when the correct number of Free­Library calls has been made.

Many of the flags to the Load­Library­Ex function alter how the system locates the DLL to load, but some of them alter how the DLL is itself loaded into memory. The interesting one here is the LOAD_LIBRARY_AS_DATAFILE flag.

If you ask that a DLL be loaded as a data file, and there isn’t already a copy of the DLL loaded normally, then the loader will search the file system for the DLL in the manner described by the other flags, and then it will just map the DLL into memory without doing any of the usual stuff like applying fixups, and then returns you an HMODULE that represents the location where the DLL was mapped into memory, but it also sets the bottom bit as a note to itself to say "This wasn’t loaded the normal way."

If the loader decides to map the DLL into memory directly, then the DLL does not get an entry in the list of loaded modules. While the module was loaded in a strict sense of the term, it was not loaded as a functional module. The code is not ready to execute: Its dependencies were not resolved. Its initialization was not run. It’s just a bunch of bytes mapped into memory. If you call Get­Module­Handle or Enum­Process­Modules, the module won’t show up because those functions use the list of "properly" loaded modules, and your datafile DLL wasn’t put on that list.

Functions like Find­Resource recognize these "not really a module" modules. For example, if you ask to find a resource in a loaded-as-datafile module, the Find­Resource function knows that it has to convert RVAs in the PE header into physical file offsets.

And when you pass the HMODULE back to Free­Library, it sees that the bottom bit is set and knows, "Oh, this was never entered into the module list, so I don’t have to remove it from the module list either."

This special behavior of the bottom bit is locked into the ABI thanks to this macros provided in the Load­Library­Ex documentation:

#define LDR_IS_DATAFILE(handle) (((ULONG_PTR)(handle)) & (ULONG_PTR)1)

I don’t know if this use of the bottom bit was intended to be an implementation detail, or whether documenting it was an intentional decision, but what’s done is done, and it’s documented, so it’s too late to change it now.

Bonus chatter : You can see in the documentation another macro that reveals that the second-from-bottom bit is also used as a special signal:

#define LDR_IS_IMAGEMAPPING(handle) (((ULONG_PTR)(handle)) & (ULONG_PTR)2)

Category<br>Old New Thing

Topics<br>Code

Share

HMODULE is set?" data-bi-id="post_page_body_share post" data-post-id=112447 data-bi-name="Share on X" aria-label="Share on X" target="_blank" rel="noreferrer noopener" class="d-flex share-post">

Author

Raymond Chen

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

1 comment

Join the discussion.

Leave a commentCancel reply<br>Sign in

Code of Conduct

Sort by :

Newest

Newest<br>Popular<br>Oldest

I remember in 16-bit Windows the HMODULE for a DLL was similar to its DS selector, but not quite. Presumably those bottom bits were also being used as flags.

Read next

June 12, 2026

How can I schedule work...

loaded bottom module azure hmodule microsoft

Related Articles