我知道这个问题已经快一年了,但我认为迟到总比没有好。 :)
找出真正的问题
首先我们应该注意异常消息过于笼统。更详细的消息将阐明问题所在。为此,您必须修改 NSIS 插件本身。
在插件的official page 上,您可以download 其源代码。使用 Visual Studio 提取并打开它。 (我使用的是 VS2010。)(可能升级后)尝试在调试和发布中构建解决方案。发布配置有一个构建后事件,它将输出 DLL 复制到您计算机上已安装的 NSIS 插件文件夹中。 (如果您有 x64 机器,则必须修改此事件以将 (x86) 后缀添加到路径的 Program Files 部分。)因此,每个 Release 版本都会更新您当前的 NSIS CLR 插件。
要生成详细的异常消息,请在 NSIS_CLS_Loader.cpp(第 145 行)中找到主要的 catch 块,并将消息更改为记录 ex->ToString() 而不是 ex->Message。重建你的插件。 (当然,您应该在开发阶段使用此过程,因为它将整个堆栈跟踪转储到您的显示器。)
在您还重建您的 NSIS 安装程序并尝试安装它之后,您将看到详细的异常消息,并且您可以找出潜在的问题是什么。我很确定这将是一条消息,告诉无法加载依赖程序集。这是这个 NSIS 插件的一个已知问题,作者自己在插件页面上声明了它:
另一个问题是,如果想从您的 .NET dll 中调用一个 .NET dll,您会发现安装程序找不到第二个 .NET dll。目前,补救措施是将您的安装程序包装在另一个安装程序中。此其他安装程序应将 .NET dll 放置在与安装程序将运行的目录相同的目录中。当安装程序运行时,现在可以找到 .NET dll。只会分发一个安装文件。
如果详细的异常信息对您来说不一样,您必须根据该信息找出解决方案。但如果你也遇到同样的问题,也许下面的解决方法可以解决问题。
解决方案
当您加载程序集并且此过程失败时,将引发 AssemblyResolve 事件。一个非常简单的解决方案是订阅这个事件,并在事件处理方法中加载依赖的程序集。
因此,在第 51 行的 LoadAssembly 调用之前,在 CallCLR 过程中订阅 AssemblyResolve 事件。
AppDomain::CurrentDomain->AssemblyResolve += gcnew ResolveEventHandler(&MyResolveEventHandler);
创建一个事件处理程序和一个字典对象,以防止多次加载同一个程序集。并发字典用于处理并发问题。
ref class Container {
public:
static ConcurrentDictionary<String^, Assembly^>^ LoadedAssemblies = gcnew ConcurrentDictionary<String^, Assembly^>();
};
static Assembly^ MyResolveEventHandler(Object^ sender, ResolveEventArgs^ args)
{
AssemblyName^ assemblyName = gcnew AssemblyName(args->Name);
String^ assemblyFileName = ".\\" + assemblyName->Name + ".dll";
if (!Container::LoadedAssemblies->ContainsKey(assemblyFileName))
{
Assembly^ loadedAssembly = LoadAssembly(assemblyFileName);
return Container::LoadedAssemblies->GetOrAdd(assemblyFileName, loadedAssembly);
}
return nullptr;
}
别忘了清理CallCLR末尾分配的资源。
当然,必须修改您的 NSIS 安装程序以将依赖的程序集文件复制到 $PLUGINSDIR 目录。
当我第一次遇到这个问题时,发现插件无法解析log4net.dll。
特别感谢我的同事 Attila,他为我提供了此解决方案的指南。
编辑 (03.10.2013.)
部署
构建修改后的插件后,您必须将其部署到 NSIS 的插件文件夹(通常为 C:\Program Files (x86)\NSIS\Plugins)并重新构建您的安装程序。它在我的机器(以及我们的 Hudson 构建服务器)上运行良好,但是当我将安装程序复制到目标 PC 时,它再次失败并出现错误消息 Could not load CLR.DLL。
原来问题在于我将源代码的解决方案转换为 Visual Studio 2012。此转换还将目标 C++ 平台设置为 Visual Studio 2012 (v110)。目标 PC 没有 C++ Redistributable for Visual Studio 2012,这是失败的原因。在搜索已安装的程序后,我在目标 PC 上找到了 Visual Sudio 2010 的 C++ 可再发行包,因此我不得不更改解决方案设置。 (转到Project Property pages\Configuration Properties\General 并将Platform Toolset 设置为您需要的任何版本,在我的情况下它是Visual Studio 2010 (v100))在重建插件并将其重新部署到NSIS 插件文件夹后,我还重建了我的安装程序包,这个修改解决了我的问题问题。