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.
User contributions licensed under CC BY-SA 3.0