32位或64位DLL从.net托管代码加载
我有一个非托管DLL(Scintilla代码编辑器的scilexer.dll,由CodePlex使用,由Scintilla.Net使用)通过Scintilla.Net组件从托管应用程序加载。在32位和64位环境下,Windows托管应用程序运行没有问题,但我需要创建使用64或32 scilexer.dll的不同安装。32位或64位DLL从.net托管代码加载
有没有办法将32位和64位格式的这两个DLL分发,以便.Net框架的DLL加载程序根据某些.config选项或某些“路径名称加载32或64位格式的非托管DLL魔术“的东西?
P/Invoke使用LoadLibrary加载DLL,如果已经有一个加载了给定名称的库,LoadLibrary将返回它。所以,如果你可以给这两个版本的DLL使用相同的名字,但是把它们放在不同的目录中,你可以在第一次从scilexer调用函数之前做一次这样的事情。DLL,而无需复制你的外部声明:
string platform = IntPtr.Size == 4 ? "x86" : "x64";
string dll = installDir + @"\lib-" + platform + @"\scilexer.dll";
if (LoadLibrary(dll) == IntPtr.Zero)
throw new IOException("Unable to load " + dll + ".");
你可以把dll放在system32中。 syswow64中的32位和真正的system32中的64位。对于32位应用程序,当访问system32时,它们被重定向到Syswow64。
您可以在注册表中创建一个条目。软件密钥有一个名为Wow6432Node的子项,32位应用程序将其视为软件密钥。
我想出的最好的是以下几点:
- 分发我的应用程序名为64或32
- 在主启动代码的两个DLL包括以下内容:
File.Delete(Application.StartupPath + @"\scilexer.dll"); { // Check for 64 bit and copy the proper scilexer dll if (IntPtr.Size == 4) { File.Copy(Application.StartupPath + @"\scilexer32.dll", Application.StartupPath + @"\scilexer.dll"); } else { File.Copy(Application.StartupPath + @"\scilexer64.dll", Application.StartupPath + @"\scilexer.dll"); } }
不幸的是,我不知道任何关于这个特定的DLL。但是,当您自己进行P/Invoke时,您可以应付一点重复,可以为每个平台创建一个代理。
例如,假设您有以下界面,应该由这两个一个32位或64位DLL中实现:
public interface ICodec {
int Decode(IntPtr input, IntPtr output, long inputLength);
}
您创建代理:
public class CodecX86 : ICodec {
private const string dllFileName = @"Codec.x86.dll";
[DllImport(dllFileName)]
static extern int decode(IntPtr input, IntPtr output, long inputLength);
public int Decode(IntPtr input, IntPtr output, long inputLength) {
return decode(input, output, inputLength);
}
}
和
public class CodecX64 : ICodec {
private const string dllFileName = @"Codec.x64.dll";
[DllImport(dllFileName)]
static extern int decode(IntPtr input, IntPtr output, long inputLength);
public int Decode(IntPtr input, IntPtr output, long inputLength) {
return decode(input, output, inputLength);
}
}
最后让一个工厂为你挑选合适的工厂:
public class CodecFactory {
ICodec instance = null;
public ICodec GetCodec() {
if (instance == null) {
if (IntPtr.Size == 4) {
instance = new CodecX86();
} else if (IntPtr.Size == 8) {
instance = new CodecX64();
} else {
throw new NotSupportedException("Unknown platform");
}
}
return instance;
}
}
由于第一次调用DLL时懒惰地加载了DLL,尽管每个平台都只能加载原生版本,但实际上它仍然有效。有关更详细的解释,请参阅this article。
小心!我已经对我的应用程序做了完全相同的事情,而且我仍然不时地在32位系统上遇到问题。由于无法控制JIT的工作方式,因此无法确定应用程序不会尝试在32位系统上加载64位DLL,反之亦然。 – 2009-03-17 03:31:12
我一直在生产中使用这个为期一年半,迄今为止它对我来说效果很好。 – SteinNorheim 2010-10-08 19:10:27
我会说,大部分时间不打扰 - 除非你希望你的应用程序使用接近2GB的RAM只是让你的项目目标仅适用于x86,它只会在32位版本的scilexer.dll中运行。 – 2009-11-15 11:07:09