How DLL Name Resolution Works on Windows: A Practical Look at Search Order, Known DLLs, API Sets, and SxS
When you work with native DLLs on Windows, confusion like this comes up all the time:
- You write
LoadLibrary("foo.dll")— where does Windows actually look? - You drop a DLL right next to the EXE, and yet a different DLL gets loaded
- Does
System32win, or does the application folder win? - At what point do manifests, API sets, and Known DLLs come into play?
- Even when you load a DLL by full path, are its dependencies pinned the same way?
This article organizes Windows DLL name resolution from a practical, day-to-day angle.
1. The bottom line first
- Windows DLL name resolution does not start with a filesystem search. DLL redirection, API sets, SxS manifests, the loaded-module list, and Known DLLs all sit in front of the search order.
- For a typical desktop app, the application folder is high up in the order, but the special rules above are evaluated before it.
- Loading a DLL by full path does not automatically pin that DLL’s dependencies to the same full path.
- Known DLLs is a mechanism by which the OS binds certain well-known DLLs to the system copy. It is not something an application can override by dropping a DLL in its own folder.
- API sets (
api-ms-win-...) are not physical DLL names — they are virtual aliases that hide the real implementation DLL. - For safety, combine full paths,
SetDefaultDllDirectories, and the search flags onLoadLibraryExto explicitly narrow the search scope.
2. Pre-filesystem rules
The DLL load search order has the following pre-filesystem rules ahead of any directory probing:
- DLL redirection
- API sets
- SxS manifest redirection
- The loaded-module list (is a DLL with the same name already in memory?)
- Known DLLs
Only after these does the loader move on to the application folder, System32, the Windows folder, PATH, and so on.
3. Standard search order for an unpackaged app
For a regular desktop app (with safe DLL search mode enabled), the search order is:
- DLL redirection
- API sets
- SxS manifest redirection
- The loaded-module list
- Known DLLs
- The directory the application was loaded from
System32- The 16-bit system folder
- The Windows folder
- The current folder
PATH
Key points:
- The current folder sits fairly far down by default. Safe DLL search mode makes it harder to push it forward.
- That does not mean it is safe just because it is at the bottom. As long as a directory an attacker can control is anywhere in the search list, there is still room for a DLL planting attack.
4. Packaged apps and unpackaged apps are different
Packaged apps (MSIX and similar) define a different search order. Missing this distinction is a common source of confusion: a DLL that the loader finds during your unpackaged dev runs may be unfindable inside the production package.
In articles and design reviews, the safest first move is to ask “are we talking about a packaged app or an unpackaged app?” before anything else.
5. Known DLLs and the loaded-module list
The loaded-module list: the system checks whether a DLL with the same module name is already loaded into the process. If you miss the fact that “a same-named DLL from a different folder was loaded into this process first,” you will misread your repro conditions.
Known DLLs: a list of DLLs that Windows treats as well known for that version. You can inspect it under HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs. This is not a list a normal application can override by dropping a same-named DLL into its own folder.
6. API sets are contracts, not physical DLL names
Names like api-ms-win-core-... are not the actual DLL filenames on disk. They are virtual aliases that hide the real implementation DLL. The API set scheme keeps things consistent even when the implementing DLL differs across Windows versions or device categories.
7. The role of manifests and SxS
DLL redirection and SxS manifests are not just minor tweaks to the search order — they exist to avoid DLL versioning conflicts. In practice you should keep these scenarios mentally separate:
- Just placing a private DLL in the app folder
- Using DLL redirection such as
.local - Using side-by-side binding via a manifest
8. Things to watch in the main APIs
SetDllDirectory
This does more than change the search order — it effectively disables safe DLL search mode as a side effect. Reaching for it casually can backfire from a security standpoint.
AddDllDirectory
Directories you add are used together with LOAD_LIBRARY_SEARCH_USER_DIRS. When you add several, the search order between them is unspecified.
SetDefaultDllDirectories
This API removes hijack-prone directories from the default DLL search path and limits the search scope. It applies process-wide, and once you set it, you cannot revert to the original default search path.
LoadLibraryEx
With flags like LOAD_WITH_ALTERED_SEARCH_PATH and LOAD_LIBRARY_SEARCH_*, you can control search behavior — for example, including the folder of the loading DLL when resolving its dependencies.
9. A full path does not pin the dependencies
This matters a lot in practice and is easy to miss. Even when you load the first DLL by full path, its dependencies are still resolved by module name alone.
In other words, explicitly loading C:\MyApp\plugins\foo.dll does not guarantee that bar.dll, which foo.dll depends on, will also be picked up from that same folder.
10. Avoiding DLL hijacking
- Cut down on bare-name loads like
LoadLibrary("foo.dll") - Use full paths where you can
- Tighten the process default search path with
SetDefaultDllDirectories - Add only the directories you have explicitly approved using
AddDllDirectory - Make the search scope explicit with
LOAD_LIBRARY_SEARCH_SYSTEM32,LOAD_LIBRARY_SEARCH_APPLICATION_DIR, andLOAD_LIBRARY_SEARCH_USER_DIRS - Avoid relying on the current folder or a careless
PATH
A process running with administrator privileges combined with a vague search path is especially dangerous. A malicious DLL loaded under those conditions runs with that process’s privileges.
11. A practical checklist
- Is the app packaged or unpackaged?
- Which DLLs come in via static linking, and which are loaded dynamically?
- Is the load done by full path, or by module name alone?
- Are you using
SetDllDirectory? - Is the design suitable for
SetDefaultDllDirectoriesandLOAD_LIBRARY_SEARCH_*? - Are you using
AddDllDirectorymultiple times while implicitly relying on an order that is not guaranteed? - Are dependencies managed via manifests, SxS, private DLLs, or redirection — which one?
- Are there security-weak assumptions about the current folder or
PATH? - Could a dependent DLL be resolved from a different location in another environment?
Summary
DLL name resolution on Windows is not just “the order in which folders are searched.” In reality, DLL redirection, API sets, SxS manifests, the loaded-module list, Known DLLs, and the search space modified by various API calls all stack on top of each other.
What matters most in practice:
- Do not memorize the search order as a single flat table
- Separate packaged from unpackaged
- Understand that even with a full path, dependent DLLs are handled separately
- Do not reach for
SetDllDirectorycasually - When you want to err on the safe side, use
SetDefaultDllDirectoriestogether with theLoadLibraryExsearch flags
Related articles
- A Minimum Security Checklist for Windows Apps
- How Far Can You Really Go With a Single-Binary Windows App?
- When Does a Windows App Actually Need Administrator Privileges?
- What Is Reg-Free COM?
References
- Microsoft Learn: Dynamic-link library search order
- Microsoft Learn: Dynamic-Link Library Security
- Microsoft Learn: Windows API sets
- Microsoft Learn: SetDefaultDllDirectories function
- Microsoft Learn: AddDllDirectory function
- Microsoft Learn: LoadLibraryEx function
- Microsoft Learn: Dynamic-link library redirection
- Microsoft Learn: Manifests
- Microsoft Learn: About Side-by-Side Assemblies
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
When Windows Admin Privileges Are Actually Required: UAC, Protected Areas, and How to Tell from the Design
A practical guide to when Windows admin rights are truly required, organized around UAC, protected locations, services, drivers, and per-...
What ClickOnce Actually Is: How It Works, How Updates Flow, and Where It Fits in Practice
A practical look at ClickOnce — how the manifests, auto-updates, per-version cache, and signing fit together, why it shines for internal ...
How to Use Windows Sandbox to Speed Up Windows App Validation - Admin Rights, Clean Environments, and Reproducing Missing-Permission or Low-Resource Cases
A practical guide to validating Windows apps with Windows Sandbox. Covers first-install checks in a clean environment, isolating admin-ri...
How to Isolate Only the Administrator-Required Work in a Windows App
A practical design for splitting the administrator-only work in a Windows app into a separate EXE: an administrator broker pattern coveri...
Best Practices for Avoiding Plaintext Secrets in Windows App Config Files
On Windows desktop apps, leaving passwords or tokens in plaintext config is risky. This post lays out where DPAPI (ProtectedData) actuall...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.