运行时设置.NET类库与Win类库位置
虽然目前32位操作系统的用户已经很少了,但若是你的程序需要兼容x86系统,可以参考一下本文。
首先,.NET 的类库是可以同时兼容x86和x64系统的(编译选项中设置目标CPU为AnyCPU),只要你的类库不包含任何本地代码(C、C++等)、或者针对特定系统的代码。
如果你的程序调用了第三方的类库,并且只能提供 x86\x64 两个版本的话,就需要在执行时设置类库文件的位置。
指定 .NET 类库位置
.NET 中动态加载类库,只需要设置程序域的 PrivatePath 即可。在程序的初始化函数中(如Main、New、Loading等)调用
AppDomain.AppendPrivatePath
,动态添加 PrivatePath 位置。' 根据指针的长度,来判断当前程序所运行的环境
Dim isX86 = (IntPtr.Size = 4)
Dim isX64 = (IntPtr.Size = 8)
' 根据系统位数,指定类库私有路径的位置,可使用分号进行分隔
Dim privatePath = If(isX86, "x86\bin\;x86\ph\", "x64\bin\;x64\ph\")
AppDomain.CurrentDomain.AppendPrivatePath(privatePath)
var isX86 = IntPtr.Size == 4;
var isX64 = IntPtr.Size == 8;
var privatePath = isX86 ? @"x86\bin\;x86\ph\" : @"x64\bin\;x64\ph\";
AppDomain.CurrentDomain.AppendPrivatePath(privatePath);
我们先根据系统指针的长度来判断程序所处于的运行环境,指针为4表示为x86环境,指针为8表示为x64环境。之后根据系统版本设置不同的 PrivatePath,只需要设置相对目录即可。如果包含多个路径,可以使用分号来分隔。
指定 Windows 类库位置
如果你调用了C、C++的类库文件,则需要在运行时指定不同的版本,因为它们在编译时就已经指定了操作系统的版本。调用不同版本的Win类库,需要使用 Windows API 的
SetDllDirectory
,用于指定额外的DLL文件加载路径。Private Declare Auto Function SetDllDirectory Lib "Kernel32.dll" (ByVal lpPathName As String) As Boolean
Dim isX86 = (IntPtr.Size = 4)
' 指定 Win DLL 文件位置
Dim dllPath As String = IO.Path.Combine(Application.StartupPath, If(isX86, "x86\dll\", "x64\dll\"))
SetDllDirectory(dllPath)
[DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
private static extern bool SetDllDirectory(string lpPathName);
var isX86 = IntPtr.Size == 4;
var dllPath = System.IO.Path.Combine(Application.StartupPath, isX86 ? @"x86\dll\" : @"x64\dll\");
SetDllDirectory(dllPath);
一定要尽早调用 SetDllDirectory
,否则有可能无法正确加载WinDLL,类库的路径需要完整路径。另外,如果你在
SetDllDirectory
之前就已经调用了一个有效的WinAPI,当你重新设置DLL文件夹之后,并不会让程序自动加载新目录下的DLL,因为Windows的DLL加载具有缓存机制,已加载的DLL会被缓存到缓冲区,只有释放过DLL文件,才会重新加载DLL。另外,还可以通过
AddDllDirectory
API实现添加更多DLL目录的功能,但是此API只支持 Windows 8 以上的系统,并且调用前还需要先调用 SetDefaultDllDirectories
,使用起来不是很方便。如果不需要支持多目录的功能,不推荐使用。还是来个示例吧:
Declare Function SetDefaultDllDirectories Lib "Kernel32" (ByVal dirFlags As Integer) As Boolean
' 注意:AddDllDirectory只支持Unicode编码,若未设置正确,则永远返回错误
Declare Unicode Function AddDllDirectory Lib "Kernel32" (ByVal NewDirectory As String) As IntPtr
Declare Function RemoveDllDirectory Lib "Kernel32" (ByVal dircookie As IntPtr) As Boolean
' 需要预先调用 SetDefaultDllDirectories
SetDefaultDllDirectories(&H1000) '使用全部搜索位置
' 添加一个路径,参数需要完整路径,返回值为一个指针,如果为0表示失败
Dim dirCookie = AddDllDirectory(IO.Path.Combine(Application.StartupPath, "newdir\"))
' 删除添加的路径,需要使用一个有效的指针
RemoveDllDirectory(dirCookie)
完整示例
此示例的 .NET DLL 放到x86\bin\
或 x64\bin\
文件夹中,Win DLL 放到 x86\dll\
或 x64\dll\
文件夹中。Private Declare Auto Function SetDllDirectory Lib "Kernel32" (ByVal lpPathName As String) As Boolean
' 根据指针长度,来判断当前程序所运行的环境
Dim isX86 = (IntPtr.Size = 4)
Dim isX64 = (IntPtr.Size = 8)
' 根据系统位数,指定类库私有路径的位置,可使用分号进行分隔
Dim privatePath As String = If(isX86, "x86\bin\", "x64\bin\")
AppDomain.CurrentDomain.AppendPrivatePath(privatePath)
' 指定 Win DLL 文件位置
Dim dllPath As String = IO.Path.Combine(Application.StartupPath, If(isX86, "x86\dll\", "x64\dll\"))
SetDllDirectory(dllPath)
[DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
private static extern bool SetDllDirectory(string lpPathName);
var isX86 = IntPtr.Size == 4;
var isX64 = IntPtr.Size == 8;
var privatePath = isX86 ? @"x86\bin\" : @"x64\bin\";
AppDomain.CurrentDomain.AppendPrivatePath(privatePath);
var dllPath = System.IO.Path.Combine(Application.StartupPath, isX86 ? @"x86\dll\" : @"x64\dll\");
SetDllDirectory(dllPath);
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。