Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Problem loading unmanaged assemblies #1253

Closed
CharliePoole opened this issue Oct 26, 2022 · 6 comments · Fixed by #1370
Closed

Problem loading unmanaged assemblies #1253

CharliePoole opened this issue Oct 26, 2022 · 6 comments · Fixed by #1370
Labels

Comments

@CharliePoole
Copy link
Member

CharliePoole commented Oct 26, 2022

Issue #1208 became a sort of anthology of somewhat related problems, leading (me) to a certain amount of confusion. All issues are solved, except the one described in this comment by @cw397. I'm making this problem a separate issue. The extract below gives the original description of the problem.


Our issue is with an unmanaged/native library. We are using a NuGet package (Esri.ArcGISRuntime) that has some unmanaged dependencies. As seems to be the convention, these get copied to a runtimes/{rid}/native folder within our test project bin/Debug directory, e.g. runtimes/win-x64/native. As with all the other issues, tests that use this library run fine in Visual Studio but fail with the error "Could not load ArcGIS Runtime (RuntimeCoreNet100_15.dll) or one of its dependencies" when run with NUnit Console.

I can workaround the issue in a couple of ways:

Copy the relevant files out of the runtimes/{rid}/native folder and put them directly in the bin/Debug directory.
Copy the entire runtimes folder into the .nuget\packages\nunit.consolerunner\3.15.2\tools\agents\net6.0 folder and also copy the relevant items from our test project's .deps.json file into the nunit-agent.deps.json file.

I have found some explanation about how native assemblies are probed for (https://learn.microsoft.com/en-us/dotnet/core/dependency-loading/default-probing), and potential ways of configuring it (https://github.com/dotnet/cli/blob/master/Documentation/specs/runtime-configuration-file.md), however I haven't been able to figure out any better workarounds than the two listed above.

@CharliePoole
Copy link
Member Author

CharliePoole commented Nov 13, 2022

@cw397 Would you please try running with version 3.16.0-dev00076, available from our MyGet feed?

@cw397
Copy link

cw397 commented Nov 14, 2022

That version doesn't seem to help I'm afraid, I still get the same behaviour.

@CharliePoole
Copy link
Member Author

@cw397 Thanks... I had made substantial changes to how dependencies are analyzed but I guess unmanaged dependencies still need to be addressed.

@CharliePoole CharliePoole modified the milestones: 3.16.0, 3.16.1 Nov 15, 2022
@CharliePoole CharliePoole removed this from the 3.16.1 milestone Jan 1, 2023
@vgriph
Copy link
Contributor

vgriph commented Oct 27, 2023

I've been able to work around this issue by using a SetUpFixture as follows:

[SetUpFixture, Order(1)] // Order doesn't work, so it has to be in a namespace higher than other namespaces if other setup fixtures depend on unmanaged dlls
public class SetUpUnmanagedAssembliesResolving
{
    private AssemblyDependencyResolver runtimeResolver;

    [OneTimeSetUp]
    public void SetupAssemblyLoadContextToHandleUnmanagedAssemblyLoadFailure()
    {
        var assembly = typeof(SetUpUnmanagedAssembliesResolving).Assembly;
        var testAssemblyLoadContext = AssemblyLoadContext.GetLoadContext(assembly) ?? AssemblyLoadContext.Default;
        testAssemblyLoadContext.ResolvingUnmanagedDll += OnResolvingUnmanagedDll;

        var assemblyLocation = assembly.Location;
        runtimeResolver = new AssemblyDependencyResolver(assemblyLocation);
    }

    private IntPtr OnResolvingUnmanagedDll(Assembly requestingAssembly, string dllName)
    {
        var path = runtimeResolver.ResolveUnmanagedDllToPath(dllName);
        if (!string.IsNullOrEmpty(path))
        {
            return NativeLibrary.Load(path);
        }

        return IntPtr.Zero;
    }
}

@veleek
Copy link
Member

veleek commented Dec 15, 2023

I'm working on a fix for this.

veleek added a commit to veleek/nunit-console that referenced this issue Dec 15, 2023
Loading of unmanaged assemblies suffers from the same issues as loading managed assemblies in that the wrong deps.json is used by default.  This updates the TestAssemblyLoadContext to use the exact same pattern for loading unmanaged assemblies as managed assemblies.
1. Use the default assembly loading logic (I'm unsure if we actually need this since, in both managed and unmanaged, the base AssemblyLoadContext logic is a no-op and it's actually the VM that has some logic for loading assemblies).
2. Use an AssemblyDependencyResolver for the test assembly.
3. Check in the same folder as the test assembly (in case the dependencies are not fully specified in deps.json).

Resolves nunit#1253
@bilbothebaggins
Copy link

I have exactly this issue and would just like to add my details here to maybe make this more discoverable for future readers.
I'm using the latest nunit-consolerunner 3.16.3 on Windows 10.

We are using System.Data.SQLite.Core/1.0.118 which uses Stub.System.Data.SQLite.Core.NetStandard.
This nuget package contains runtimes\win-x64\native\SQLite.Interop.dll which is copied to this runtimes location in the ouput folder. It is also entered into the ..deps.json file of my project and the nunit project.

Running from within VS or with dotnet test works fine, The native DLL is loaded from (TestAssemblyDir)\runtimes\win-x64\native\SQLite.Interop.dll
Running with nuget3-console.exe fails with

1) Error : ...
System.DllNotFoundException : Unable to load DLL 'SQLite.Interop.dll' or one of its dependencies: Das angegebene Modul wurde nicht gefunden. (0x8007007E)
   at System.Data.SQLite.UnsafeNativeMethods.sqlite3_config_none(SQLiteConfigOpsEnum op)
   at System.Data.SQLite.SQLite3.StaticIsInitialized()

Adding the workaround code posted by #1253 (comment) above does work 💯

Obvioulsy, having #1317 make it into production will be ideal, but I fear I'll have to live with the workaround for now.

veleek added a commit to veleek/nunit-console that referenced this issue Jan 9, 2024
Loading of unmanaged assemblies suffers from the same issues as loading managed assemblies in that the wrong deps.json is used by default.  This updates the TestAssemblyLoadContext to use the exact same pattern for loading unmanaged assemblies as managed assemblies.
1. Use the default assembly loading logic (I'm unsure if we actually need this since, in both managed and unmanaged, the base AssemblyLoadContext logic is a no-op and it's actually the VM that has some logic for loading assemblies).
2. Use an AssemblyDependencyResolver for the test assembly.
3. Check in the same folder as the test assembly (in case the dependencies are not fully specified in deps.json).

Resolves nunit#1253
veleek added a commit to veleek/nunit-console that referenced this issue Apr 4, 2024
Loading of unmanaged assemblies suffers from the same issues as loading managed assemblies in that the wrong deps.json is used by default.  This updates the TestAssemblyLoadContext to use the exact same pattern for loading unmanaged assemblies as managed assemblies.
1. Use the default assembly loading logic (I'm unsure if we actually need this since, in both managed and unmanaged, the base AssemblyLoadContext logic is a no-op and it's actually the VM that has some logic for loading assemblies).
2. Use an AssemblyDependencyResolver for the test assembly.
3. Check in the same folder as the test assembly (in case the dependencies are not fully specified in deps.json).

Resolves nunit#1253
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants