调用IPGlobalProperties.GetIPGlobalProperties()时从NSIS脚本加载DLL时出错

时间:2012-09-16 21:47:10

标签: c# .net nsis

我正在用C#构建一个简单的库。这个DLL有两个功能;一个用于获取计算机的MAC地址,另一个用于获取CPU ID。 DLL的最终目的是从将调用这些函数的NSIS脚本调用。

我在NSIS脚本中这样称呼它:

CLR::Call /NOUNLOAD "CypherLibrary.dll" "CpuMacGetter1.HwInfoRetriever" "GetMacAddress" 0

执行返回以下错误:

  

调用.NET DLL方法时出错。目标抛出异常   调用。

只有当我使用这种物体时才会发生:

  

IPGlobalProperties computerProperties =   IPGlobalProperties.GetIPGlobalProperties();

或者:

  

ManagementClass mc = new ManagementClass(“Win32_NetworkAdapterConfiguration”);

如果我返回当前时间,那么没有问题。

更重要的是,只有当我使用.NET 4 Framework构建而不是使用.NET 3.5构建时才会发生错误(是的,我已经安装了它们)。如果在最终用户的计算机中这还不够,那就相反了。适用于4版但不适用3.5版。

这种行为背后的任何线索?

非常感谢你!!

1 个答案:

答案 0 :(得分:1)

我知道这个问题已经快一年了,但我觉得迟到总比没有好。 :)

找出真正的问题

首先我们应该注意到异常消息太笼统了。更详细的消息将澄清问题所在。要实现这一点,您必须修改NSIS插件本身。

在插件的official page上,您可以download其源代码。使用Visual Studio解压缩并打开它。 (我使用VS2010。)(在可能的升级之后)尝试在调试和发布中构建解决方案。发布配置有一个生成后事件,它将输出DLL复制到计算机上已安装的NSIS Plugins文件夹中。 (如果您有一台x64机器,则必须修改此事件以将(x86)后缀添加到路径的Program Files部分。)因此,每个Release构建都会更新您当前的NSIS CLR插件。

要生成详细异常消息,请在NSIS_CLS_Loader.cpp(第145行)中找到主要捕获块,并将消息更改为记录ex->ToString()而不是ex->Message。重建你的插件。 (当然,您应该在开发阶段使用此过程,因为它会将整个堆栈跟踪转储到您的显示器。)

在您重建NSIS安装程序并尝试安装它之后,您将看到详细的异常消息,您可以找出潜在的问题。我很确定这将是一条消息,告诉无法加载依赖程序集。这是这个NSIS插件的一个已知问题,作者本身在插件的页面上声明了它:

  

另一个问题是,如果想从.NET dll调用.NET dll,您会发现安装程序找不到第二个.NET dll。目前,补救措施是将安装程序包装在另一个安装程序中。此其他安装程序应将.NET dll放在安装程序将从其运行的同一目录中。安装程序运行时,现在可以找到.NET dll。只会分发一个设置文件。

如果您的详细异常消息不一样,您必须根据消息找出解决方案。但是如果你有相同的,也许以下的解决方法可以解决问题。

解决方案

加载程序集时,如果此过程失败,则会引发AssemblyResolve事件。一个非常简单的解决方案是订阅此事件并在事件处理程序方法中加载依赖程序集。

因此,请在第51行AssemblyResolve电话会议前的CallCLR程序中订阅LoadAssembly个事件。

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的plugins文件夹(tipical C:\Program Files (x86)\NSIS\Plugins)并重建安装程序。它在我的机器上(以及我们的Hudson构建服务器上)运行良好,但是当我将安装程序复制到目标PC时,它再次失败并显示错误消息Could not load CLR.DLL

原来问题是我将源代码的解决方案转换为Visual Studio 2012.此转换还将目标C ++平台设置为Visual Studio 2012(v110)。目标PC没有Visual Studio 2012的C ++ Redistributable,这是失败的原因。搜索安装的程序后,我在目标PC上找到了Visual Sudio 2010的C ++可再发行程序包,因此我不得不更改解决方案设置。 (转到Project Property pages\Configuration Properties\General并将Platform Toolset设置为您需要的任何版本,在我的情况下,它是Visual Studio 2010(v100))重建并重新部署插件到NSIS插件文件夹后,我还重建了我的安装程序包和这个修改解决了我的问题。

相关问题