无法从本地项目编译的 dll 加载某些类型 [英] Some types could not be loaded from a dll that was compiled from a local project

查看:71
本文介绍了无法从本地项目编译的 dll 加载某些类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个没有外部依赖项的 C# 项目,名为Hopper".我将它分成几个不同的模块,每个模块都包含在相应的子文件夹中.该问题的相关问题如下:

I am working on a C# project without external dependencies, named "Hopper". I have split it up into a couple of different modules, each contained within a corresponding sub-folder. The relevant ones for the problem are the following:

  • Utils/Hopper.Utils.csproj
  • 共享/Hopper.Shared.csproj
  • Core/Hopper.Core.csproj,其中引用了 UtilsShared,稍后会详细介绍
  • Mine/Hopper.Mine.csproj,引用Core
  • Utils/Hopper.Utils.csproj
  • Shared/Hopper.Shared.csproj
  • Core/Hopper.Core.csproj, which references Utils and Shared, more details later
  • Mine/Hopper.Mine.csproj, which references Core

所有这些都针对 .NET 4.8,包括 Hopper.Core.

All of these target .NET 4.8, including Hopper.Core.

我已将每个项目的 AssemblyName 属性设置为相应的字符串.例如,Hopper.Utils.csproj:

I have set the AssemblyName property for each of the projects to the corresponding string. For example, the Hopper.Utils.csproj:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Library</OutputType>
    <TargetFramework>net4.8</TargetFramework>
    <AssemblyName>Hopper.Utils</AssemblyName>
  </PropertyGroup>

</Project>

Hopper.Core.csproj 通过 ProjectReference 引用其他项目,如下所示:

Hopper.Core.csproj references the other projects via ProjectReference, like so:

<ItemGroup>
  <ProjectReference Include="..\Utils\Hopper.Utils.csproj" />
  <ProjectReference Include="..\Shared\Hopper.Shared.csproj" />
</ItemGroup>

Hopper.Mine.csproj,为测试目的而创建,引用核心如下:

Hopper.Mine.csproj, created for testing purposes, references core like this:

<ItemGroup>
  <ProjectReference Include="..\Core\Hopper.Core.csproj" />
</ItemGroup>

Hopper.Core 项目包含一些类型,包括类和结构.一些源文件是由工具自动生成的,并且在顶部包含 #pragma warning disable.(删除它没有帮助).

The Hopper.Core project contains some types, both classes and structs. Some source files have been autogenerated by a tool and contain #pragma warning disable at the top. (Removing it did not help).

Hopper.Core 引用的两个项目只是包含了一些类型和属性,被Core 使用,所以对这个问题没什么兴趣.它们不引用任何其他项目或 dll.

The two projects referenced by Hopper.Core just contain some types and attributes, used by Core, so nothing of interest to the problem. They do not reference any other projects or dll's.

我可以通过在项目文件的子文件夹中运行 dotnet build 来编译 Hopper.Mine.我能够在每个项目文件夹下的 bin/Debug/net4.8/Hopper.Whatever.dll 中看到引用子项目的输出 dll.VSCode 中的 Intellisense 也能正常工作,不会报告任何错误.

I am able to compile Hopper.Mine by running dotnet build in the sub-folder with the project file. I am able to see the output dll's of the referenced sub-projects in bin/Debug/net4.8/Hopper.Whatever.dll under each project's folder. Intellisense in VSCode also works correctly and does not report any errors.

Hopper.Mine 有一个带有 Main 函数的类,用于测试.列出加载成功的Hopper.Core的类型,并报告未加载的:

Hopper.Mine has a single class with a Main function, created for a test. It lists the types of Hopper.Core that have loaded successfully, and reports the ones that have not been loaded:

using System;
using System.Reflection;
using System.Text;

namespace Hopper.Mine
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Assembly lib = typeof(Hopper.Core.Action).Assembly;
            Type[] types;
            try
            {
                types = lib.GetTypes();
            }
            catch (ReflectionTypeLoadException ex)
            {
                StringBuilder sb = new StringBuilder();
                foreach (Exception exSub in ex.LoaderExceptions)
                {
                    Console.WriteLine(exSub.Message);
                }
                types = ex.Types;
            }
            
            foreach (Type type in types)
            {
                if (type != null)
                    Console.WriteLine(type.FullName);
            }
        }
    }
}

运行这段代码,我可以看到一堆有问题的类型,其中一些重复了,其余的类型已经成功加载:

Running this code, I can see a bunch of problematic types, some of which repeat, and the rest of types that have loaded successfully:

Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Dig' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Move' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Push' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Could not load type 'Hopper.Core.Stat.Attack' from assembly 'Hopper.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

... more errors here ...

Hopper.Core.DirectedPredict
Hopper.Core.UndirectedPredict
Hopper.Core.DirectedDo

... more types here ...

Hopper.Core.Stat.Attack+Source+Resistance
Hopper.Core.Stat.Push+Source+Resistance

需要注意的 4 件事:

To be noted 4 things:

  1. 加载失败的类型都是结构体,派生自IStat.
  2. 具有这些类型的源文件已由工具自动生成(工具加载生成的其他文件没有错误).
  3. 这些类型不引用 Hopper.SharedHopper.Utils 中的任何类型.
  4. 某些类型具有嵌套结构,最多 3 级深度,一些更深的似乎已成功加载.
  1. The types that failed to load are all structs and derive from IStat.
  2. The source files with these types have been autogenerated by a tool (other files generated by the tool load without errors).
  3. The types do not reference any types from Hopper.Shared or Hopper.Utils.
  4. Some of the types have nested structs, up to 3 level deep, some of the deeper ones seem to have loaded successfully.

我很确定 Hopper.Core 的输出文件夹中的 dll 输出与 Hopper.Mine 的输出文件夹中的相同.我也很确定在我启动程序时链接了正确的 dll(我已经尝试使用 VS 进行调试,在那里我可以看到链接的是哪个 dll 以及匹配的路径).

I am pretty sure that the dll output from the Hopper.Core's output folder is identical to the one in the output folder of Hopper.Mine. I am also pretty sure that the right dll is linked against when I start the program (I've tried debugging with VS, where I can see which dll is being linked against and the paths matched).

我尝试删除 AssemblyName 属性,将 csproj 文件从 Hopper.Whatever.csproj 重命名为 Hopper_Whatever.csproj,我显然尝试过清理任何以前的输出 dll 并从头开始构建,这些都没有帮助.我还能够在另一台机器上重现这一点,结果完全相同.

I have tried removing AssemblyName properties, renaming csproj files from Hopper.Whatever.csproj to Hopper_Whatever.csproj, I've obviously tried cleaning up any previous output dll's and building from scratch, none of which helped. I have also been able to reproduce this on a different machine with the exact same results.

我尝试使用反编译器 ILSpy 查看 Hopper.Core dll.我能够看到失踪"代码中的类型.

I have tried looking at the Hopper.Core dll with a decompiler, ILSpy. I am able to see the "missing" types as they are in code.

我已经在谷歌上搜索了几个小时来寻找像我这样的问题,但无济于事.我发现很多人错误地设置了他们的项目文件,因此他们意外地链接到了与他们预期不同的 dll 版本.对于某些人来说,这种依赖性出现在引用的第三方项目之一的需求中.有些人指责 GAC 提供了错误版本的 dll.但是,我很确定我的项目设置正确(+ 设置非常简单并且没有引用第三方项目)并且我引用了正确的 dll(因为我可以在其中看到我的类型).看起来类型在 dll 中是声明"的,但不是定义"的(有点),所以 dll 被破坏了.但是为什么反编译不会失败呢?所以我目前完全被困在这个问题上.

I have searched on google for hours for a problem like mine, to no avail. I have found many cases of people setting up their project files incorrectly, due to which they unexpectedly link against a different version of the dll than they expected. For some, such dependency showed up in requirements of one of the referenced third party projects. Some blame GAC for providing incorrect version of the dll. However, I'm pretty sure that my projects are set up right (+ the set up is pretty simple and no third party projects are being referenced) and I am referencing the right dll's (since I can see my types in it). It kind of seems like the types are 'declared' in the dll, but not 'defined' (sort of), so the dll's turn out corrupted. But then why would the decompilation not fail? So I'm currently completely stuck on this.

我尝试通过它们的名称动态链接 dll.列出从 Hopper.SharedHopper.Utils 导入的类型有效,但是,当我尝试使用 Hopper.Core 时,我得到相同的结果输出如上例.

I have tried linking against the dll's dynamically, by their names. Listing the types imported from Hopper.Shared and Hopper.Utils works, however, when I try it with Hopper.Core, I get the same output as the example above.

为了实现这一点,我将以前的 dll 从输出复制到一个新文件夹,从 Hopper.Mine 中删除引用,使用 dotnet build 编译新代码>,然后将可执行文件移动到新文件夹并最终在控制台中运行它.代码:

To achieve this, I have copied the previous dll's from the output over to a new folder, removed the reference from Hopper.Mine, compiled the new code with dotnet build, after which moved the executable to the new folder and finally ran it in the console. The code:

using System;
using System.Reflection;
using System.Text;

namespace Hopper.Mine
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Assembly shared = Assembly.LoadFrom("Hopper.Shared.dll");
            Assembly utils = Assembly.LoadFrom("Hopper.Utils.dll");
            Assembly core = Assembly.LoadFrom("Hopper.Core.dll");
            Type[] types;
            try
            {
                types = core.GetTypes();
            }
            catch (ReflectionTypeLoadException ex)
            {
                StringBuilder sb = new StringBuilder();
                foreach (Exception exSub in ex.LoaderExceptions)
                {
                    Console.WriteLine(exSub.Message);
                }
                types = ex.Types;
            }
            
            foreach (Type type in types)
            {
                if (type != null)
                    Console.WriteLine(type.FullName);
            }
        }
    }
}

重现

如果有帮助,您可以尝试运行我的代码.这是指向项目当前状态的 github 链接.为了运行它,您需要:

To reproduce

If it helps, you may try running my code. Here's the github link to the current state of the project. In order to run it, you need:

  1. 已安装 .NET 4.8.
  2. 从上面的链接克隆存储库.
  3. 仍然缺少一些代码.您需要运行文件夹 Meta 中的项目来生成它.只需在 Meta 文件夹中运行 dotnet run.
  4. 现在您可以运行 Mine 子项目.
  1. .NET 4.8 installed.
  2. Clone the repository from the link above.
  3. Some code is still missing. You need to run the project in the folder Meta to generate it. Just run dotnet run in the Meta folder.
  4. Now you can run the Mine subproject.

推荐答案

找出原因.CLR 类型系统中存在一个已存在 6 年的错误.基本上,在其他结构中声明的泛型结构类型的字段(静态或非静态)会导致此行为.有关详细信息,请参阅此 github 线程.

Found out the reason. There is a bug in the CLR type system that has been around for 6 years. Basically, fields (either static or non-static) of type of a generic struct declared in other structs cause this behavior. See this github thread for more details.

就我而言,我已经能够制作一个更短的版本来说明该错误.

In my case, I have been able to make a shorter version that illustrates the bug.

public struct Index<T>
{
}

public struct Test
{
    public static Index<Test> Index;
}

使用以下代码测试程序:

Use the following code to test the program:

public class Program
{
    public static void Main(string[] args)
    {
        Console.WriteLine(typeof(Test).Assembly.GetTypes());
    }
}

代码运行时,抛出如下错误:

When the code is run, the following error is thrown:

Unhandled Exception: System.TypeLoadException: Could not load type 'Hopper.Mine.Test' from assembly 'Hopper.Mine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
   at Hopper.Mine.Program.Main(String[] args)

但是,对此有一种简单的解决方法.将 Test 从 struct 更改为 class 有效:

However, there is an easy sort of workaround for this. Changing Test from struct to class works:

// This works!
public struct Index<T>
{
}

public class Test
{
    public static Index<Test> Index;
}

在某些情况下这可能是不可接受的.所以这是另一种解决方法——将静态字段存储在数组中并使用属性访问它也可以:

That may not be acceptable in some cases. So here is another workaround — storing the static field in an array and accessing it with a property also works:

public struct Index<T>
{
}

public struct Test
{
    public static Index<Test> Index { get => index[0]; set => index[0] = value; }
    public static Index<Test>[] index = new Index<Test>[1];
}

这篇关于无法从本地项目编译的 dll 加载某些类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆