简体   繁体   中英

understanding CLR/System.IO.FileNotFoundException "Could not load file or assembly ... or one of its dependencies"

I have an issue with using System.Text.Json insinde my C# class library, which is a SolidWorks addin. It is probably an instance of DLL hell as described here .

Since this approach does not work, I might be able to figure something out if I understand a bit more about this issue. Maybe someone can help?

First - my code.

My 'csproj' file:

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

  <!-- general stuff -->
  <PropertyGroup>
    <TargetFrameworks>net48</TargetFrameworks>
    <ImplicitUsings>disable</ImplicitUsings>
  </PropertyGroup>

  <!-- references: the top two are SolidWorks API (needed for making a SolidWorks addin -->
  <ItemGroup>
    <PackageReference Include="com.solidworks.core" Version="29.5.1" />
    <PackageReference Include="com.solidworks.tools" Version="21.5.0" />
    <PackageReference Include="System.Text.Json" Version="6.0.2" />
  </ItemGroup>

  <!-- In order to have the addin available within SolidWorks,
       it's dll needs to be registered in the codebase. For convenience
       we automatically register on build and unregister on clean. -->
  <Target Name="Register" AfterTargets="AfterBuild">
    <Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe &quot;$(TargetPath)&quot; /codebase" />
  </Target>
  <Target Name="Unregister" BeforeTargets="BeforeClean">
    <Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe &quot;$(TargetPath)&quot; /u" />
  </Target>

</Project>

The relevant parts of my cs file:

using System;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using SolidWorks...; // all the SolidWorks usings required

namespace SwxAddin
{
    [Guid("acb6f17b-9738-4f11-a324-30e05625ff89")]
    [ComVisible(true)]
    public class SwxAddinImpl : ISwAddin
    {
        // will be called on addin load in SolidWorks
        public bool ConnectToSW(object swx, int addinId)
        {
            var jsonText = "{ \"foo\": { \"bar\": 2 } }";
            var doc = System.Text.Json.JsonDocument.Parse(jsonText); // exception occurs

            return swx != null;
        }

        // will be called on addin unload in SolidWorks
        public bool DisconnectFromSW() { return true; }

        // This is run when registering the dll. It writes some stuff into the
        // SolidWorks registry to make the addin available.
        [ComRegisterFunction]
        protected static void RegisterFunction(Type type) { ... }

        // This is run when unregistering the dll. It removes the stuff from the
        // SolidWorks registry that was written into it by RegisterFunction.
        [ComUnregisterFunction]
        protected static void UnregisterFunction(Type type) { ... }
    }
}

When I run SolidWorks after building (and thus, registering my dll in the codebase) and debug it, I get a runtime error on

var doc = System.Text.Json.JsonDocument.Parse(jsonText);

saying

Exception has occurred: CLR/System.IO.FileNotFoundException An exception of type 'System.IO.FileNotFoundException' occurred in System.Text.Json.dll but was not handled in user code: 'Could not load file or assembly 'System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.'

. As mentioned above, I did try adding

<PropertyGroup>
  <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
  <GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
</PropertyGroup>

to my csproj file, resulting in the following .dll.config file in my bin/Debug folder:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

but the runtime error still occurs.

So I'd like to actually understand the issue instead of just following cooking recipes. Here are some things I tried and thoughts:

  1. The error says the issue is inside System.Text.Json.dll . I understand it so that the file System.Text.Json.dll , which lies in location A , expects a file System.Runtime.CompilerServices.Unsafe.dll of version 4.0.4.1 in location B , but in location B there is a different version of file System.Runtime.CompilerServices.Unsafe.dll (or no file of that name at all).

=> Can anyone tell me which locations A and B we are talking about? Is it a certain folder? Is it the GAC? In case it is the GAC, are we actually talking about files, or something else?

  1. I checked the (for me) most probable location, the folder $myProjectPath\bin\Debug\net48 . There I can find (amongst others) both dlls System.Text.Json.dll and System.Runtime.CompilerServices.Unsafe.dll . I opened both in some decompilation tool to check their versions and the versions of their references. This is what I found:
  • System.Text.Json.dll has version 6.0.0.2 and references System.Runtime.CompilerServices.Unsafe.dll of version 6.0.0.0 .

  • System.Runtime.CompilerServices.Unsafe.dll has version 6.0.0.0 .

=> So the required version and the present version of System.Runtime.CompilerServices.Unsafe.dll do align. Why do I then get the error? Doesn't this just mean that location A and B are NOT $myProjectPath\bin\Debug\net48 ? Or is the referenced version ignored under some circumstances? What kind of cirumstances?

  1. I built a standalone console app, just using System.Text.Json and containing the two lines

    var jsonText = "{ \"foo\": { \"bar\": 2 } }";

    var doc = System.Text.Json.JsonDocument.Parse(jsonText);

inside it Main method. No runtime error occurs there. So SolidWorks must be the culprit somehow , even if it is not mentioned in the runtime error message.

  1. This article covers dll hell and gives suggestions for troubleshooting. I checked the modules (Debug -> Windows -> Modules) in Visual Studio. It turns out that just before the error occurs
  • System.Text.Json version 6.0.0.0 is already loaded (from my $myProjectPath\bin\Debug\net48 folder).

  • System.Runtime.CompilerServices.Unsafe is not loaded.

=> But if System.Runtime.CompilerServices.Unsafe has not been loaded before, why does System.Text.Json want to load version 4.0.4.1 instead of the version specified in its own references ( 6.0.0.0 )? Where does the 4.0.4.1 come from?

Thanks to M Kloster's comment, I could work around the issue by manually loading the assembly - although this unfortunately does not help understanding the issue.

First I inserted the line

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

into the ConnectToSW method (as first line).

Then I implemented MyResolveEventHandler like so:

private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
    var nameCompilerServicesUnsafe = "System.Runtime.CompilerServices.Unsafe";
    if (args.Name == nameCompilerServicesUnsafe + ", Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
    {
        var assemblyPath = Assembly.GetCallingAssembly().Location;
        if (Path.GetFileName(assemblyPath) == "System.Text.Json.dll")
        {
            var assemblyFolder = Path.GetDirectoryName(assemblyPath);
            var pathCompilerServicesUnsafe = Path.Combine(assemblyFolder, nameCompilerServicesUnsafe + ".dll");
            if (File.Exists(pathCompilerServicesUnsafe))
                return Assembly.LoadFile(pathCompilerServicesUnsafe);
        }
    }

    return null;
}

Now, whenever an assembly cannot be loaded by the automatic mechanism, MyResolveEventHandler will be called.

Here I just check if it is System.Text.Json.dll trying to load System.Runtime.CompilerServices.Unsafe version 4.0.4.1 , and if yes, I return the assembly System.Runtime.CompilerServices.Unsafe.dll from System.Text.Json.dll 's location folder.

Strangely, this allowed me to confirm that the System.Text.Json.dll trying to load System.Runtime.CompilerServices.Unsafe version 4.0.4.1 really is the one located in my $myProjectPath\bin\Debug\net48 folder. Which makes no sense to me, as the decompilation tool told me that the file $myProjectPath\bin\Debug\net48\System.Text.Json.dll references System.Runtime.CompilerServices.Unsafe version 6.0.0.0 , not 4.0.4.1 .

And as I said in my question, the issue does not occur outside of SolidWorks (eg in a standalone console app). So SolidWorks must somehow interfere in the (automatic) assembly resolving mechanism, maybe redirecting bindings? Very mysterious... Is there a way to turn that off?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM