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

CustomHostClr Sample doesn't work with returning values #6975

Open
RomanGirin opened this issue Jul 4, 2024 · 1 comment
Open

CustomHostClr Sample doesn't work with returning values #6975

RomanGirin opened this issue Jul 4, 2024 · 1 comment

Comments

@RomanGirin
Copy link

RomanGirin commented Jul 4, 2024

[CustomHostClr Sample doesn't work with returning values]

I worked through this sample core/hosting/src. There is a missing sample for invocation when some value retuned from the managed side via pre-defined buffer.
I tried to add this sample myself but it doesn't seem to work.

On the native side (in nativehost.cpp line ~190) added:

        // Function pointer to managed delegate with non-default signature
        typedef void (CORECLR_DELEGATE_CALLTYPE *custom_entry_point_returning_fn)(wchar_t const* someptr, wchar_t* out_res);
        custom_entry_point_returning_fn custom_returning = nullptr;

        // Custom delegate type
        rc = load_assembly_and_get_function_pointer(
            dotnetlib_path.c_str(),
            dotnet_type,
            STR("CustomEntryPointReturning") /*method_name*/,
            STR("DotNetLib.Lib+CustomEntryPointReturningDelegate, DotNetLib") /*delegate_type_name*/,
            nullptr,
            (void**)&custom_returning);
        assert(rc == 0 && custom_returning != nullptr && "Failure: load_assembly_and_get_function_pointer()");
        wchar_t const* someptr = STR("some data");
        wchar_t buffer[128]{STR("some init data")};
        custom_returning(someptr, buffer);
        
        std::wcout << STR("returned buffer: ") << buffer << std::endl;

In the Lib.cs I added:

        public delegate void CustomEntryPointReturningDelegate(IntPtr someptr, StringBuilder res);
        public static void CustomEntryPointReturning(IntPtr someptr, StringBuilder res)
        {
            string message = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
                ? Marshal.PtrToStringUni(someptr)
                : Marshal.PtrToStringUTF8(someptr);

            Console.WriteLine($"Hello, world! from {nameof(CustomEntryPointReturning)} in {nameof(Lib)}");

            Console.WriteLine($"Passed-in string: {message}");

            Console.WriteLine($"the buffer on the managed side: {res.ToString()}");
            res.Clear();
            res.Append("some returned text");
            Console.WriteLine($"the buffer on the managed side: {res.ToString()}");
        }

It outputs:

Hello, world! from CustomEntryPointReturning in Lib
Passed-in string: some data
the buffer on the managed side: s
the buffer on the managed side: some returned text
returned buffer:

Please, add this kind of example I sketch above (with some fixes required) into the sample.

UPDATE:
To fix reported behaviour

On the native side (in nativehost.cpp line ~190) added:

        // Function pointer to managed delegate with non-default signature
        typedef void (CORECLR_DELEGATE_CALLTYPE *custom_entry_point_returning_fn)(wchar_t const* someptr, char* out_res);
        custom_entry_point_returning_fn custom_returning = nullptr;

        // Custom delegate type
        rc = load_assembly_and_get_function_pointer(
            dotnetlib_path.c_str(),
            dotnet_type,
            STR("CustomEntryPointReturning") /*method_name*/,
            STR("DotNetLib.Lib+CustomEntryPointReturningDelegate, DotNetLib") /*delegate_type_name*/,
            nullptr,
            (void**)&custom_returning);
        assert(rc == 0 && custom_returning != nullptr && "Failure: load_assembly_and_get_function_pointer()");
        wchar_t const* someptr = STR("some data");
        char buffer[128]{"some init data"};
        custom_returning(someptr, buffer);
        
        std::wcout << STR("returned buffer: ") << buffer << std::endl;

Note the change of type for out_res pointer from wchat_t to char. And then it works (the output is below):

Hello, world! from CustomEntryPointReturning in Lib
Passed-in string: some data
the buffer on the managed side: some init data
the buffer on the managed side: some returned text
returned buffer: some returned text

It's strange that marshaled back string is 8-bit instead of 16-bit. If you let me know I'll create issue in more relevant repo about this possible CLR marshaling bug.


Issue metadata

  • Issue type: sample-update
@RomanGirin
Copy link
Author

RomanGirin commented Jul 4, 2024

Also add a small remark as a comment: this example IMHO is very important because returning values via pre-created buffer is very often used idiom. Actually this is the only way(?) to return a string from managed side to native. So without this kindly requested addition the sample is incomplete. Thank you in advance!

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

No branches or pull requests

1 participant