c# - 在 Linux 嵌入式单声道程序中,数组数据未正确编码为从 c# 到 c 的结构

标签 c# linux mono embedded

我在 Mono Embed 示例的基础上尝试调用 C# 程序集中的方法来更新结构。该结构有 1 个 int 数组。这是在 Linux 系统上。

在 C# 中访问 int 数组字段会导致段错误。仅检查该字段是否为 null 就足以导致错误。

当我在 C# 中进行内部编码模拟时,将结构转换为字节,然后再转换回结构,这工作正常。

单声道版本:3.2.3

我在下面包含了 c# 和 c 代码,如果需要,可以根据要求提供更多信息。

这是 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;
}

这是 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;
        }
    }
}

最佳答案

糟糕!我的无知!

看来我对编码的理解不正确。基于原始数组的数据类型(字符串、长[])不能直接编码。 c 结构必须具有 Monoxxx* 类型作为运行时正确编码的成员。

使用 MonoString* StringValue1 而不是 char StringValue1[31] 和 MonoArray* IntArray 而不是 int IntArray[2] 允许编码正常工作。

这是我最终得到的结果 我真的需要从 c 传递原始结构,而结构中没有所有“单声道”包袱,我正在尝试使用现有的 c 结构而不更改它们。我能够通过使用“不安全”的 c# 代码并允许将结构本身的地址传递到 c# 方法中来做到这一点。这允许在 c# 中操作原始内存,并为 c# 编码(marshal)拆收器提供充分的自由,将字节转换为结构,反之亦然。

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 代码

#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;
}

关于c# - 在 Linux 嵌入式单声道程序中,数组数据未正确编码为从 c# 到 c 的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19503860/

相关文章:

c# - API 函数 AllocConsole 和 AttachConsole(-1) 之间有什么不同?

c# - 控制台应用程序和单元测试方法之间的不同垃圾收集行为

linux - Git pull/取自动化

c# - 为什么单声道有时会截断 http 下载?

.net - 使用 MONO_IOMAP 来处理硬编码文件路径?

c# - 'Group By'怎么能上一个ultrawingrid列呢?

c# - 从代码执行时 Handle.exe 不工作

java - 如何解决延迟创建文件?

php - 目录不是由 php 代码在 linux 服务器中创建的

c# - mono_field_set_value 不起作用