使用包含 char* 的结构进行无效内存访问

Invalid memory access with Structure containing char*

我正在尝试使用 JNA 映射包含一些 char * 字段的结构。我收到以下异常:

Exception in thread "main" java.lang.Error: Invalid memory access

这是我的 C 代码:

调用函数的签名

extern "C" MATHLIBRARY_API ERR ErrorTest(); 

我做的 C 示例实现作为示例:

ERR sngErrorTest() {
    ERR err;
    char message[] = "My message";
    char filename[] = "My file Name";

    err.errorCode = OK;
    err.errorDetails.fileName = NULL;
    err.errorDetails.lineNumber = 1;
    err.errorDetails.message = message;
    err.errorDetails.fileName = filename;
    return err;
}

C 类型定义:

typedef enum {
    OK = 0, 
    ERR_ONE,
    ERR_TWO,
    ERR_THREE,
    ERR_FOUR,
    ERR_FIVE,
    ERR_SIX,
} ERR_CODE;

typedef struct {
    char *fileName; 
    char *message; 
    int lineNumber; 
} ERR_DETAILS;

typedef struct {
    ERR_CODE errorCode; 
    ERR_DETAILS errorDetails; 
} ERR;

这是我的 java 代码:

public interface IFunctions extends Library {
    public static class ERR_DETAILS extends Structure {
        public static class ByValue extends ERR_DETAILS implements Structure.ByValue {}
        public static class ByReference extends ERR_DETAILS implements Structure.ByReference {}
        public Pointer fileName; 
        public Pointer message; 
        public int lineNumber; 
        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] { "fileName", "message","lineNumber"});
        }
    }
    public static class ERR_CODE {
        public static int OK = 0; 
        public static int ERR_ONE = 1;
        public static int ERR_TWO = 2;
        public static int ERR_THREE = 3;
        public static int ERR_FOUR = 4;
        public static int ERR_FIVE = 5;
        public static int ERR_SIX = 6;

    }
    
    public static class ERR extends Structure {
        public static class ByValue extends ERR implements Structure.ByValue {}
        public static class ByReference extends ERR implements Structure.ByReference {}
        public ERR_CODE errorCode;
        public ERR_DETAILS errorDetails;
        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList(new String[] { "errorCode", "errorDetails"});
        }
    }
    
    public ERR ErrorTest();
    }

这是我的 java 测试主要 class :

ERR err = IFunctions.ErrorTest();
  1. 关于 char * 类型,我用 String 替换了 Pointer,但我遇到了同样的问题。
  2. 一旦我将 fileName 项检索为指针,我如何获取 fileName 引用的值?是 err.errorDetails.fileName.getString(0); 吗?

因此,我试图隔离每种类型的结构,并创建了以下 C 函数作为示例:

签名:

    extern "C" MATHLIBRARY_API ERR_CODE ErrorCode();

C 实现:

ERR_CODE ErrorCode() {
    ERR_CODE err_code;
    err_code = OK;
    return err_code;
}  

Java声明

public interface IFunctions extends Library {
....
public ERR_CODE ErrorCode();
....
}

调用我的 jna 函数:

ERR_CODE errCode = IFunctions.ErrorCode();

在那种情况下,我得到以下错误: 线程“main”中的异常 java.lang.IllegalArgumentException: 函数 ErrorCode

中不支持 return 类型 class ERR_CODE

我是不是忘记了关于枚举类型定义的一些想法?

问题出在 ERR_CODE 枚举的映射中。

JNA 没有 enum 类型的直接映射,因为这些类型通常是 32 位整数。但是,您已将其映射为没有任何 JNA 映射的普通 java class(扩展对象)。

在 JNA 中映射 enum 的常规方法是将 int 值包装在 interface 中。 (这只是为了自记录代码。实际上,您真正需要的只是 int 值。)

所以您应该将 ERR_CODE 映射更改为:

interface ERR_CODE {
    int OK = 0; 
    int ERR_ONE = 1;
    int ERR_TWO = 2;
    int ERR_THREE = 3;
    int ERR_FOUR = 4;
    int ERR_FIVE = 5;
    int ERR_SIX = 6;
}

请注意,publicstatic 修饰符对于接口成员来说是多余的。

那么您还应该将 ERR 结构映射更改为:

@FieldOrder({"errorCode", "errorDetails"})
class ERR extends Structure {
    public int errorCode; // ERR_CODE
    public ERR_DETAILS errorDetails;
}

注意我刚刚将 enum 映射为 int,使用注释字段表示 int 代表的特定类型(枚举)。

这里可能存在对齐问题,这可能与平台有关。通常最大字节大小用于对齐,并且由于 ERR_DETAILS 映射中包含 8 字节指针,因此 4 字节 int 和下一个元素的字节边界之间可能存在未对齐(例如,errorDetails 可能从 0x8 而不是 0x4 的偏移量开始。要解决此问题,您可能需要在结构中设置对齐类型,将映射更改为:

@FieldOrder({"errorCode", "errorDetails"})
class ERR extends Structure {
    public int errorCode; // ERR_CODE
    public ERR_DETAILS errorDetails;

    public ERR() {
        super();
        setAlignType(ALIGN_NONE);
    }
}

另请注意,我还删除了不必要的 ByValueByReference subclasses。您很少需要定义它们——在结构内部,ByValue 是默认值,在方法参数中 ByReference 是默认值,您只需要在不使用默认值的极少数情况下定义它们。

我还使用了 JNA 5.x @FieldOrder 注释而不是覆盖方法。

您可以在 char * 的结构映射中使用 String 而不是 Pointer,尽管 PointergetString() 是正确的处理方式如果您选择保留该映射:

@FieldOrder({"fileName", "message", "lineNumber"})
class ERR_DETAILS extends Structure {
    public String fileName; 
    public String message; 
    public int lineNumber; 
}

我的问题终于得到了回复。

在我的 JNA 包装器中,我必须明确定义我的函数 return 是一个值结构,而不是 JNA 默认完成的引用结构。 如 JNA API 中关于结构 class 所述: “当用作函数参数或return值时,此class对应于struct*。 当用作另一个结构中的字段时,它对应于结构。 标记接口 Structure.ByReference 和 Structure.ByValue 可用于更改默认行为。"

于是解决方案如下:

public interface IFunctions extends Library {
...
public ERR.ByValue ErrorTest();
}

没有其他要更改的内容。 感谢您的帮助。