JNA

发布时间 2023-08-18 09:56:22作者: 我不想学编丿程

JNA

嵌套Struct的指针的数组的C函数

现在给大家看看最复杂的Struct的例子。

Struct中嵌套的是一个结构体的指针的数组。

C语言代码


struct CompanyStruct2{
long id;
   wchar_t*  name;

  UserStruct*  users[100];

  // UserStruct   users[100];

   int count;

 

};

MYLIBAPI void sayCompany2(CompanyStruct2* pCompanyStruct);

这里,把刚才使用的UserStruct数组换成UserStruct指针的数组。

JNA代码

     public static class CompanyStruct2 extends Structure{
     public static class ByReference extends CompanyStruct2 implements Structure.ByReference { }

       public NativeLong id;

       public WString  name;
       
       public UserStruct.ByReference[] users=new UserStruct.ByReference[100];

       public int count;
        }

  public void sayCompany2(CompanyStruct2.ByReference  pCompanyStruct);

测试代码:

  CompanyStruct2.ByReference companyStruct2=new CompanyStruct2.ByReference();
  
  companyStruct2.id=new NativeLong(2);

   companyStruct2.name=new WString("Yahoo");

   companyStruct2.count=10;
   
     UserStruct.ByReference pUserStruct=new UserStruct.ByReference();

   pUserStruct.id=new NativeLong(90);

   pUserStruct.age=99;

   pUserStruct.name=new WString("良少");

    pUserStruct.write();

//  TestDll1.INSTANCE.sayUser(pUserStruct);

   for(int i=0;i<companyStruct2.count;i++){

       companyStruct2.users[i]=pUserStruct;

          }
          
       TestDll1.INSTANCE.sayCompany2(companyStruct2);

程序说明----Pin锁住Java对象:
因为是结构体的指针的数组,所以,我们使用了

public UserStruct.ByReference[] users=new UserStruct.ByReference[100];

来对应C语言中的

UserStruct* users[100];

但是,有问题,如果去除

pUserStruct.write();

这一行代码,就会报错。

如果去除pUserStruct.write();

但是使用 TestDll1.INSTANCE.sayUser(pUserStruct);

也不会有问题。

这是怎么回事?

原来,错误的原因就是内存锁定!

java内存锁定机制和JNI,JNA调用
我们知道,java的内存是GC管理的。它会自动管理JVM使用的堆内存。删除不再使用的内存,并把Java对象使用的内存自动移动,以防止内存碎片增多。

因此,虽然Java的引用实际上就是指针,但还是和指针不同。Java中不能直接使用指针。因为对象在内存中的地址都不是固定的。说不准什么时候GC就把它给移位了。

如果使用JNI,JNA等技术调用C函数。那么就会有问题。因此,C语言使用的是指针。如果想要获取C函数的返回值。那么我们必须提供一块内存给C语言访问。而C语言是不知道你Java的引用的。它只能访问固定的内存地址。

如果GC把Java对象移来移去,那么C函数就没办法和Java交互了。

因此,在使用JNI和JNA时,都会把Java对象锁住。GC不再管理。不删除,也不移动位置。由此出现的内存碎片,也不管了!

这种技术的术语是PIN。 .NET也有同样的概念。

上面TestDll1.INSTANCE.sayUser(pUserStruct);这个调用是JNA调用。这个操作就把pUserStruct这个Java对象锁住了。

例4中,嵌套的是结构体,因此也会直接把CompanyStruct类的实例锁住。

但是例5中,嵌套的是结构体的指针的数组。 CompanyStruct2类的实例companyStruct2在执行

TestDll1.INSTANCE.sayCompany2(companyStruct2);

时是被锁住了,可以companyStruct2 的users 指针数组 的成员:

pUserStruct 都没有被锁住。

怎么办呢?

难道每一个UserStruct.ByReference 的实例都先调用一遍不需要的

TestDll1.INSTANCE.sayUser(pUserStruct); 方法?

没事!JNA开发人员早已想到了:

Structure 类中有方法:

write
public void write()
Writes the fields of the struct to native memory

writeField
public void writeField(String name)
Write the given field to native memory. The current value in the Java field will be translated into native memory.

Throws:

IllegalArgumentException - if no field exists with the given name

这些write方法,会把Java的内存Pin住。   就是C语言可以使用。GC不再管理它们。

现在只要调用

pUserStruct.write();

把java模拟结构体实例给Pin住就可以了。

题外话,C#定义了语法,可以使用关键字 pin 锁住.NET对象。

————————————————
版权声明:本文为CSDN博主「良少」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shendl/article/details/3599849

Buffers,Memory,数组和 Pointer ByReference

  • Pointer和ByReference都可以在JNA项目中用来地址传递参数,Pointer的使用方式更像C/C++的语言结构,自己分配内存空间,自己释放。ByReference则是完完全全的java语法,只要用就行了,内存通过垃圾回收完成。
    所以总的来讲ByReference是更好的,但是对于更多层的指针引用,可能Pointer更合适。
    ————————————————
    版权声明:本文为CSDN博主「cy谭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/zhan107876/article/details/121058925

Pointer

三、指针参数Pointer
那么如何实现类似C语言那样的地址传递,或者说指针传递呢?在JNA框架中,我们可以借助一个类完成,他就是Pointer。
com.sun.jna.Pointer,指针数据类型,用于匹配转换映射函数的指针变量。

1、定义
Pointer c = new Memory(50);
Pointer msg = new Memory(50);
1
2
说明:
这样的指针变量定义很像C的写法,就是在定义的时候申请空间。比如这里就申请50个空间
根据测试结果对于字符串,一个空间对于两个字符左右。如果返回的结果长度比分配的空间大,则会报错

Exception in thread “main” java.lang.IndexOutOfBoundsException: Bounds exceeds available space : size=6, offset=8

最后可以这样释放申请的空间

Native.free(Pointer.nativeValue(c)); //手动释放内存
Pointer.nativeValue(c, 0); //避免Memory对象被GC时重复执行Nativ.free()方法
Native.free(Pointer.nativeValue(msg));
Pointer.nativeValue(msg, 0);
————————————————
版权声明:本文为CSDN博主「cy谭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhan107876/article/details/121056384

结构体数组

创建方法

当遇到C++函数中的入参出现结构体数组,或者结构体中出现结构体数组的时候,使用常用的方法给数组中每一项new一下是错误的。

LOGIN_INFO_S [] stu = new LOGIN_INFO_S [3]; 
for(int i = 0; i < 3; i ++)
{
   stu[i] = new LOGIN_INFO_S (); 
} 

这种方法是错误的,因为用这种方法new出来的内存不一定是连续的,到了C++那边就乱了。正确的方法是调用jna库中structure类的toArray方法:

LOGIN_INFO_S [] stu = new LOGIN_INFO_S [3]; 
stu = (LOGIN_INFO_S [])(new LOGIN_INFO_S()).toArray(3); 
该方法申请了一段连续的内存。 

版权声明:本文为CSDN博主「呆萌的我」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zjutzmh/article/details/53574504

类型映射

c java
bool int

void、void*、char、char、int等类型映射关系及简单示例

image-20230811172756555

https://blog.csdn.net/ctwy291314/article/details/84626829

修改字节对齐

重写方法 获取 字节对齐方法

Structure alignment and type mappings are derived by default from the enclosing interface definition (if any) by using Native.getStructureAlignment(java.lang.Class<?>) and Native.getTypeMapper(java.lang.Class<?>). Alternatively you can explicitly provide alignment, field order, or type mapping by calling the respective Structure functions in your subclass's constructor.


# 该方法被用来 计算字节对齐 

fieldAlignment = getNativeAlignment(nativeType, value, firstField);  
 @Override
    protected int getNativeAlignment(Class<?> type, Object value, boolean isFirstElement) {
        int nativeAlignment = super.getNativeAlignment(type, value, isFirstElement);
        return Math.min(nativeAlignment,4);
    }