[英]array data not marshaled correctly for structures from c# to c within Linux embedded mono program
I've built on the Mono Embed sample to try and invoke a method in a C# assembly that updates a structure. 我以Mono Embed示例为基础,尝试并调用C#程序集中的方法来更新结构。 The structure has 1 int array.
该结构具有1个int数组。 This is on a Linux system.
这是在Linux系统上。
Accessing the int array field in c# results in a segmentation fault. 在c#中访问int array字段会导致分段错误。 Just checking if the field is null is enough to cause the fault.
仅检查该字段是否为空就足以导致故障。
When I do internal marshaling simulation within C#, converting the struct to bytes and then back to a struct this works fine. 当我在C#中进行内部封送处理模拟时,将结构转换为字节,然后再转换回结构,即可正常工作。
Mono Version: 3.2.3 单声道版本:3.2.3
I have included the c# and c code below and can furnish more information upon request if need be. 我已经在下面包含了c#和c代码,并且可以根据需要提供更多信息。
Here's the c code... 这是C代码...
#include <mono/jit/jit.h>
#include <mono/metadata/object.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <string.h>
#include <stdlib.h>
#ifndef FALSE
#define FALSE 0
#endif
struct STRUCT_Test
{
int IntValue1[2];
};
int
main (int argc, char* argv[]) {
MonoDomain *domain;
MonoAssembly *assembly;
MonoClass *klass;
MonoObject *obj;
MonoImage *image;
const char *file;
int retval;
if (argc < 2){
fprintf (stderr, "Please provide an assembly to load\n");
return 1;
}
file = argv [1];
domain = mono_jit_init (file);
assembly = mono_domain_assembly_open(domain, file);
if (!assembly)
exit(2);
image = mono_assembly_get_image(assembly);
klass = mono_class_from_name(image, "StructTestLib", "StructReader");
if (!klass) {
fprintf(stderr, "Can't find StructTestLib in assembly %s\n", mono_image_get_filename(image));
exit(1);
}
obj = mono_object_new(domain, klass);
mono_runtime_object_init(obj);
{
struct STRUCT_Test structRecord; memset(&structRecord, 0, sizeof(struct STRUCT_Test));
void* args[2];
int val = 277001;
MonoMethodDesc* mdesc = mono_method_desc_new(":ReadData", FALSE);
MonoMethod *method = mono_method_desc_search_in_class(mdesc, klass);
args[0] = &val;
args[1] = &structRecord;
structRecord.IntValue1[0] = 1111;
structRecord.IntValue1[1] = 2222;
mono_runtime_invoke(method, obj, args, NULL);
printf("IntValue1: %d, %d\r\n", structRecord.IntValue1[0], structRecord.IntValue1[1]);
}
retval = mono_environment_exitcode_get ();
mono_jit_cleanup (domain);
return retval;
}
Here's the c# code... 这是C#代码...
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace StructTestLib
{
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct STRUCT_Test
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public Int32[] IntValue1;
}
public class StructReader
{
public void ReadData(int uniqueId, ref STRUCT_Test tripRecord)
{
if (tripRecord.IntValue1 != null)
Console.WriteLine("IntValue1: " + tripRecord.IntValue1[0] + ", " + tripRecord.IntValue1[1]);
else
Console.WriteLine("IntValue1 is NULL");
tripRecord.IntValue1[0] = 3333;
tripRecord.IntValue1[1] = 4444;
}
}
}
Oops! 糟糕! My Ignorance!
我的无知!
Seems that my understanding of the marshaling was incorrect. 看来我对封送的理解不正确。 Raw array based data types (string, long[]) cannot be marshaled directly.
基于原始数组的数据类型(string,long [])无法直接封送。 The c structure has to have the Monoxxx* type as the member for the runtime to marshal correctly.
c结构必须具有Monoxxx *类型作为成员,以便运行时正确地封送。
Using MonoString* StringValue1 instead of char StringValue1[31] and MonoArray* IntArray instead of int IntArray[2] allows the marshaling to work correctly. 使用MonoString * StringValue1代替char StringValue1 [31]和MonoArray * IntArray代替int IntArray [2]可以使封送处理正常工作。
Here's what I ultimately ended up with I really needed to pass in the raw structure from c without all the "mono" baggage within the structure, I'm trying to use existing c structures without changing them. 这就是我最终要得到的结果,我真的需要从c中传递原始结构,而不必在结构中包含所有“单”行李,我试图使用现有的c结构而不更改它们。 I was able to do this by using "unsafe" c# code and allowing the address of the structure itself to be passed into the c# method.
通过使用“不安全的” c#代码并将结构本身的地址传递到c#方法中,我能够做到这一点。 This allows the raw memory to be manipulated within c# and gives full freedom for the c# marshaler to convert bytes to struct and vice versa.
这样就可以在c#中操纵原始内存,并为c#封送处理程序提供了将字节转换为struct的完全自由权,反之亦然。
c# code C#代码
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using EmpireCLS.Comm;
namespace StructTestLib
{
[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct STRUCT_Test
{
public Int32 IntValue1;
public Int32 IntValue2;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 20)]
public string StringValue1;
}
public class StructReader
{
unsafe public void ReadDataRaw(int uniqueId, void* tripRecordPtr)
{
STRUCT_Test tripRecord = (STRUCT_Test)Marshal.PtrToStructure((IntPtr)tripRecordPtr, typeof(STRUCT_Test));
tripRecord.IntValue1 = 3333;
tripRecord.IntValue2 = 4444;
Console.WriteLine("c# StringValue1: " + tripRecord.StringValue1);
tripRecord.StringValue1 = "fghij";
GCHandle pinnedPacket = new GCHandle();
try
{
int structSizeInBytes = Marshal.SizeOf(typeof(STRUCT_Test));
byte[] bytes = new byte[structSizeInBytes];
pinnedPacket = GCHandle.Alloc(bytes, GCHandleType.Pinned);
Marshal.StructureToPtr(tripRecord, pinnedPacket.AddrOfPinnedObject(), true);
Marshal.Copy(bytes, 0, (IntPtr)tripRecordPtr, bytes.Length);
}
finally
{
if (pinnedPacket.IsAllocated)
pinnedPacket.Free();
}
}
}
}
c code C代码
#include <mono/jit/jit.h>
#include <mono/metadata/object.h>
#include <mono/metadata/environment.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/debug-helpers.h>
#include <string.h>
#include <stdlib.h>
#ifndef FALSE
#define FALSE 0
#endif
struct STRUCT_Test
{
int IntValue1;
int IntValue2;
char StringValue1[20];
};
int
main (int argc, char* argv[]) {
MonoDomain *domain;
MonoAssembly *assembly;
MonoClass *klass;
MonoObject *obj;
MonoImage *image;
const char *file;
int retval;
if (argc < 2){
fprintf (stderr, "Please provide an assembly to load\n");
return 1;
}
file = argv [1];
domain = mono_jit_init (file);
assembly = mono_domain_assembly_open(domain, file);
if (!assembly)
exit(2);
image = mono_assembly_get_image(assembly);
klass = mono_class_from_name(image, "StructTestLib", "StructReader");
if (!klass) {
fprintf(stderr, "Can't find StructTestLib in assembly %s\n", mono_image_get_filename(image));
exit(1);
}
obj = mono_object_new(domain, klass);
mono_runtime_object_init(obj);
{
struct STRUCT_Test structRecord; memset(&structRecord, 0, sizeof(struct STRUCT_Test));
void* args[2];
int val = 277001;
char* p = NULL;
MonoMethodDesc* mdesc = mono_method_desc_new(":ReadDataRaw", FALSE);
MonoMethod *method = mono_method_desc_search_in_class(mdesc, klass);
args[0] = &val;
args[1] = &structRecord;
structRecord.IntValue1 = 1111;
structRecord.IntValue2 = 2222;
strcpy(structRecord.StringValue1, "abcde");
mono_runtime_invoke(method, obj, args, NULL);
printf("C IntValue1: %d, %d\r\n", structRecord.IntValue1, structRecord.IntValue2);
printf("C StringValue: %s\r\n", structRecord.StringValue1);
}
retval = mono_environment_exitcode_get ();
mono_jit_cleanup (domain);
return retval;
}
尝试将StringValue1
传递为字符数组,因为这实际上是您在C程序中定义的值。
mono_runtime_invoke() doesn't do any type marshalling (same if you go the other way around and use internal calls). mono_runtime_invoke()不会进行任何类型的封送处理(如果您采用其他方法并使用内部调用,则与此相同)。
Only P/Invoke methods perform data marshalling. 仅P / Invoke方法执行数据编组。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.