Load Library |
|
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.
Dll
Search
Mode 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.
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 "SafeDll
Search
Mode 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;"