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 GetModuleHandle and EnumProcessModules 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 FreeLibrary calls has been made.
Many of the flags to the LoadLibraryEx 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 GetModuleHandle or EnumProcessModules, 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 FindResource recognize these "not really a module" modules. For example, if you ask to find a resource in a loaded-as-datafile module, the FindResource function knows that it has to convert RVAs in the PE header into physical file offsets.
And when you pass the HMODULE back to FreeLibrary, 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 LoadLibraryEx 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...