Skip to content

Need way to exclude specified assemblies from automatic loading via the dependency file and not bypass AssemblyResolve #77751

@rkeithhill

Description

@rkeithhill

We have a library we provide to internal partners that contains sensitive code that we don't want to be hijacked at runtime. So we require our partners to embed this assembly as a manifest resource and use AssemblyResolve at runtime to extract the assembly from the embedded resource and load it as a byte stream like below. Note: AssemblyResolve works in both new .NET and old .NET Framework.

private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    string assemblyName = "Acme.Foo.Bar";

    // Do not resolve any other assemblies
    if (!args.Name.StartsWith($"{assemblyName},"))
    {
        return null;
    }

    // Look through the manifest resource names for the assembly name
    string apiAssemblyResourceName = null;
    Assembly manifestHostAssembly = Assembly.GetExecutingAssembly();
    foreach (string resourceName in manifestHostAssembly.GetManifestResourceNames())
    {
        if (resourceName.EndsWith($"{assemblyName}.dll"))
        {
            apiAssemblyResourceName = resourceName;
            break;
        }
    }

    if (apiAssemblyResourceName == null) throw new DllNotFoundException(assemblyName);

    Assembly loadedAssembly = null;
    using (Stream streamApiAssembly = manifestHostAssembly.GetManifestResourceStream(apiAssemblyResourceName))
    {
        // Given that the resource name was returned by reflection, the streamApiAssembly should rarely be null
        if (streamApiAssembly == null) throw new DllNotFoundException(assemblyName);

#if NETCOREAPP
        loadedAssembly = AssemblyLoadContext.Default.LoadFromStream(streamApiAssembly);
#else
        byte[] assemblyBytes = new byte[streamApiAssembly.Length];
        streamApiAssembly.Read(assemblyBytes, 0, assemblyBytes.Length);
        loadedAssembly = Assembly.Load(assemblyBytes);
#endif
    }

    return loadedAssembly;
}

This works great if you remove the generated .deps.json file which isn't a problem for our sample console apps. However, as partners are building more complicated WPF apps, they need to leave the .deps.json file in-place. This causes the app to fail because something is trying to load the assembly, which is embedded in the app dll and NOT on the filesystem. That load attempt fails before the app ever hits the AssemblyResolve event, preventing the app from starting.

Our current hack is to use a bit of PowerShell to remove the assembly from the libraries section of the .deps.json file:

function Remove-LibFromDependencyFile([string]$Path) {
    $jsonDepth = 10
    $depsJson = Get-Content $Path | ConvertFrom-Json -Depth $jsonDepth
    $updatedDepsJson = $depsJson | ForEach-Object {
        $jsonObj = $_
        $name = $_.libraries.psobject.Members | Where-Object Name -match "^Acme\.Foo\.Bar\/" | ForEach-Object Name
        $jsonObj.Libraries.psobject.Members.Remove($name)
        $jsonObj
    }
    $updatedDepsJson | ConvertTo-Json -Depth $jsonDepth | Out-File $Path -Encoding ascii
}

Is there a better way to accomplish this goal of having the .NET runtime NOT try to preload a specified assembly and let my app do the assembly resolution? Or maybe there's a way to keep the assembly out of the .deps.json file which would prevent said mechanism from trying to "pre-load" the assembly?

Somewhat related: Fody/Costura#282

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-AssemblyLoader-coreclrquestionAnswer questions and provide assistance, not an issue with source code or documentation.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions