无法加载文件或程序集。

4

我有一个简单的可执行文件,想在其中使用 DLL 文件。我已将其添加为引用,并创建了 app.config 文件,因为该 DLL 最终不会在可执行目录中。

如果我从 IDE 中执行程序,一切正常,因为 DLL 文件被复制到本地,但是一旦我将可执行文件移走,它就会崩溃。我的 Fusion 日志暗示它找不到指定的文件。

我的 Main() 方法:

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);

Assembly assembly = null;
String dllLocation = @"C:\BMS_ACTD\bin\DX\Tools\BmsReplayAnalysis.dll";
IToolsInterface myProgram = null; //from ToolsInterface.dll
try
{
    assembly = Assembly.LoadFrom(dllLocation);
}
catch
{
}

foreach (Type myType in assembly.GetTypes())
{
    if (myType.GetInterface(typeof(IToolsInterface).FullName) != null)
    {
        myProgram = (IToolsInterface)assembly.CreateInstance(myType.Namespace + "." + myType.Name, true);
        myProgram.RunTool();
        break;
    }
}

这是我的配置文件:

<runtime>
    <dependentAssembly>
        <assemblyIdentity name="ToolsInterface" publicKeyToken="null" culture="neutral" />
        <codeBase version="1.0.0.0" href="file://C:/BMS_ACTD/bin/DX/Globals/ToolsInterface.dll"/>
    </dependentAssembly>
</runtime>

我不想担心强名称的问题。我只需要一个版本的dll,这就是我关心的。

以下是来自fusionlog的摘录:

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework\v4.0.30319\clr.dll
Running under executable  C:\Users\greenj\Desktop\BmsReplayLauncher.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: User = BMS-JMGREEN\greenj
LOG: DisplayName = ToolsInterface, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///C:/Users/greenj/Desktop/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = BmsReplayLauncher.exe
Calling assembly : BmsReplayLauncher, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
===
LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\Users\greenj\Desktop\BmsReplayLauncher.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/Users/greenj/Desktop/ToolsInterface.DLL.
LOG: Attempting download of new URL file:///C:/Users/greenj/Desktop/ToolsInterface/ToolsInterface.DLL.
LOG: Attempting download of new URL file:///C:/Users/greenj/Desktop/ToolsInterface.EXE.
LOG: Attempting download of new URL file:///C:/Users/greenj/Desktop/ToolsInterface/ToolsInterface.EXE.
LOG: All probing URLs attempted and failed.

1
你的.config文件是否已经重命名以匹配你的程序?例如:program.exe => program.exe.config - Julien Lebosquain
@Julien - 根据您的回复,我需要重新表述原始问题。我的配置错误(使用了vshost版本)。话虽如此,我的问题将在一秒钟内更新。 - Jason
2个回答

2

如果您尝试从不在应用程序EXE位置的文件夹或子文件夹中加载文件,那么使用.net就会变得棘手。我建议尝试两件事情。首先,使用<probing>属性指定要查找自定义DLL的位置,参见answer,而不是您正在使用的方式。

其次,如果这样做不起作用,请尝试使用AppDomain.AssemblyResolve事件而不是使用配置文件。然后,您可以从任何地方加载您的程序集。

编辑:Jay Walker指出,.NET 4及更高版本已更改AssemblyResolve,以便如果DLL不在自定义文件夹中,则返回null以保留默认行为。

以下是一些AssemblyResolve的示例代码:

Assembly currentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    if (args.Name == THE_DLL_I_WANT_TO_CUSTOM_LOAD)
    {
        // Load from our custom path
        string finalPath = null;
        try
        {
            finalPath = MyCustomPath + args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
            return Assembly.LoadFrom(finalPath);
        }
        catch ()
        {
        }
    }

    // Not our custom, use the default loading
    return null;
}

这是正确的,这就是为什么我包含了AppDomain.AssemblyResolve方法(我不确定你的应用程序位于哪里)。 - Ed Bayiates
你是在静态构造函数或静态初始化器中引用 DLL 吗? - Ed Bayiates
它正在静态 Main 中被引用。 - Jason
Main是静态的,没错。但我指的是类中包含Main的字段,引用了DLL中你未能成功加载的内容。该DLL通常不应在该类的静态构造函数或Main()被调用之前加载。 - Ed Bayiates
1
@Jay,现在是正确的;这是在4.0框架中进行的更改,在我回答时并不正确。感谢您指出这个变化,它使AssemblyResolve的编写变得更加容易。 - Ed Bayiates
显示剩余8条评论

0

引用 MSDN 对 <codebase> 的说法:

如果程序集有强名称,则 codebase 设置可以位于本地局域网或互联网上的任何位置。如果程序集是私有程序集,则 codebase 设置必须是相对于应用程序目录的路径。

你的程序集没有强名称,因此无法通过指定完整路径来使用此指令。签署你的程序集(在 Visual Studio 中只需三个点击即可)或按照 AresAvatar 的建议使用 AssemblyResolve


我的希望/目标是使用AssemblyResolve。实际上,我更希望不必始终跟随可执行文件的配置文件。如上面的讨论所提到的,即使是一个包含接口的简单dll也无法加载。程序在崩溃之前未能解决程序集依赖项。我很快就会运行depends.exe(一旦我的笔记本电脑启动)看看是否有什么奇怪的问题。正常的系统依赖项是否算作需要的东西?(例如System.Windows.Forms) - Jason
depends.exe显示有2个dll找不到。gpsvc.dll和ieshims.dll。有一些帖子抱怨这个问题。我不知道这是否是真正的问题,但人们并没有真正的解决方案。另外,如果我将我的程序更改为将工具接口dll复制到本地,并在IDE中运行它,我就没有问题了。所以这是怎么回事? - Jason

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接