C#,如何分别在x64和x86平台上加载同一个名称的原生dll
以sqlite-net为例:
1、定义API
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] static extern bool SetDllDirectory(string lpPathName);
2、在static SQLite3()中,加入下列代码
static SQLite3() { //https://stackoverflow.com/questions/4385806/determine-the-loaded-path-for-dlls/4385997 SetDllDirectory(null); // Reset. SetDllDirectory(""); // Plug "binary planting" security hole. ` string dllpath = IntPtr.Size == 8 ? "x64" : "x86"; dllpath = $"{AppDomain.CurrentDomain.BaseDirectory}{dllpath}"; //BaseDirectory 有分隔符结尾 bool ret = SetDllDirectory(dllpath); if (!ret) throw new System.ComponentModel.Win32Exception(); }
3、在exe文件的启动目录下新建x64和x86两个目录,分别将64位和32位的sqlite3.dll放入对应的目录里。
4、记住,要在退出程序之前重置搜索顺序。请使用 null 和 空串 分别调用一次 SetDllDirectory
说明:
在windows中,假如安全DLL搜索模式启用,搜索顺序如下:
1. 应用程序所在的路径
2. Windows SYSTEM目录。通过调用GetSystemDirectory函数可以获取这个目录的路径。
3. 16位系统的目录。并没有函数可以获取这个目录的路径,但是它会被查找。
4. Windows目录。通过调用GetWindowsDirectory函数可以获取这个目录的路径。
5. 当前目录
6. PATH环境变量指定的路径。请注意,这并不包括每个应用程序的应用程序路径注册表项中指定。在应用程序路径注册表项的键值并不作为DLL的搜索路径。
BOOL SetDllDirectoryA([in, optional] LPCSTR lpPathName);
参数 [in, optional] lpPathName
要添加到搜索路径的目录。如果此参数为空字符串 (""),则调用将从默认 DLL 搜索顺序中删除当前目录。
如果此参数为 NULL,则该函数将恢复默认搜索顺序。
返回值
如果函数成功,则返回值非零。
如果函数失败,则返回值为零。要获取扩展错误信息,请调用 GetLastError。
说明
该 SetDllDirectory会功能会影响到所有后续调用 的LoadLibrary和 LoadLibraryEx功能。当指定目录在搜索路径中时,它还有效地禁用安全 DLL 搜索模式。
对于未运行打包或受保护进程的Win32 进程,调用此函数还会影响从调用该函数的进程开始的子进程的 DLL 搜索顺序。
调用 SetDllDirectory 后,标准的 DLL 搜索路径为:
1. 应用程序加载的目录。
2. lpPathName参数指定的目录。
3. 系统目录。使用 GetSystemDirectory函数获取该目录的路径。该目录的名称是 System32。
4. 16 位系统目录。没有获取这个目录路径的函数,但是会搜索。该目录的名称是 System。
5. Windows 目录。使用 GetWindowsDirectory函数获取该目录的路径。
6. PATH 环境变量中列出的目录。
每次调用SetDllDirectory函数时,它都会替换上一次SetDllDirectory调用中指定的目录。要指定多个目录,请使用AddDllDirectory函数并使用LOAD_LIBRARY_SEARCH_USER_DIRS调用LoadLibraryEx。
要恢复到LoadLibrary和 LoadLibraryEx使用的标准搜索路径 ,请使用 NULL调用 SetDllDirectory。这还会根据SafeDllSearchMode注册表值恢复安全的 DLL 搜索模式。