How Windows DLL Name Resolution Works - Search Order and SxS

· · Windows, DLL, Loader, Security, Windows Development

Whenever native DLLs on Windows come up, confusion like the following arises with remarkable frequency.

  • When you write LoadLibrary("foo.dll"), where does it actually look?
  • You put the DLL in the same folder as the executable, so why is a different DLL being loaded?
  • Does System32 take precedence, or does the application folder?
  • At which stage do manifests, API sets, and Known DLLs take effect?
  • What changes when you use SetDllDirectory or AddDllDirectory?
  • What makes DLL planting attacks and DLL hijacking likely to happen?

Memorizing the search order as a one-liner is not enough for real work. In reality, the Windows loader evaluates several special rules before it ever starts walking the file system in order.

In this article, we organize DLL name resolution on Windows for practical use, covering the differences between unpackaged and packaged apps, Known DLLs, the loaded-module list, API sets, side-by-side manifests, and the effects of the LoadLibraryEx family of APIs. The content is based on public information on Microsoft Learn as of March 2026.123456789

1. The Conclusion First

Here are the practical conclusions up front.

  • Windows DLL name resolution is not “file system search first.” Elements like DLL redirection, API sets, SxS manifests, the loaded-module list, and Known DLLs come before the search order proper.1
  • In the standard configuration for an unpackaged app with safe DLL search mode enabled, the application folder ranks high, but the special rules above are evaluated before it.1
  • Even if you load a DLL by full path, its dependent DLLs are not automatically pinned to the same full path. Dependent DLLs are searched by module name only, so they can be resolved from somewhere else.1
  • Known DLLs is a mechanism by which the OS binds certain well-known DLLs to the system-side copies; it is not something a normal app-side deployment overrides.1
  • An API set is not “the actual DLL name itself” but a virtual alias that hides the implementation DLL. If you see a name like api-ms-win-... and reason about it the way you would about a normal DLL search, you will likely misunderstand.3
  • SetDllDirectory does more than change the search order — it has the behavior of effectively disabling safe DLL search mode, so using it casually can backfire from a security standpoint.1
  • In practice, the safe approach is to combine full-path loading, SetDefaultDllDirectories, AddDllDirectory, and the LOAD_LIBRARY_SEARCH_* flags of LoadLibraryEx to narrow the search scope explicitly.4562

In short, the practical view is that Windows DLL name resolution is determined not just by “which folder comes in what order,” but by “which pre-stage rules resolve the name first” and “how the APIs have altered the search space.”

According to the DLL search order documentation on Microsoft Learn, when a DLL is loaded, the following elements are treated as part of the search order first.1

  1. DLL redirection
  2. API sets
  3. SxS manifest redirection
  4. The loaded-module list
  5. Known DLLs

Only after that does the loader proceed to the file system search across the app folder, System32, the Windows folder, PATH, and so on.1

If you miss this, it can feel “wrong” that something gets decided before the application folder — but as a description of the Windows loader, that is in fact the main path.

DLL name needs resolutionDLL redirectionAPI setSxS manifest redirectionloaded-module listKnown DLLsSearch order on the file systemThe DLL actually loaded is determined

3. The Standard Search Order for Unpackaged Apps

For an ordinary desktop app loading a DLL without a full path, Microsoft Learn describes the standard search order for unpackaged apps. In the default state with safe DLL search mode enabled, the flow is essentially this.1

  1. DLL redirection
  2. API sets
  3. SxS manifest redirection
  4. The loaded-module list
  5. Known DLLs
  6. On Windows 11 21H2 and later, the package dependency graph
  7. The folder the application was loaded from
  8. System32
  9. The 16-bit system folder
  10. The Windows folder
  11. The current folder
  12. PATH

Three points matter most in practice.

  • The current folder is quite far down by default. Safe DLL search mode makes it hard to move the current folder forward.1
  • However, being far down does not mean safe. As long as a directory the attacker can control remains in the search scope, room for DLL preloading remains.2
  • On Windows 11 21H2 and later, the package dependency graph appears in the unpackaged app search description as well. This is an easy delta to miss if you only remember the older description.1

4. Packaged Apps and Unpackaged Apps Are Not the Same

Microsoft Learn defines a separate search order for packaged apps. In packaged apps, the package dependency graph takes effect at an earlier stage, and the search model itself is somewhat different.1

Miss this difference, and after moving to MSIX or adopting the Windows App SDK you get confusion like this:

  • A DLL found when running unpackaged during development is not found in the production package
  • Dependencies via the package manifest mix with old-style PATH dependence and the reproduction conditions change
  • Explaining “the Windows DLL search order is such-and-such” with a single table, dropping the behavioral differences of packaged apps

In articles and design reviews, it is safest to first separate “is this about a packaged app or an unpackaged app?”1

5. What Known DLLs and the Loaded-Module List Are Doing

The parts of DLL resolution most likely to defy intuition are the loaded-module list and Known DLLs.

5.1 The loaded-module list

Microsoft Learn explains that the system can check whether a DLL with the same module name is already loaded in memory.1

In other words, before the file system search, there is a check of

  • whether that DLL name is already loaded, and
  • consequently, whether there is any need to go searching at all.

So if, during an investigation, you drop the fact that “in this process, a same-named DLL from another folder was already loaded first,” you will misread the reproduction conditions.

5.2 Known DLLs

Known DLLs is the list of DLLs Windows considers well known for that version, and it can be inspected at HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\KnownDLLs. For a DLL on the list, the system uses its copy of that known DLL.1

What matters here is that Known DLLs is not the kind of thing a regular app can “win” by placing a same-named DLL in its application folder. Understanding it as a simple first-come race against System32 misreads the behavior.

6. An API Set Is a Contract Name, Not an “Actual DLL Name”

When you see a name like api-ms-win-core-..., it is tempting to wonder “where do I find that DLL file?” But Microsoft Learn explains that an API set is a virtual alias for a physical DLL — a mechanism that separates the contract from the implementation.3

In other words, reasoning like

  • API set name = the physical DLL file name as is
  • API set resolution = the same file search as a normal DLL

is inaccurate.

With the API set model in mind, it becomes easier to explain that

  • things stay consistent even when the implementing DLL name differs across Windows versions and device types, and
  • the caller does not need to know, in fixed terms, “which host DLL implements this.”3

7. Manifests and Side-by-Side (SxS) Are an Alternative Answer to the DLL Versioning Problem

DLL redirection and SxS manifests are described not as minor search-order tricks but as mechanisms for avoiding DLL versioning conflicts.789

Microsoft Learn’s framing is:

  • A manifest is XML describing a side-by-side assembly or an isolated application
  • A side-by-side assembly is the unit of naming, binding, versioning, and deployment
  • The loader decides which version to bind to based on the dependencies recorded in the manifest

89

So in practice you need to think about these three separately:

  • Simply placing a private DLL in the app folder
  • Using DLL redirection such as .local
  • Using side-by-side binding via manifests

All of them are similar in that they “affect DLL resolution,” but their design intent is not the same.

8. What Changes with LoadLibraryEx, SetDllDirectory, and AddDllDirectory

8.1 SetDllDirectory

SetDllDirectory changes the search order, but Microsoft Learn explicitly states that it effectively disables safe DLL search mode.1

That is, even if you only intended to “add one app-specific folder,” the search space changes as a result, including how the current folder is treated.

Furthermore, calling SetDllDirectory in a parent process can affect the standard search order in child processes as well.1

For this reason, rather than using SetDllDirectory carelessly as a habit, it is safer in practice to lean on

  • SetDefaultDllDirectories
  • AddDllDirectory
  • the LOAD_LIBRARY_SEARCH_* flags of LoadLibraryEx

456

8.2 AddDllDirectory

Paths added with AddDllDirectory are used in combination with LOAD_LIBRARY_SEARCH_USER_DIRS. On Microsoft Learn, the search order among multiple added directories is unspecified.15

So a design that

  • adds multiple directories, and
  • strictly expects a particular search order among them

is best avoided.

8.3 SetDefaultDllDirectories

SetDefaultDllDirectories is described as an API for removing the more vulnerability-prone directories from the standard DLL search path and restricting the search scope.4

Three properties are particularly worth knowing.

  • It takes effect per process
  • Once called, it persists for the lifetime of the process
  • A standard search path once set cannot simply be restored to the original default

From a security standpoint, this API lends itself to a design of “shifting to a safer search space right after startup.”4

8.4 LoadLibraryEx

LoadLibraryEx lets you change the search behavior with LOAD_WITH_ALTERED_SEARCH_PATH and the LOAD_LIBRARY_SEARCH_* flags.61

Practically, it is an API well suited to requirements like

  • including the folder of the DLL being loaded in the search scope, dependencies included, or
  • restricting the search to the application folder, System32, and explicitly added user directories only.

9. A Full Path Does Not Pin the Dependent DLLs

This is the point on this topic that matters a great deal in practice yet is easily overlooked. Microsoft Learn explains that even when the first DLL is loaded by full path, its dependent DLLs are searched by module name only.14

That is, just because

  • you explicitly loaded C:\\MyApp\\plugins\\foo.dll

it does not follow that

  • bar.dll, which foo.dll depends on, will always be taken from the same folder.

With this misconception in place, you get the rather nasty failure mode of

  • it works in the development environment,
  • a different bar.dll gets resolved at the customer site, and
  • dependent-DLL conflicts become environment-dependent to reproduce.

10. Avoiding DLL Preloading / Hijacking

Microsoft Learn’s DLL security documentation explains that the combination of dynamic loading without a full path and attacker-controllable directories in the search scope leads to DLL preloading attacks and binary planting attacks.2

In practice, it helps to converge on this baseline.

  • Reduce bare-name loads like LoadLibrary("foo.dll")
  • Use full-path loading where needed
  • Narrow the process-default search path with SetDefaultDllDirectories
  • Add only explicitly approved directories with AddDllDirectory
  • Make the search scope explicit with flags like LOAD_LIBRARY_SEARCH_SYSTEM32, LOAD_LIBRARY_SEARCH_APPLICATION_DIR, and LOAD_LIBRARY_SEARCH_USER_DIRS
  • Avoid the current folder and careless reliance on PATH

A situation where a process running with administrator privileges has an ambiguous search path is especially dangerous. Microsoft Learn also notes that when a malicious DLL gets loaded, it executes with the privileges of that process.2

11. A Practical Decision Checklist

When reviewing DLL loading design on Windows, checking at least the following reduces incidents.

  1. Is the app a packaged app or an unpackaged app?
  2. Which DLLs come from static linking, and which are dynamically loaded?
  3. Full-path loading, or module name only?
  4. Is SetDllDirectory in use anywhere?
  5. Is the configuration such that SetDefaultDllDirectories and LOAD_LIBRARY_SEARCH_* can be used?
  6. Are multiple AddDllDirectory calls in use with an implicit expectation of ordering?
  7. Are dependencies managed via manifests / SxS / private DLLs / redirection — and which one?
  8. Are there weak security assumptions around the current folder or PATH?
  9. Could dependent DLLs be resolved from a different location in a different environment?

Checking these nine points separately lets you eliminate problems like “DLL not found,” “the wrong DLL was loaded,” “it only fails to start in production,” and “blocked at the vulnerability review” at a much earlier stage.

12. Summary

DLL name resolution on Windows is not merely a “folder search order.” In reality, it is determined by the overlap of DLL redirection, API sets, SxS manifests, the loaded-module list, Known DLLs, and the search space as modified by API calls.134

What matters most in practice comes down to these five things.

  • Do not memorize the search order as a single table
  • Separate packaged from unpackaged
  • Understand that even with full-path loading, dependent DLLs can be treated differently
  • Do not use SetDllDirectory casually
  • To err on the safe side, use SetDefaultDllDirectories and the search flags of LoadLibraryEx

DLL name resolution is a place where startup failures, environment differences, and security problems all tend to surface at once. That is exactly why it is worth understanding not just “in what order Windows searches,” but “what Windows treats as the premises of name resolution in the first place.”

References

  1. Microsoft Learn: Dynamic-link library search order
  2. Microsoft Learn: Dynamic-Link Library Security
  3. Microsoft Learn: Windows API sets
  4. Microsoft Learn: SetDefaultDllDirectories function
  5. Microsoft Learn: AddDllDirectory function
  6. Microsoft Learn: LoadLibraryEx function
  7. Microsoft Learn: Dynamic-link library redirection
  8. Microsoft Learn: Manifests
  9. Microsoft Learn: About Side-by-Side Assemblies
  1. Microsoft Learn, Dynamic-link library search order, accessed March 24, 2026  2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

  2. Microsoft Learn, Dynamic-Link Library Security, accessed March 24, 2026  2 3 4 5

  3. Microsoft Learn, Windows API sets, accessed March 24, 2026  2 3 4 5

  4. Microsoft Learn, SetDefaultDllDirectories function, accessed March 24, 2026  2 3 4 5 6 7

  5. Microsoft Learn, AddDllDirectory function, accessed March 24, 2026  2 3 4

  6. Microsoft Learn, LoadLibraryEx function, accessed March 24, 2026  2 3 4

  7. Microsoft Learn, Dynamic-link library redirection, accessed March 24, 2026  2

  8. Microsoft Learn, Manifests, accessed March 24, 2026  2 3

  9. Microsoft Learn, About Side-by-Side Assemblies, accessed March 24, 2026  2 3

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

These topic pages place the article in a broader service and decision context.

This article connects naturally to the following service pages.

Windows App Development

DLL placement and loading directly affect whether a Windows app starts, how it is distributed, and how reproducible failures are, so this is well worth addressing in the context of Windows application development.

Author Profile

Profile page for the article author.

Go Komura

Representative of KomuraSoft LLC

Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.

Back to the Blog