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 搜索模式。

 

posted @ 2022-05-20 14:29  Charltsing  阅读(1140)  评论(0编辑  收藏  举报