I'm developing an Unity app which needs to dynamically load native libraries from outside where the app is installed, for some reason I cannot set the absolute path to DllImport before compiling (such as read the library path in a .txt at runtime and load it), and I don't want to use platform specific API such as LoadLibrary() on Windows or dlopen on Linux because it's inconvenient. And I have been struggled for several days.
I know that the search path can be adjusted by SetDllDirectory() on Windows from this post, and it works well when testing on .NET Framework applications.
However, it does not work in Unity which is based on mono 2.0, it just throws DllNotFoundException at runtime, but it works fine when I use absolute path in DllImport or copy the dll into my Unity project (I'm sure that the code is same)
The next way I tried is environment variable, and it does not work on both .NET and Mono, this post explained that CLR never refreshes the environment during process execution.
The third way I tried is to load the native library with platform specific API such as LoadLibrary() on Windows and dlopen() on Linux first, and then Dllimport may find that the library with same name has already been loaded, then it will use the load library to find function pointers, just as this post did. And I get the same result. The top answer of that question says we can write a wrapper class which uses platform specific API to explicitly load library and get functions pointers, instead of an approach focusing Dllimport, but it is not what I want.
If my guess is right, according to mono's document, DllImportAttribute calls LoadLibrary or dlopen internally at runtime to load a library into memory space. So it follows the search rules of specific OS platform, for example windows:
- The directory from which the application loaded.
- The current directory
- The system directory. Use the
GetSystemDirectory()function to get the path of this directory. - The 16-bit system directory.
- The Windows directory. Use the
GetWindowsDirectory()function to get the path of this directory. - The directories that are listed in the PATH environment variable.
and Linux:
- A colon-separated list of directories in the user’s
LD_LIBRARY_PATHenvironment variable. This is a frequently-used way to allow native shared libraries to be found by a CLI program. - The list of libraries cached in
/etc/ld.so.cache./etc/ld.so.cacheis created by editing/etc/ld.so.confand runningldconfig(8). Editing/etc/ld.so.confis the preferred way to search additional directories, as opposed to usingLD_LIBRARY_PATH, as this is more secure (it’s more difficult to get a trojan library into/etc/ld.so.cachethan it is to insert it intoLD_LIBRARY_PATH). /lib, followed by/usr/lib.
By the way, I also tried to set LD_LIBRARY_PATH at runtime, but it does not work because LD_LIBRARY_PATH will be parsed only once when a process started, which is similar to PATH environment variable on Windows.
So my question is:
- Why does the same code performs differently on .NET Framework and Mono? Does Mono just ignore the effect of
SetDllDirectory()on Windows? What doesDllImportAttributeactually do in Mono? - Is there any way to adjust search path for Unity/Mono apps at runtime, just using
DllImportrather than platform specific APIs such asLoadLibrary()anddlopen()?