On Windows, binary modules are dynamically loaded into Lua as [DLLs]. Windows almost always loads DLLs via the [LoadLibrary] or [LoadLibraryEx] Win32 API functions. These functions have Unicode (LoadLibraryW/LoadLibraryEx) and ANSI (LoadLibraryA/LoadLibraryExA) variants for the file name parameter. In Lua 5.1, loadlib.c calls LoadLibraryA. In Lua 5.2-beta, loadlib.c calls LoadLibraryExA, and its third parameter is taken from the LUA_LLE_FLAGS define (which defaults to 0).

### Dll Path Rules

See [Dynamic-Link Library Search Order] for the rules governing the order of search paths that Windows looks for the DLL in. These rules are complicated and tend to change between versions of Windows. (Also, Windows Embedded (CE) [1]/Mobile [2] have special rules described later below.) In particular...

First, "If the string specifies a fully qualified path, the function searches only that path for the module." [3]. No further rules below are applied. Also, if you use [DLL redirection] or manifests (see manifest/WinSxS links at bottom), those techniques are used to find the DLL before the other techniques below.

Otherwise, the following two rules are applied next:

• (1) "If a DLL with the same module name is already loaded in memory, the system checks only for redirection and a manifest before resolving to the loaded DLL, no matter which directory it is in. The system does not search for the DLL." [4] This rule seems to only govern DLLs that are not loaded with an absolute path. If a system DLL "foo.dll" is loaded and you later try to load your own "foo.dll" using an absolute path, both will indeed load.

• (2) "If the DLL is on the list of known DLLs for the version of Windows on which the application is running, the system uses its copy of the known DLL (and the known DLL's dependent DLLs, if any). The system does not search for the DLL." [4] In Windows 7, there are a few dozen of these known DLLs, including user32, kernel32, gdi32, msvcrt, and others. This rule seems to only govern DLLs that are not loaded with an absolute path. If a system DLL "kernel32.dll" is loaded and you later try to load your own "kernel32.dll" using an absolute path, both will indeed load.

If a match is still not found, a search order (standard or alternate) is done as described below.

• In the "Standard Search Order for Desktop Applications" with "SafeDllSearchMode enabled" [4], which is normally the default behavior, Windows will search in the "The directory from which the application loaded" (i.e. where the EXE is located), then various Windows system directories, then the current directory, and finally the directories in the PATH environment variable. The DLL's in the Windows system directories have the potential to interfere, and on Windows 7 there are close to 1500 of them.

• Concerning LoadLibraryEx, "If lpFileName specifies a relative path, the entire relative path is appended to every token in the DLL search path. To load a module from a relative path without searching any other path, use [GetFullPathName] to get a nonrelative path and call LoadLibraryEx with the nonrelative path. If the module is being loaded as a datafile and the relative path starts with .\ or ..\, the relative path is treated as an absolute path." [3] (Is this also true of LoadLibrary?) Since Lua modules are loaded as images not as datafiles (LOAD_LIBRARY_AS_DATAFILE), a package.cpath of ".\?.dll" will not be treated by LoadLibraryEx as an absolute path (as described below), and you will need to use something like GetFullPathName if you want to form an absolute path. [9][10]

• Note: GetFullPathName has a worrying statement "Multithreaded applications and shared library code should not use the GetFullPathName function and should avoid using relative path names [...] possible data corruption". [5] Can the same corruption occur when LoadLibrary/LoadLibraryEx is passed a relative path?

• Concerning LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH, "If this value is used and lpFileName specifies an absolute path, the system uses the alternate file search strategy discussed in the Remarks section to find associated executable modules that the specified module causes to be loaded." [3] In other words, if your binary Lua module DLL in turn loads other DLLs via LoadLibrary without absolute paths, the LOAD_WITH_ALTERED_SEARCH_PATH will begin searching relative to the directory containing your DLL (which might be different from the directory containing lua.exe).

• Concerning LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH, "If this value is used and lpFileName specifies a relative path, the behavior is undefined." and "LOAD_WITH_ALTERED_SEARCH_PATH does not work with relative paths." [3] This suggests that Lua 5.2.0-beta LUA_LLE_FLAGS=LOAD_WITH_ALTERED_SEARCH_PATH will not work properly if the Lua binary module DLL is located via ./?.dll rather than via an absolute path.

• Although it's undefined behavior, in practice LoadLibraryEx + LOAD_WITH_ALTERED_SEARCH_PATH with a "relative" path of ".\\winmm.dll" seems to load preferentially from the current directory rather than from the system directory in Windows 7.

Windows Embedded (CE) [1] / Mobile [2] have these special behaviors:

• "[...] all path information is ignored when determining if the DLL is already loaded. This means that if a DLL with the same name is currently loaded by any process on the system the search order is ignored, even if the path is absolute. [...] Two different modules cannot have the same file name, even if the extensions are different. These effectively have the same module name. For example, if LoadLibrary is made on Sample.cpl, the OS does not load Sample.cpl, but instead loads Sample.dll again. A similar limitation exists for modules with the same name but residing in different directories. For example, if LoadLibrary is called on \\Windows\Sample.dll, and then LoadLibrary is called on \\MyDir\Sample.dll, \\Windows\Sample.dll will simply be reloaded."

• "Unless the full path to the module is specified, Windows Embedded Compact searches the following path for the module: The absolute path specified by the lpLibFileName parameter; The .exe launch directory; The Windows directory; ROM DLL files; An OEM-specified search path."

• LoadLibrary/LoadLibraryEx does not support manifests, LOAD_WITH_ALTERED_SEARCH_PATH, and other such more advanced features.

### Placement of DLLs

A typical use case involves an application process (e.g. lua.exe) that loads binary Lua modules (e.g. foo.dll). lua.exe and foo.dll may in turn also depend directly or indirectly on other (non-Lua) library/system DLLs (e.g. bar.dll). The simplest file organization is often to put all these files in the same directory, which "largely" works under the above search rules. (There is, however, possible confusion if the binary Lua module DLL and library/system DLL names happen to collide, as is discussed later below.) Many people (e.g. LuaForWindows?) feel that the DLLs clutter up the directory containing the EXE, and they prefer to place the DLLs in a separate subdirectory that they don't typically need to look at. You can place both types of DLLs in the same subdirectory or you can further split these into two subdirectories. (Linux distributions of many scripting languages do the latter to avoid the scripting language libraries littering the system libraries.) In any case, putting these files in different directories can complicate the situation because you then need to have a mechanism for the files to find each other. Some solutions include

• Use .bat file wrappers that set PATH, LUA_CPATH, and LUA_PATH environment variables. This is ugly, and the .bat files don't entirely behave identically to EXEs either (.exe wrappers are cleaner but more work). LuaDist abandoned this.
• Use some combination of Win32 functions like [SetDllDirectory], LoadLibraryEx options like LOAD_WITH_ALTERED_SEARCH_PATH, [GetModuleFileName], and [GetFullPathName] to force absolute or more specific paths.

### Caveats

Conflicts with system DLL names using relative paths: As an example, if you want to name your Lua module "winmm", which you compile to a DLL named winmm.dll, this will happen to have the same name as winmm.dll in the Windows system directory. If your place your winmm.dll in the current directory (which we'll assume differs from the EXE's directory), then the following:

package.loadlib('winmm.dll', 'luaopen_winmm')


will both fail under "Standard Search Order for Desktop Applications" with "SafeDllSearchMode enabled". If the path provided to LoadLibraryEx is relative, as both are considered here, Windows first looks in the EXE directory (where lua.exe is located) and then in the Windows system directories before looking in the current directory. (Moreover, if your application calls [SetDllDirectory], then it will never look in the current directory.)

Note: [DLL Preloading Attacks] recommends, for security reasons, that 'If you never have a need to load a DLL from the current directory, just call SetDllDirectory with an argument of "".'

If, however, you place your winmm.dll in the same directory as lua.exe, then the above two statements usually will both work. However, if your process (or even a DLL indirectly loaded into your process by the operating system, outside of your control) tries to load the system "winmm.dll" (which it typically will try to load without an absolute path), it will instead receive your winmm.dll, which won't work and could crash. (Maybe we can avoid that problem with manifests?)

We could alternately pass an absolute path:

package.loadlib([[c:\path\to\winmm.dll]], 'luaopen_winmm')


Now, Windows will only try to load the module from the specified location, which it will begin to do. However, if your process later (rather than before) tries to also load the system "winmm.dll" (typically without an absolute path), it will fail because it sees a DLL with the same name already loaded and uses that, which is incorrect.

That above problems largely do not seem to affect the case if your module were named "foo.winmm", located in .\foo\winmm.dll, and loaded with a relative path. When .\foo\winmm.dll is passed to LoadLibraryEx, Windows appends .\foo\winmm.dll to all its search paths, and of course none of these likely exists. However, we still have problems if your process later tries to load the system "winmm.dll" without a path.

These problems could be compounded if scripting languages other than Lua also try to use a similar module naming scheme.

The above problems could be avoided by ensuring Lua module DLL names never conflict with system DLL names. You might do this by giving Lua module DLL names a globally unique suffix and/or extension--e.g. rather than ".dll" maybe ".luad" (analogous to Python .pyd and following similar conventions to .luac), ".luadll", ".lua.dll" (might not be a good idea since "Many things break when you have an extra dot in DLL names" -- MikePall in [11]), or "-lua.dll". LuaList:2011-09/msg00398.html . You will need to adjust package.cpath appropriately to handle this. On Linux, system shared libraries are prefixed by lib [12].

The above solutions, however, are insufficient on Windows CE/Mobile/Embedded, which have the additional restriction that "all path [and extension] information is ignored when determining if the DLL is already loaded". This has caused LuaSocket mime/core.dll and socket/core.dll to conflict [6]. It would also prevent the unique extension solution above, with the exception of things like -lua.dll (where the unique extension is actually part of the file name). One overall solution here, at least for embedded versions of Windows, is to have DLL names like socket_core-lua.dll, which will never conflict with mime_core-lua.dll nor any possible core.dll. A change may be made to findfile() in loadlib.c so that dots in the module name are replaced with "_" rather than "/" [6]. Lua 5.2 provides a LUA_CSUBSEP variable for this.

• 'For those who do use non-path filenames in their LUA_CPATH e.g. "abc.dll" there is a significant performance overhead (>2 orders of magnitude) in allowing windows to scan the serach path.' (David Burgess in LuaList:2006-12/msg00049.html )
• 'even a seemingly innocent file open dialog may change the current directory ...' (LuaList:2006-12/msg00064.html)

### Comparisons

• In Cygwin, all Cygwin DLL's (as opposed to native ones) are given the prefix "cyg" (e.g. cyglua-5.1.dll), similar to the "lib" prefix on Linux shared libraries. [13][7]
• In Windows Python (2.7 and 3.2), binary Python module DLL's are given the extension .pyd rather than .dll See [Is a *.pyd file the same as a DLL?]. "The output file should be called spam.pyd (in Release mode) or spam_d.pyd (in Debug mode). The extension .pyd was chosen to avoid confusion with a system library spam.dll to which your module could be a Python interface." [8]. Python module DLLs (e.g. _sqlite3.pyd) and Python C library DLLs (e.g. sqlite3.dll) are both stored in a "DLLs" directory (which only has about two dozen files), Python source libraries (e.g. sqlite3\dump.py) are stored in a "Lib" directory, and python.exe (and pythonw.exe) are in the parent directory. However, Cygwin Python 2.6 doesn't quite follow that (e.g. _sqlite3.dll and cygsqlite3-0.dll). Python [dynload_win.c] uses LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH, and the path is forced to be absolute with GetFullPathName.
• Windows Strawberry Perl uses names like TIFF.dll (binary Perl module) and libtiff-3_.dll (C library), and these are stored in separate directories. Perl [win32.c] uses LoadLibraryEx with LOAD_WITH_ALTERED_SEARCH_PATH, and the path appears forced to be absolute (PerlDir_mapA).
• Windows Ruby uses binary module DLL names like zlib.so and system DLL names like zlib1.dll. The latter are stored in their own directories, whereas the former as stored in the same directory as the ruby.exe and rubyw.exe. Ruby dl.h/dln.c seems to just use LoadLibrary (not LoadLibraryEx).

The common theme is that there is an effort to keep distinct DLL naming conventions for scripting language binary C modules and system libraries.

### Possible Conclusions

• Lua binary modules should have a unique DLL naming convention, like "foo.luad" or "foo-lua.dll" rather than "foo.dll", to avoid conflict with non-Lua DLLs. LUA_CPATH can be updated accordingly.
• Something maybe should be done about Lua 5.2.0-beta LoadLibraryEx triggering undefined behavior with LUA_LLE_FLAGS=LOAD_WITH_ALTERED_SEARCH_PATH and relative paths like package.cpath=".\\?.dll". A recommended approach is to make paths absolute. This may be done via GetFullPathName, though it possibly leads to corruption in multithreaded and shared library situations (why shared libraries???). [5] Alternately, ".\\?.dll" could could be removed from package.cpath, but the user might still try to add it.
• Embedded versions of Windows have unique requirements, and loadlib.c findfile() may need a small patch.

(Footnote: Perhaps LUA_PATH should match .luac files too. On the other hand, renaming .luac files to .lua works without complicating the path.)

### DLL Test code

The following test DLL in conjunction with package.loadlib may be useful in understanding this behavior.

//cl test.c /link /DLL /out:test.dll winmm.lib

#include <stdio.h>
#include <windows.h>

__declspec(dllexport) int __cdecl luaopen_winmm(void * x) {
printf("called test, %d\n", (int)timeGetTime());
return 0;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) {