做一个项目,第三方提供了一系列的C++函数接口。
现在我在用vc.net2005封装这些函数,即自己再定义一个c函数来调用这些接口。
例如:
(1).第三方C++函数接口为 int api_get_logfile(Struct fileinfo tfile),参数是个结构体,且套了另一个结构体:struct fileinfo{
 char *fullpath;
 int  filesize;
 int  filemode;
 struct disnode *tnode;
};struct disnode{
 int  number;
 struct {
   int node;
   int stat;
 }s[number];
};问题:这个结构体我该做一个class来对应吗?怎么写呢?里面有个结构体数组,头痛ing 
我做一个JNIEXPORT jint JNICALL Java_N_api_get_logfile(JNIEnv *env, jobject, jobject)的封装函数
怎么把这个class和参数tfile这个结构体对应传递?(2)另一个接口 int api_set_cvltgr(HWND hwnd, UINT ut, LPARAM lm),这个参数对应的java类型是什么?怎么传递?(3)另一个接口 Struct fileinfo *api_get_inifile(char *name), 这个接口返回值是个结构体指针,JNI函数里怎么写啊?小弟初学JNI,java项目也是第一次,很多地方没有头绪,网上的资料都是一些最基本的类型参数,这种复杂的求达人们指教,万分感谢!

解决方案 »

  1.   

    第(3)个问题现在自己想到的方案是,接口函数的返回值是结构体指针,然后在封装函数里将结构体指针的结构体内容再赋值到对应的class中,再返回给java这个class对象。是不是这样的思路?
      

  2.   

    其实你的三个问题可以说都是同一个问题,java中没有结构体的概念,对于结构体你可以建立一个class与其对应,但是转化过程必须自己写。
    例如你的结构体为fileinfo,那么你就可以建立一个FileInfo类对应他,但是在C中必须自己转化,也就是传入一个FileInfo的对象,然后对FileInfo对象的属性通过fileinfo结构体逐一复制。
    又或者你不建立class与其对应,多建立几个String类型来接受结构体中的各个信息也可以。
    但是java是不支持char *这种形式的字符串的,所以你必须把C中字符串表示方式转换成java的String,java中String类型在c中表示形式其实就是jstring类型,它本身是一个复杂类型(jobject类型),实际为执行内存的指针。
    对于如何把char*转变成jstring,你可以查看jni相关文档,jni有很多内置函数实现这些功能。
    对于复杂类型的转换,如结构体转换成jobject,需要自己手动转换。java不直接支持结构体特性,因为java的jni不是仅仅去支持C的。
      

  3.   

    也就是传入一个FileInfo的对象,然后对FileInfo对象的属性通过fileinfo结构体逐一复制这部分我理解了,查了资料,貌似可以这样做:
    //通过GetObjectClass方法得到这个对象
     jclass objectClass = (env)->GetObjectClass(obj);
    //通过GetFieldID方法得到这个对象的fsize属性
    jfieldID fs = (env)->GetFieldID(objectClass,"filesize","I");
    //再通过SetIntField()方法把这个属性和结构体一一对应赋值对吗?类似于下面这样:
    (env)->SetIntField(obj,fs,40);
    不过有个地方不知道具体怎么做:
    如果这个FileInfo对象的有个属性是一个数组的话,通过GetFieldID()这个JNI函数怎么取得这个数组?
    又怎么把这个数组和结构体内的数组对应起来呢?
      

  4.   

    JNI里面也有专门处理数组的函数,其实如果你足够了解java的话也应该知道数组也是一个object,其签名为[xtype的形式,其中x为[的数量,type为数组类型,例如[[i相当于int[][],[[Ljava/util/Date相当于Date[][]。所以数组本身也可以用GetObjectClass来获得,但是处理数组元素需要专门的jni内置函数来处理。
    最后提醒你一句,一定要记得回收内存噢。jni的字符串与数组最容易出现内存泄漏。
      

  5.   

    还是不明白怎么获取对象的数组属性例如FileInfo类有个属性是 int ss[5]:
    public class FileInfo{
       public int filesize;
       public int ss[] = new int[5];
    }
    在JNI的封装中:jclass objectClass = (env)->GetObjectClass(obj);  //obj就是FileInfo类的实际对象
    jfieldID fs = (env)->GetFieldID(objectClass,"filesize","I");  //可以获取到int型的filesize这个属性了
    jfieldID ss = (env)->GetFieldID(objectClass,"ss","[I");      //这样是不是可以获取到int型的数组ss了?如果可以的话,怎么对这个ID ss进行实际的访问操作? 
     
      

  6.   

    jfieldID fs = (env)->GetFieldID(objectClass,"filesize","I");
    这样只是获得对象字段的句柄指针,并没有获得字段的值,要获得字段的值还需要其它操作,例如:
    jint jfs=(*env)->GetObjectField(env,cl,fs);
    如果获得的是jstring类型或者是数组类型还需要进行转换,转换成char[]类型或者char*类型,例如
    char* str=(*env)->GetStringUTFChars(env,jstr,NULL);
    如果是基本数据类型数组的话,访问其内部元素用GetXxxArrayElements(env,jarray,0);函数Xxx是类型名称,返回Xxx*类型,
    如int * i=GetIntArrayElements(env,jint,0)
    如果是对象型数组就是jobject jobj=GetObjectArrayElement(env,jobjectArray,jsize),jobject是数组变量,jsize是偏移量,返回值是数组中对应偏移量的值。
      

  7.   

    lz你弄反了,
    你应该先写一个java 调用本地文件dll的接口,
    然后通过java的jni工具生成C的头文件,
    然后你再去实现相应的C/C++函数。
      

  8.   

    现在的具体问题是:java用一个类作为参数传递给C++的函数,C++内部声明了一个结构体来对应这个类的属性
    类中有个属性是个int型的数组:int info[30]
    在JNI封装函数中这样写:
    //取得类对象
    jclass objectClass = (env)->GetObjectClass(sobj);  //sobj即传递进来的类的对象
    //取得该数组属性的ID
    jfieldID jarr_info = (env)->GetFieldID(objectClass,"info","[I"); //参数“[I”表示这个是int型数组
    //通过ID取得具体的数组
    jintArray jintarr = (jintArray)(env)->GetObjectField(obj,jarr_info); //参数obj是该封装函数自动参数(JNIEnv *env, jclass obj)中的jclass//以上部分执行没有问题,问题出在下面语句://要对数组进行操作,要取得该数组的数组指针,通过JNI的函数GetIntArrayElements()来取得:
    jint *pia = (env)->GetIntArrayElements(jintarr,0);  //这句出问题了我在调试时,在上述每句语句后都紧跟一句printf的输出语句,到上面这句,紧跟的输出语句没有出现,所以应该是这句语句出现了问题。
    可我不明白错在哪里,请达人指点!
      

  9.   

    用Java native access吧,或者参考里面的example
      

  10.   

    如果有第三方的头文件,直接#include该头文件,然后直接使用该结构体
    如果没有,就自己定一个结构体
    struct fileinfo{ 
    char *fullpath; 
    int  filesize; 
    int  filemode; 
    struct disnode *tnode; 
    }; struct disnode{ 
    int  number; 
    struct { 
      int node; 
      int stat; 
    }s[number]; 
    }; for example
    struct fileinfo ff;
    memcopy(obj, ff);
    printf("%s",ff.fullpath);
      

  11.   

    jint *pia = (env)->GetIntArrayElements(env, jintarr,0); //your parameter is wrong
      

  12.   


    没错啊,C++里前面用(env)的话,后面就两个参数了。这个不错的,不然编译不通过的。
    现在是编译通过,执行出错。
      

  13.   


    请问memcpy的参数obj是指什么?如果指的是传递进来的类对象的话,我试了,结构体内容这样赋值给类对象是不行的呀
      

  14.   

    恩,我刚才把jni.h头文件看了一下了,里面对JNIEnv结构体的定义,就是把JNINativeInterface的方法封装了
    如果你是在C++的集成环境下调试的话,你看看每一步的值是否都对了?
    我现在看你的代码也看不出问题
      

  15.   

    我是看了你的jclass objectClass = (env)->GetObjectClass(obj); 这个写的
    这个obj就是传进来的FileInfo的instance
    memcpy(obj, ff, sizeof(fileinfo))
    用这个方法还有个问题,java和C++的类型长度不一定一样,
    所以估计还是要一个一个的属性去设置
    当然,如果能保证类型长度一样的话,用memcpy最方便
      

  16.   


    JNI涉及了java和C++两部分语言,目前我可以调试java部分,C部分没有办法调试,所以才在每一句JNI函数后加一个输出语句,就是为了判断哪里会出现问题的。这个东东还真是搞死人了呀。。
      

  17.   

    jclass objectClass = (env)->GetObjectClass(sobj);  //sobj
    ...
    jintArray jintarr = (jintArray)(env)->GetObjectField(obj,jarr_info); //0bj->sobj
      

  18.   

    http://www.blogjava.net/orangewhy/archive/2007/05/24/119645.html
      

  19.   

    class中有个属性为String类型:
    public class FileInfo{
       public String name; 
    }JNI中://取得类对象
    jclass objectClass = (env)->GetObjectClass(sobj);  //sobj即传递进来的类的对象:new FileInfo()
    //取得该String属性的ID
    jfieldID jname = (env)->GetFieldID(objectClass,"name","Ljava/lang/String;"); //参数“Ljava/lang/String;”表示这个是String型
    //取得该属性的具体值:
    jstring js = (env)->GetObjectField(sobj,jname);  //这里报错!错误提示说不能将jobject型转为jstring型,这个怎么办?
    查网上的例子,说jstring型就是jobjcet型啊,为什么不行?
      

  20.   


    强行转换呢
    jstring js = (jstring)(env)->GetObjectField(sobj,jname);  //这里报错!
      

  21.   

    最新的一个问题是:
    参数是一个类对象数组,传递进C++函数后,先将对应的结构体赋值,然后改变结构体的值,再返回给这个对象数组,最后通过java打印出该返回数组的每个属性值。结构体定义如下:struct fileinfo{ 
    char *fullpath; 
    int  filesize; 
    struct disnode *tnode; 
    }; struct disnode{ 
    int  number;  
    int stat[32];
    }; java类定义如下:public class FileInfo{
      public String fullpath;
      public int filesize;
      public DisNode dn = new DisNode();
    }public class DisNode{
      public int number;
      public int[] stat = new int[32];
    }JNI封装函数定义:JNIEXPORT jint JNICALL Java_TestJni_makefiles
    (JNIEnv *env, jclass obj,jobjectArray objAry)
    请问如下问题:
    1.jfieldID是对象数组中的每个类对象都要提取一次,还是只要取一个对象,大家都可以用?
    2.传递入的是对象数组,那对应赋值给结构体的时候,结构体也必须是数组,那循环赋值时类对象如何一一提取出来?
    3.结构体值被更改后,赋值回对象数组,该数组如何返回?
    以上最好有简单的例子,谢谢!
      

  22.   

    我自己试了一下,jobject sobj = (env)->GetObjectArrayElement(objAry,0);//get java instance
    jclass objectClass = (env)->GetObjectClass(sobj);  //编译通过,但执行错误
    用GetObjectArrayElement函数从数组得到的object无法用GetObjectClass函数进行class类型提取,求解!
      

  23.   

    Java 深度探索者 QQ群: 65670864
      

  24.   

    楼主,如果 c++中有一方法获取了DisInfo d的一个对象,我想把这个d中的值传给 java中的实例类 DisInfo djava,那怎么实现?
    JNIEXPORT jobject JNICALL Java_com_sundy_jnidemo_ChangeMethodFromJni_getStruct   
    JNIEnv *env, jobject obj)   
    {
     DisInfo d=Java_com_sundy_jnidemo_Test_GetDisInfo();
     //在这里如何把d中的值给obj?
    }
      

  25.   

    建议LZ可以使用JNA,也就是JNI的扩展包。省去了自己写C++程序的问题。JNA的唯一不足就是无法用于C调用Java,而JNI却可以。另外,Java与C之间的调用,接口函数的参数最好不要使用指针形式,用TCHAR数组作为参数类型,传递比较方便。