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
.NET Core hosting: Call managed method without knowing the types at compile time #40203
Comments
I couldn't figure out the best area label to add to this issue. If you have write-permissions please help me learn by adding exactly one area label. |
Tagging subscribers to this area: @vitek-karas, @swaroop-sridhar, @agocke |
@fsinisi90 can you elaborate on what you had in mind? I have seen people use a known .NET entry point, and that entry point uses reflection to then construct the call to a more dynamic call site. Would that work in your case? |
@jeffschwMSFT Thanks for answering. Basically, I have a C++ "engine" that allows C# scripting. I'm loading the managed assembly and I need to execute methods that I'm reading from an XML file, so I don't know the signatures at compile time (it's, of course, restricted to some primitive types like int, float, and string). With Mono, I was able to provide a string containing the signature, then create an array of std::string TypeMethodDescStr = "Program:MyMethodFromXML(int,single,string)";
TypeMethodDesc = mono_method_desc_new(TypeMethodDescStr.c_str(), NULL);
poMethod = mono_method_desc_search_in_image(TypeMethodDesc, m_poImage);
void* poArgs[poCommand->oArguments().size()];
if (poCommand->oArguments().size() > 0)
{
QVector<QStringRef> oTypes = poCommand->strSignature().splitRef(',');
// Prepare arguments
for (int i = 0; i < oTypes.size(); i++)
{
if (oTypes.at(i) == "string")
{
QString strArg = poCommand->oArguments()[i];
poArgs[i] = mono_string_new(mono_domain_get(), strArg.toUtf8());
}
else if (oTypes.at(i) == "single") // float in C#
{
float fArg = poCommand->oArguments()[i].toFloat();
cout << fArg << endl;
poArgs[i] = &fArg;
}
else
{
int iArg = poCommand->oArguments()[i].toInt();
cout << iArg << endl;
poArgs[i] = &iArg;
}
}
}
cout << "C++: Running the static method: " << TypeMethodDescStr.c_str() << endl;
mono_runtime_invoke(poMethod, nullptr, (void**)poArgs, nullptr); I was looking for a similar functionality using .NET Core but I guess it's not present yet. In that case, I'll have to do exactly what you proposed: have a known entry point and then use reflection to make the call. My problem now is how to marshal the data... If you have any code snippets or suggestions it will be highly appreciated. |
Here is a quick sample that has an arbitrary reflection based invoke.
What types are you looking to marshal? strings, arrays of strings, IntPtr's, and int's should all be very standard. |
@jeffschwMSFT Thank you very much, it works having a known entry point and then making the call using reflection. Is there a way to call non-static C# methods from C++? Or are you planning to add that feature? |
Not really, the interface between managed and native is c-style methods. If you have control of both sides it certainly is possible given you have access to cache objects on the managed code and can dispatch calls. I am including area owners who may have more to share. |
@fsinisi90 Not at the runtime level. This is one of those things that people can already do without any runtime feature. The likely future of this scenario is leveraging a Roslyn source generator or other source generation tool and autogenerating the boiler plate code for a natural C++ experience. The DNNE tool is part of the way there. The two biggest missing pieces are handling all the marshalling and making the generated C exports align with a C++ object model. |
Ok, I see. Thank you both for the quick answer! Yes, I have control over both sides. Could you share an example of how to make the mentioned call? |
@fsinisi90 Many people have asked for an example - thanks for the push. I have updated DNNE with an example on one approach - https://github.com/AaronRobinsonMSFT/DNNE/blob/master/test/ExportingAssembly/InstanceExports.cs. |
@AaronRobinsonMSFT Thank you. I noticed that .NET 5.0 is a minimum requirement. Is there a way to accomplish the same with .NET Core? My scenario is a C++ application hosting .NET Core. I'd like to instantiate managed objects and call methods from C/C++ directly. I can take care of creating the glue code and the marshalling. I was already able to call methods as callbacks using delegates. Any advice would be appreciated. Here's what I did so far. C#: public class Example
{
public int Add(int iFirst, int iSecond)
{
return iFirst + iSecond;
}
}
public class Program
{
[SuppressUnmanagedCodeSecurity]
[DllImport("__Internal", CallingConvention = global::System.Runtime.InteropServices.CallingConvention.Cdecl, EntryPoint = "ExecuteDelegate")]
internal static extern int __ExecuteDelegate(int iFirst, int iSecond, System.Delegate oHandler);
public delegate int Delegate(int iFirst, int iSecond);
public static void Initialize()
{
Example oExample = new Example();
Delegate oHandler = oExample.Add;
int iResult = __ExecuteDelegate(20, 30, oHandler);
Console.WriteLine(iResult);
}
} C++: extern "C"
{
__declspec(dllexport) int ExecuteDelegate(int iFirst, int iSecond, int (*f)(int, int))
{
return f(iFirst, iSecond);
}
} |
Yep. The .NET 5.0 requirement is simply because the optimized path is using C# function pointers and the |
Very clear, thanks again. I hope this is going to be useful to others as well - I'm closing the issue now. |
With
load_assembly_and_get_function_pointer
you can call a managed method by using the default signature or by using a custom signature (see this sample). But in both cases, you have to know the types at compile time.Is there a way to call a managed method but knowing the types at runtime?
The text was updated successfully, but these errors were encountered: