重新定位我的应用程序的依赖项会引发 System.IO.FileNotFoundException

Relocating dependencies for my application throws System.IO.FileNotFoundException

我正在尝试清理我的 Release 文件夹,以便对依赖文件进行某种组织。在这种情况下,我的目标是将压缩模块所需的所有依赖项包含在一个文件夹中。对于我的新 class 库的第一部分,我只是尝试支持 7zip 文件。我使用 SevenZipSharp.Interop NuGet which depends on the SevenZipSharp NuGet.

默认情况下,上述 NuGets 构建结果在我的 Release/Debug 文件夹中,如下所示...

Release
|- x64
| `- 7z.dll
|- x86
| `- 7z.dll
|- SevenZipSharp.dll
`- compressionmodule.dll

随着我为 class 库添加更多支持,我想确保所有内容都有自己的文件夹等。因此,我想按以下方式组织它...

Release
|- compressionmodule
| `- 7zip
|   |- x64
|   | `- 7z.dll
|   |- x86
|   | `- 7z.dll
|   `- SevenZipSharp.dll
`- compressionmodule.dll

我通过 运行 宁一些 POST BUILD 命令来做到这一点,一旦项目构建,我就得到了上面我想要的结构(后者)。但是,在调试时,在我编译并出现 POST BUILD 命令后,应用程序尝试 运行 并且我收到 System.IO.FileNotFoundException 错误,告诉我找不到 SevenZipSharp.dll .我知道这会在我的脑海中发生,但希望有一种方法可以让应用程序知道依赖项的新位置。

是否可以重新定位文件,然后以某种方式让我的应用程序知道它们位于不同的位置?

我最终使用了 NuGet package Costura.Fody (repository found here)。它在 class 模块(或程序)中包装依赖项的方式简直就是魔术。使用 Costura.Fody 时,它会查找任何依赖项并自动将它们压缩到 class 库 dll 中,从而形成一个 dll。然后它将使对该资源的所有引用都指向您的 dll 中的嵌入式资源。您需要做的就是添加两个 NuGet 包,它会在您构建项目时完成所有工作。真的很光滑!

Important Note: Fody's (a dependency of Costura.Fody needed to run Costura.Fody) current version is only compatible with Visual Studio 2019 (using MSBuild 16). In my case I have Visual Studio 2017 (which uses MSBuild 15). I had to use an old version of Costura.Fody that still used the old MSBuild. That also required me to downgrade Fody as well. So I ended up using the latest versions that would work with MSBuild 15. Fody version 4.2.1 and Costura.Fody version 3.3.3 works great with Visual Studio 2017.

它甚至支持在需要时提取资源。例如,我希望在 SevenZipSharp 库中使用它。它需要使用 7zip.dll 文件的路径。因此,我在与您的解决方案位于同一目录中的 FodyWeavers.xml 文件中使用了以下内容。

<?xml version="1.0" encoding="utf-8"?>
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
  <Costura CreateTemporaryAssemblies='true' IncludeDebugSymbols='false'>
    <Unmanaged32Assemblies>
      7z
    </Unmanaged32Assemblies>
    <Unmanaged64Assemblies>
      7z
    </Unmanaged64Assemblies>
  </Costura>
</Weavers>

您还需要在项目中包含 7zip.dll 以便它编译 dll,因为它们不像资源那样使用(就像 SevenZipSharp 库那样)。您可以通过在项目的根目录中添加以下文件夹以及适当的体系结构文件来完成此操作。然后将它们添加为 Build Action: Embedded Resource.

这会在需要时在 运行 时提取 7zip.dll 依赖项,这样我就可以在使用 SevenZipSharp 库时引用路径。在这一点上,我还没有找到如何获得对它使用的目录(C83815D61CE49B2E8D23145A1B95A956 可能是目录本身中所有文件的校验和?)的引用,但在我的情况下,这是将它提取到...

%tmp%\Costura\C83815D61CE49B2E8D23145A1B95A956\

更新:我找到了一种解决方法,可以使用程序集信息获取该校验和的路径,例如目录。我创建了以下函数来尝试设置 7Zip 库路径。它可能对某些人有帮助,所以我把它放在这里。

private bool SetupSevenZipLibrary()
{
    string costuraExtractionPath = null;

    try
    {
        //This will use the embeded resource to try and set a base path to the extraction
        //location created by by Costura.Fody
        string sevenZipAssembly = typeof(SevenZip.ArchiveFileInfo).Assembly.Location;
        if (File.Exists(sevenZipAssembly))
            costuraExtractionPath = Path.GetDirectoryName(sevenZipAssembly);
    }
    catch
    {
        //Issue trying to grab the extraction path from the Assembly information so try
        //to use the first path found in the Costura directory as a last ditch effort

        DirectoryInfo di = null;

        string costuraTempPath = Path.Combine(
            Path.GetTempPath(), //ex: C:\Users\username\AppData\Local\Temp
            "Costura" //ex: Costura
        );

        di = new DirectoryInfo(costuraTempPath);
        if (!di.Exists)
            return false;
        //ex: C:\Users\username\AppData\Local\Temp\Costura\C83815D61CE49B2E8D23145A1B95A956\
        costuraExtractionPath = di.GetDirectories().First().FullName;
    }

    try
    {
        if (!Directory.Exists(costuraExtractionPath))
            throw new Exception();

        string sevenZipPath = Path.Combine(
            costuraExtractionPath, //ex: C:\Users\username\AppData\Local\Temp\Costura\C83815D61CE49B2E8D23145A1B95A956
            Environment.Is64BitProcess ? "64" : "32", //ex: 32
             "7z.dll" //ex: 7z.dll
        );

        if (!File.Exists(sevenZipPath))
            throw new Exception();

        SevenZipBase.SetLibraryPath(sevenZipPath);
        return true;
    }
    catch { return false; }
}

它的用法...

if (!SetupSevenZipLibrary())
    throw new Exception("Error setting the path of the 7zip library.");