InteropServices.COMException thrown when passing c# structure to c++

0

I have a Java API that I want to access via c# code running on centos (Linux) running in the .Net-Core 3.1.100 version. Consequently I wish to pass a structure array from c# to native code responsible for launching the JVM and doing the java stuff. My problem was that the c# structure array would not get to the c++ code under certain circumstances. Here is the c# code:

using System;
using System.Runtime.InteropServices;

namespace dotnet
{
    [StructLayout (LayoutKind.Explicit)]
    internal struct JValue {
        [FieldOffset (0)] bool z;
        [FieldOffset (0)] byte b;
        [FieldOffset (0)] char c;
        [FieldOffset (0)] short s;
        [FieldOffset (0)] int i;
        [FieldOffset (0)] long j;
        [FieldOffset (0)] float f;
        [FieldOffset (0)] double d;
        [FieldOffset (0)] IntPtr l;

        internal long JLong {
            set {
                Console.WriteLine("JLong setter set JValue as a long with " + value);
                j = value;
            }
        }

        internal float JFloat {
            set {
                Console.WriteLine("JFloat setter set JValue as a float with " + value);
                f = value;
            }
        }
    }

    class Program
    {
        [DllImport("libNative.so", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.LPStr)]
        public static extern string testJvalueLong(JValue[] args, int size);

        [DllImport("libNative.so", CallingConvention = CallingConvention.Cdecl)]
        [return: MarshalAs(UnmanagedType.LPStr)]
        public static extern string testJvalueFloat(JValue[] args, int size);

        static void Main(string[] args)
        {
            JValue[] args_long = new JValue[1];
            args_long[0].JLong = 92;
            try {
                string reply = testJvalueLong(args_long, 1);
                Console.WriteLine("Long passed, native code returned: " + reply);
            }
            catch(Exception e) {
                Console.WriteLine("Error " + e.ToString());
            }

            JValue[] args_float = new JValue[1];
            args_float[0].JFloat = 18.7F;
            try {
                string reply = testJvalueFloat(args_float, 1);
                Console.WriteLine("Float passed, native code returned: " + reply);
            }
            catch(Exception e) {
                Console.WriteLine("Error " + e.ToString());
            }
        }
    }
}

c++ code:

#include <stdio.h>
#include <jni.h>
#include <iostream>
#include <fstream>
#include <string.h>
#include <cstdlib>
#include <sstream>

typedef unsigned long ULONG;

extern "C" {
        const char * testJvalueLong(jvalue * arg, int size) {
                std::cout << "C++ testJvalueLong" << std::endl;

                std::ostringstream as_string;
                as_string << "C++ success " << arg->j;
                std::string str = as_string.str();
                const char * p = str.c_str();
                ULONG ulSize = strlen(p) + sizeof(char);

                char* pszReturn = NULL;
                pszReturn = (char*)::malloc(ulSize);
                strcpy(pszReturn, p);

                return pszReturn;
        }
        const char * testJvalueFloat(jvalue * arg, int size) {
                std::cout << "C++ testJvalueFloat" << std::endl;

                std::ostringstream as_string;
                as_string << "C++ success " << arg->f;
                std::string str = as_string.str();
                const char * p = str.c_str();
                ULONG ulSize = strlen(p) + sizeof(char);

                char* pszReturn = NULL;
                pszReturn = (char*)::malloc(ulSize);
                strcpy(pszReturn, p);

                return pszReturn;
        }
}

When I run this the output is:

  JLong setter set JValue as a long with 92
  C++ testJvalueLong
  Long passed, native code returned: C++ success 92
  JFloat setter set JValue as a float with 18.7
  Error System.Runtime.InteropServices.COMException (0x8007007A): The data area passed to a system call is too small.
   (0x8007007A)
     at System.StubHelpers.MngdNativeArrayMarshaler.ConvertContentsToNative(IntPtr pMarshalState, Object& pManagedHome, IntPtr pNativeHome)
     at dotnet.Program.testJvalueFloat(JValue[] args, Int32 size)
     at dotnet.Program.Main(String[] args) in /home/vagrant/stack_exchange/dotnet/Program.cs:line 59

However If I comment out the declaration of the char member in the c# structure // [FieldOffset (0)] char c; then it works and the output is this:

  JLong setter set JValue as a long with 92
  C++ testJvalueLong
  Long passed, native code returned: C++ success 92
  JFloat setter set JValue as a float with 18.7
  C++ testJvalueFloat
  Float passed, native code returned: C++ success 18.7

Any help as to why this might be would be greatly appreciated.

c#
c++
linux
.net-core
asked on Stack Overflow Jul 10, 2020 by Hugh Foster • edited Jul 10, 2020 by Hugh Foster

0 Answers

Nobody has answered this question yet.


User contributions licensed under CC BY-SA 3.0