Skip to content

No way to set the .NET entry assembly from unmanaged host? Also, documentation seems to be wrong/unclear. #94139

Closed

Description

@Sebtous

Description

I have a rather complex scenario where I have to launch a WPF (.NET 7) application from another .NET application (let's call that one the .NET host application), in the same process as the .NET host application, which in turn is hosted in an unmanaged application using hostfxr's hdt_load_assembly_and_get_function_pointer.

In the .NET host application, I load the WPF application assembly from bytes in memory, then invoke the Assembly.EntryPoint to launch it. It is here that the problem manifests itself. WPF depends on Assembly.GetEntryAssembly () returning the main WPF assembly for resource management.
In my case however, Assembly.GetEntryAssembly () returns null, with no apparent way to set it to anything else, making the WPF app crash.

The documentation for Assembly.GetEntryAssembly states it returns:

The assembly that is the process executable in the default application domain, or the first executable that was executed by AppDomain.ExecuteAssembly(String). Can return null when called from unmanaged code.

However, when I look at the runtime source code, all AppDomain.ExecuteAssembly(String) seems to do is call a private method that invokes the assembly entrypoint, just like I am doing:

private static int ExecuteAssembly(Assembly assembly, string?[]? args)
{
    MethodInfo? entry = assembly.EntryPoint;
    if (entry == null)
    {
        throw new MissingMethodException(SR.Arg_EntryPointNotFoundException);
    }

    object? result = entry.Invoke(
        obj: null,
        invokeAttr: BindingFlags.DoNotWrapExceptions,
        binder: null,
        parameters: entry.GetParameters().Length > 0 ? new object?[] { args } : null,
        culture: null);

    return result != null ? (int)result : 0;
}

Needless to say, this does not make Assembly.GetEntryAssembly () return a different value.

As far as I can tell, there's no way to change the behavior of Assembly.GetEntryAssembly (), as all this function does is call GetEntryAssemblyInternal: github

So how do I make this work? (assuming I can't modify the WPF application as it is not mine).

Reproduction Steps

Create an unmanaged .NET host, use that to load a .NET assembly that loads another .NET assembly that is the main assembly of a WPF application.
Start the WPF application by invoking its Assembly.EntryPoint.

The application crashes on exceptions caused by Assembly.GetEntryAssembly () returning null.

Expected behavior

I expect there should be a way for an unmanaged host (or even another .NET assembly) to set the value returned by Assembly.GetEntryAssembly() to a specific assembly so that it does not return null.

Actual behavior

Assembly.GetEntryAssembly() always returns null

Regression?

No response

Known Workarounds

None as far as I can tell

Configuration

.NET 7, Windows 11, x64. Not relevant, I suspect.

Other information

No response

Activity

  1. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Oct 28, 2023
  2. davidfowl commented on Oct 28, 2023

    @davidfowl
    Member

    +1 @vitek-karas we spoke about this recently.

  3. vitek-karas commented on Oct 30, 2023

    @vitek-karas
    Member

    I can confirm that there's no way to do this currently. The only way to set the entry assembly from native host is to use the hostfxr_run_app API, but that is not usable in some cases.

    That said I think this issue and what @davidfowl and I were discussing is somewhat different. This scenario seems to need a way to effectively modify the value returned from GetEntryAssembly when there's already managed code running. The other scenario is if the managed code is executed via a different way than hostfxr_run_app, where it would be set just once on runtime start.

    I think that adding ability to set the entry point assembly at runtime start is something we can design/implement relatively easily. The only interesting questions would be:

    • How/where is it specified - for this I would probably go with a runtime property and just the simple name of the assembly.
    • How is the assembly resolved - for consistency I think this would require the assembly to be in the TPA, or otherwise immediately resolvable at runtime start.
    • What is the interaction with hostfxr_run_app - what happens if the property is specified in this case - should the property win or the "run app" call win.

    Adding the ability to change this value later on - basically from managed code (as this scenario loads the assembly from memory), is something I would be a bit more nervous about. Whether specified or not, the runtime currently effectively guarantees that the value will never change - allowing it could break things in subtle ways.

    @elinor-fung - any ideas, opinions?

  4. jkotas commented on Oct 30, 2023

    @jkotas
    Member

    Adding the ability to change this value later on - basically from managed code (as this scenario loads the assembly from memory), is something I would be a bit more nervous about.

    My preference is to add a managed API to let people set it to whatever they want anytime to solve this problem once for all. I agree that it would be somewhat dangerous API. It is similar to AppDomain.CurrentDomain.SetData when used on keys owned by the runtime.

  5. jkotas commented on Oct 30, 2023

    @jkotas
    Member

    This managed API would allow us to get rid of the s_forceNullEntryPoint internal testing hook.

  6. added this to the Future milestone on Dec 2, 2023
  7. ghost removed
    untriagedNew issue has not been triaged by the area owner
    on Dec 2, 2023
  8. elinor-fung commented on Jun 7, 2024

    @elinor-fung
    Member

    The new Assembly.SetEntryAssembly API allows setting the entry assembly:

    With this, you should be able to set the entry assembly once your first .NET assembly is loaded / before running the other assembly's entry point.

    Docs are also updated to clarify that the comments about AppDomain.ExecuteAssembly are only for .NET Framework (dotnet/dotnet-api-docs#10004).

  9. locked and limited conversation to collaborators on Jul 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    • Status
      No status

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions