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).
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:
foo.dll" is loaded and you later try to load your own "foo.dll" using an absolute path, both will indeed load.
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.
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.
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]
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?
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).
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.
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:
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."
.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.
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
LoadLibraryEx options like LOAD_WITH_ALTERED_SEARCH_PATH, [GetModuleFileName], and [GetFullPathName] to force absolute or more specific paths.
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') 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.)
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.
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 )
cyg" (e.g. cyglua-5.1.dll), similar to the "lib" prefix on Linux shared libraries. [13] [7]
.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.
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).
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.
foo.luad" or "foo-lua.dll" rather than "foo.dll", to avoid conflict with non-Lua DLLs. LUA_CPATH can be updated accordingly.
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.
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.)
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) {
printf("load/unload my DLL\n");
return TRUE;
}
LoadLibrary and 5.2" - Scuri suggest LOAD_WITH_ALTERED_SEARCH_PATH
LUA_LLE_FLAGS in 5.2work2
luaconf.h option for LOAD_WITH_ALTERED_SEARCH_PATH
LOAD_WITH_ALTERED_SEARCH_PATH
LoadLibrary in windows" - suggest LOAD_WITH_ALTERED_SEARCH_PATH
LUA_CDIR"\\?.dll;" prior to "?.dll;"