0

我正在用 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。

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

非常感谢您提前!!

4

1 回答 1

1

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

找到真正的问题

首先我们应该注意异常消息过于笼统。更详细的消息将阐明问题所在。为此,您必须修改 NSIS 插件本身。

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

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

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

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

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

解决方案

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

因此,在第 51 行调用之前订阅过程中的AssemblyResolve事件。CallCLRLoadAssembly

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 插件文件夹后,我还重建了我的安装程序包,这个修改解决了我的问题。

于 2013-09-15T13:19:23.920 回答