dotnet/docs

What is an "Assembly qualified delegate type name"?

brianjenkins94 opened this issue · 11 comments

Does this provide me a way to specify the function signature of the C# method?

What would an example of this parameter look like?

https://github.com/dotnet/samples/blob/master/core/hosting/HostWithHostFxr/src/NativeHost/nativehost.cpp#L104


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Still interested in the answer to the original question, though.

I'll ping the author on email.

Regarding the original question, yes, my understanding is that HostFxr allows specifying a particular overload of a managed method by giving the (assembly-qualified) name of a delegate defining the method signature (as opposed to hosting with CoreClrHost.h, which only looks up managed entry points by method name).

You should be able to just provide a string containing the assembly-qualified name of the delegate type matching your entry point's signature (including, perhaps, marshaling attributes).

cc'ing @vitek-karas to confirm this, though, because I'm not very experienced with HostFxr.

The code which consumes that string is here: https://github.com/dotnet/runtime/blob/eb95163a0880cac1ba127b81e3cb753e5a65f8cc/src/coreclr/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs#L64

If you specify null it defaults to the `ComponentEntryPoint delegate: https://github.com/dotnet/runtime/blob/eb95163a0880cac1ba127b81e3cb753e5a65f8cc/src/coreclr/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs#L19

If you specify a value, it is effectively passed to Type.GetType(yourValue, ...): https://github.com/dotnet/runtime/blob/eb95163a0880cac1ba127b81e3cb753e5a65f8cc/src/coreclr/src/System.Private.CoreLib/src/Internal/Runtime/InteropServices/ComponentActivator.cs#L114

It should be assembly qualified to avoid possible issues with duplicate type names across assemblies, but in reality it's not enforced.

As @mjrousos noted this lets you declare the method signature via declaring a delegate type in C#. The added benefit is that you can use marshalling attributes in the delegate if necessary.

So I should be able to invoke this:

namespace SamplePrecompiledAssembly {
	public static class Startup {
		public static Func<object, Task<object>> Func(string code) { // <--

Like this?:

returnCode = load_assembly_and_get_function_pointer_fn(loadAssemblyAndGetFunctionPointer)(
	assemblyPath.c_str(),
	assemblyQualifiedTypeName.c_str(),
	methodName.c_str(),
	std::string("SamplePrecompiledAssembly.Startup+Func, SamplePrecompiledAssembly").c_str(), // <--
	nullptr,
	reinterpret_cast<void **>(&run));

The string should point to a delegate type, not a method. So something like this should work:

namespace SamplePrecompiledAssembly {
	public static class Startup {
                public delegate Func<object, Task<object>> MyFuncDelegate(string);

		public static Func<object, Task<object>> Func(string code) { 
                }
        }
}

And then you should be able to use:
SamplePrecompiledAssembly.Startup.MyFuncDelegate as the string for the function.

Note that given this sample it may not work as I don't know how the interop would marshal a Func<,> to native code. And even if it did manage to do that, there would be no way from the native code to operate on Task<object>.

Hmm, I'm beginning to think this might be a product bug.

Steps to Reproduce:

git clone https://github.com/brianjenkins94/Run-DNC.git
cd Run-DNC
npm install
npm build # For whatever reason on Windows you just need to keep building through errors about architecture mismatches and errors writing to the program database.
npm start

Output on Windows 10:

> run-dnc@1.0.0 start C:\Users\User\Documents\GitHub\Run-DNC
> node test.js

get_hostfxr_path() = 0
hostfxr_initialize_for_runtime_config_fn() = 0
hostfxr_get_runtime_delegate_fn() = 0
hostfxr_close_fn() = 0

assembly_path = "C:\\Users\\User\\Documents\\GitHub\\Run-DNC\\src\\SamplePrecompiledAssembly\\bin\\Debug\\netstandard2.1\\SamplePrecompiledAssembly.dll"
type_name = SamplePrecompiledAssembly.Startup, SamplePrecompiledAssembly
method_name = Invoke
delegate_type_name = SamplePrecompiledAssembly.Startup.InvokeDelegate, SamplePrecompiledAssembly

load_assembly_and_get_function_pointer_fn() = 0
run() = 0

Output on macOS 10.15 and Ubuntu 18.04:

> run-dnc@1.0.0 start /Users/bjenks/Sites/Run-DNC
> node test.js

get_hostfxr_path() = 0
hostfxr_initialize_for_runtime_config_fn() = 0
hostfxr_get_runtime_delegate_fn() = 0
hostfxr_close_fn() = 0

assembly_path = "/Users/bjenks/Sites/Run-DNC/src/SamplePrecompiledAssembly/bin/Debug/netstandard2.1/SamplePrecompiledAssembly.dll"
type_name = SamplePrecompiledAssembly.Startup, SamplePrecompiledAssembly
method_name = Invoke
delegate_type_name = SamplePrecompiledAssembly.Startup.InvokeDelegate, SamplePrecompiledAssembly

load_assembly_and_get_function_pointer_fn() = -2146233054
run() = zsh: segmentation fault

So it's working on Windows, but not on Mac or Linux.

Where would be the appropriate place to pursue this? Since I assume docs isn't it.

I didn't try to run it, but looking at the code there's a difference between Windows and Linux/Mac - when you're calling the load_assembly_and_get_function_pointer you pass the delegate type name in the Linux/Mac path, but not in the Windows path. Also the output above doesn't seem to match the code in the repo: The code in the repo would specify SamplePrecompiledAssembly.InvokeDelegate as the delegate type name (without the Startup) but the output above has it - so maybe you have local fixes?

The error code is COR_E_TYPELOAD - this would happen if the call Type.GetType(delegateTypeName) failed.

I made those fixes and it works now.

Thank you very much for your help and patience.