jni. Чтение в C++ методе поля вложенного java объекта
От: TnedutS  
Дата: 10.12.09 15:36
Оценка:
Всем привет!
Коротко проблема обрисована в теме. Сама задача является тестовой "чтобы разобраться".
Теперь подробнее.
В java — объекте класса HelloWorld есть нативный метод print, который должен вывести на экран содержимое поля a объекта TestObject, который в свою очередь является полем cfvjобъекта класса HelloWorld


class HelloWorld {
  public class TestClass{
    public String a = "abc";
  }
  public TestClass TestObject;
  private native void print();
  public static void main(String[] args) {
  new HelloWorld().print();
  }
  static {
    System.loadLibrary("HelloWorldLib");
  }
}


Нативный метод на c++ определен в dll-ке HelloWorld и выглядит так.
Работающая часть метода списана из документации

#include <stdio.h>
#include "jni.h"
#include "HelloWorld.h"

JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *env, jobject obj)
{
  jfieldID fid1,fid2; /* store the field ID */
  jobject jEmbObj;
  jstring jstr;
  const char *str;

  jclass cls1, cls2;
  /* Get a reference to obj's class */
  cls1 = (*env)->GetObjectClass(env, obj);

  /* Look for the instance field s in cls */
  fid1 = (*env)->GetFieldID(env, cls1, "TestObject",
  "LHelloWorld$TestClass;");
  if (fid1 == NULL) {
    return; /* failed to find the field */
  }

  // Read the instance field TestObject
  jEmbObj = (*env)->GetObjectField(env, obj, fid1);
  // Get a reference to obj's class
  cls2 = (*env)->GetObjectClass(env, jEmbObj);//***
  // Look for the instance field a in cls
  fid2 = (*env)->GetFieldID(env, cls2, "a",
  "Ljava/lang/String;");

  if (fid2 == NULL) {
    return; // failed to find the field
  }

  // Read the instance field s
  jstr = (*env)->GetObjectField(env, jEmbObj, fid2);
  str = (*env)->GetStringUTFChars(env, jstr, NULL);
  if (str == NULL) {
    return; // out of memory
  }
  printf(" c.s = \"%s\"\n", str);
  (*env)->ReleaseStringUTFChars(env, jstr, str);

  return;
}


В этой строке с помощью GetObjectClass я хотел получить класс вложенного объекта, а потом дальше по аналогии GetFieldID — GetObjectField — GetStringUTFChars получить доступ к полям уже вложенного объекта.
По-моему здесь http://rsdn.ru/forum/java/3366520.aspx
Автор: denis.zhdanov
Дата: 21.04.09

как раз такая идея и изложена.
Похожая идея и тут.
http://www.experts-exchange.com/Programming/Languages/Java/Q_11830578.html


Dll-ка компилируется.
Однако при использовании в java сразу возникают ошибка.
#
# An unexpected error has been detected by HotSpot Virtual Machine:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x6d6c5b38, pid=3868, tid=3888
#
# Java VM: Java HotSpot(TM) Client VM (1.5.0_03-b07 mixed mode)
# Problematic frame:
# V [jvm.dll+0x85b38]
#
# An error report file with more information is saved as hs_err_pid3868.log
#
# If you would like to submit a bug report, please visit:
# http://java.sun.com/webapps/bugreport/crash.jsp
#

Проблемы возникают со строкой ***. Если ее и все ниже комментировать, ошибок не возникает.
Вопрос: как получить доступ к полям вложенных объектов?
Причем в перспективе требуется не только чтение.
Си часть должна считывать информацию(структуры различные) из файла и передавать в java.
Re: jni. Чтение в C++ методе поля вложенного java объекта
От: alsemm Россия  
Дата: 10.12.09 16:07
Оценка: 5 (2) +1
Здравствуйте, TnedutS, Вы писали:

TS>Всем привет!

TS>Коротко проблема обрисована в теме. Сама задача является тестовой "чтобы разобраться".
TS>Теперь подробнее.
TS>В java — объекте класса HelloWorld есть нативный метод print, который должен вывести на экран содержимое поля a объекта TestObject, который в свою очередь является полем cfvjобъекта класса HelloWorld


TS>
TS>class HelloWorld {
TS>  public class TestClass{
TS>    public String a = "abc";
TS>  }
TS>  public TestClass TestObject;
TS>  private native void print();
TS>  public static void main(String[] args) {
TS>  new HelloWorld().print();
TS>  }
TS>  static {
TS>    System.loadLibrary("HelloWorldLib");
TS>  }
TS>}
TS>


TS>Нативный метод на c++ определен в dll-ке HelloWorld и выглядит так.

TS>Работающая часть метода списана из документации

TS>[ccode]

TS>#include <stdio.h>
TS>#include "jni.h"
TS>#include "HelloWorld.h"

TS>JNIEXPORT void JNICALL

TS>Java_HelloWorld_print(JNIEnv *env, jobject obj)
TS>{
TS> jfieldID fid1,fid2; /* store the field ID */
TS> jobject jEmbObj;
TS> jstring jstr;
TS> const char *str;

TS> jclass cls1, cls2;

TS> /* Get a reference to obj's class */
TS> cls1 = (*env)->GetObjectClass(env, obj);

TS> /* Look for the instance field s in cls */

TS> fid1 = (*env)->GetFieldID(env, cls1, "TestObject",
TS> "LHelloWorld$TestClass;");
TS> if (fid1 == NULL) {
TS> return; /* failed to find the field */
TS> }

TS> // Read the instance field TestObject

TS> jEmbObj = (*env)->GetObjectField(env, obj, fid1);
// Скорее всего jEmbObj = null, т.к. полагаю, что никто поле TestObject не инициализировал (судя по коду класса HelloWorld)

TS> // Get a reference to obj's class

TS> cls2 = (*env)->GetObjectClass(env, jEmbObj);//***
// Скормил в GetObjectClass вторым аргументом null, он и умер.
TS>#

TS>Проблемы возникают со строкой ***. Если ее и все ниже комментировать, ошибок не возникает.

TS>Вопрос: как получить доступ к полям вложенных объектов?
Добавить проверки в нативный код и инициировать TestObject поле:

public TestClass TestObject = new TestClass();
Re[2]: jni. Чтение в C++ методе поля вложенного java объекта
От: TnedutS  
Дата: 10.12.09 16:47
Оценка:
Здравствуйте, alsemm, Вы писали:
Скорее всего jEmbObj = null, т.к. полагаю, что никто поле TestObject не инициализировал (судя по коду класса HelloWorld)
Скормил в GetObjectClass вторым аргументом null, он и умер.

Спасибо огромное!Заработало!
Re[3]: jni. Чтение в C++ методе поля вложенного java объекта
От: alsemm Россия  
Дата: 10.12.09 18:22
Оценка:
Здравствуйте, TnedutS, Вы писали:

TS>Здравствуйте, alsemm, Вы писали:

TS>Скорее всего jEmbObj = null, т.к. полагаю, что никто поле TestObject не инициализировал (судя по коду класса HelloWorld)
TS>Скормил в GetObjectClass вторым аргументом null, он и умер.

TS>Спасибо огромное!Заработало!

Куда ж ему деваться-то
Re: jni. Чтение в C++ методе поля вложенного java объекта
От: Jakop Россия https://wmspanel.com
Дата: 11.12.09 08:37
Оценка:
Здравствуйте, TnedutS, Вы писали:

[skipped]
могу дать пример кода, который позволяет легко и просто дебажить подобные ситуации.
Код создает instance java-машины, создает твой джава класс и может вызывать его методы. Учень удобно дебажить jni с таким подходом. Надо ?
https://wmspanel.com/nimble — Nimble Streamer media server for live and VOD HLS, RTMP, HTTP streaming

https://wmspanel.com/ — Control and reporting panel for Wowza and Nimble Streamer
Re[2]: jni. Чтение в C++ методе поля вложенного java объекта
От: andrey_kon Россия  
Дата: 22.12.09 10:15
Оценка:
Здравствуйте, Jakop, Вы писали:

J>Здравствуйте, TnedutS, Вы писали:


J>[skipped]

J>могу дать пример кода, который позволяет легко и просто дебажить подобные ситуации.
J>Код создает instance java-машины, создает твой джава класс и может вызывать его методы. Учень удобно дебажить jni с таким подходом. Надо ?

Да, очень было бы полезно. У меня задача использовать в С++ программе Java классы (jar файлы стороннего производителя). Ваш код позволяет это сделать?
AndreyK
Re[3]: jni. Чтение в C++ методе поля вложенного java объекта
От: Jakop Россия https://wmspanel.com
Дата: 22.12.09 12:12
Оценка: 1 (1)
Здравствуйте, andrey_kon, Вы писали:

_>Здравствуйте, Jakop, Вы писали:


J>>Здравствуйте, TnedutS, Вы писали:


J>>[skipped]

J>>могу дать пример кода, который позволяет легко и просто дебажить подобные ситуации.
J>>Код создает instance java-машины, создает твой джава класс и может вызывать его методы. Учень удобно дебажить jni с таким подходом. Надо ?

_>Да, очень было бы полезно. У меня задача использовать в С++ программе Java классы (jar файлы стороннего производителя). Ваш код позволяет это сделать?


Да, я делаю это создавая instance java машины у себя в коде.

Вот для примера как можно создать java машыну и получить объект jclass.
Линковать нужно с jvm.lib. Инклуды и либы в папке с jdk.(см include, lib)

У меня подобный код в проекте осуществляет unit test и под windows и под linux.


#include "jni.h"

int main(int argc, char* args[])
{
    JNIEnv *p_env;
    JavaVM *p_vm;
    JavaVMInitArgs arglist;
    JavaVMOption options[1];
    // тут у нас нужно указать библиотеки и их зависимости для отладки. У нашем пример допустим две     // либы и одна зависит от другой. Под линуксом вместо ; нужно ставить : иначе работать не будет
    options[0].optionString = "-Djava.class.path=C:\\project\\mylib1.jar;C:\\project\\mylib2.jar";
    options[0].extraInfo = 0;

    
    memset(&arglist, 0x0, sizeof(arglist));
    // версия jre которая нужная
    arglist.version = 0x00010006;

    jint ret_val = JNI_GetDefaultJavaVMInitArgs(&arglist);
    
    if(0 == ret_val)
    {
        arglist.nOptions = 1;
        arglist.options = options;
        // создаем java машыну. папка с должна быть в переменной PATH. Для отладки лучше использовать свойства в студии
        jint ret = JNI_CreateJavaVM(&p_vm, (void**)&p_env, &arglist);

        if(0 == ret)
        {
            // все клево машина создана
            jclass YourClass = p_env->FindClass("yourpackage/YourClass");
            if(YourClass)
            {
                 // класс найден в jar-ах которые мы указали выше
                 // ну а далее пользуемся богатейшим api ява машыны для создания/удаления объектов, вызова их методов итд
                 // все эти бонусы доступны через p_env
                 // например
                 // создаем экземпляр java.lang.Strung  
                 jstring subject = p_env->NewString((jchar*)L"hi", wcslen(L"hi"));
                 // получаем конструктор по умолчанию.
                 jmethodID constructor_id = p_env->GetMethodID(YourClass,  "<init>", "()V");
                 jobject instance = m_env->NewObject(YourClass , constructor_id);
                 // Получаем дескриптор метода public void setName(String val);
                 jmethodID yourClassMethod = p_env->GetMethodID(YourClass,  "setName", "(Ljava/lang/String;)V");
                 m_env->CallVoidMethod(instance , yourClassMethod, subject);

            }
        }
    }
    return 0;
}


В общем что угодно можно делать. Люблю я это дело
https://wmspanel.com/nimble — Nimble Streamer media server for live and VOD HLS, RTMP, HTTP streaming

https://wmspanel.com/ — Control and reporting panel for Wowza and Nimble Streamer
Re[4]: jni. Чтение в C++ методе поля вложенного java объекта
От: . Великобритания  
Дата: 22.12.09 16:04
Оценка:
On 22/12/2009 14:12, Jakop wrote:

> У меня подобный код в проекте осуществляет unit test и под windows и под

> linux.
А зачем? Ведь гораздо проще юнит-тестить из явы.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: jni. Чтение в C++ методе поля вложенного java объекта
От: Jakop Россия https://wmspanel.com
Дата: 22.12.09 23:54
Оценка:
Здравствуйте, ., Вы писали:

.>On 22/12/2009 14:12, Jakop wrote:


>> У меня подобный код в проекте осуществляет unit test и под windows и под

>> linux.
.>А зачем? Ведь гораздо проще юнит-тестить из явы.
Это если ты дебажишь обычный явоввский код. Я же дебажу явовский код который юзает jni. Описанным выше способом, я бедажу dll и so
библиотеки jni. C-ный код дебажить то тоже нужно, особенно если его несколько тысяч строк как у меня
https://wmspanel.com/nimble — Nimble Streamer media server for live and VOD HLS, RTMP, HTTP streaming

https://wmspanel.com/ — Control and reporting panel for Wowza and Nimble Streamer
Re[6]: jni. Чтение в C++ методе поля вложенного java объекта
От: . Великобритания  
Дата: 23.12.09 09:00
Оценка:
On 23/12/2009 01:54, Jakop wrote:

> .>А зачем? Ведь гораздо проще юнит-тестить из явы.

> Это если ты дебажишь обычный явоввский код. Я же дебажу явовский код
> который юзает jni. Описанным выше способом, я бедажу dll и so
> библиотеки jni. C-ный код дебажить то тоже нужно, особенно если его
> несколько тысяч строк как у меня
Я просто подключаюсь MSVS express edition к процессу java.exe — всё прекрасно отлаживается. Кстати, прекрасно работают оба отладчика одновременно: Java и C. Под линухом не знаю, не пробовал...
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[7]: jni. Чтение в C++ методе поля вложенного java объекта
От: Jakop Россия https://wmspanel.com
Дата: 23.12.09 09:46
Оценка:
Здравствуйте, ., Вы писали:

.>On 23/12/2009 01:54, Jakop wrote:


>> .>А зачем? Ведь гораздо проще юнит-тестить из явы.

>> Это если ты дебажишь обычный явоввский код. Я же дебажу явовский код
>> который юзает jni. Описанным выше способом, я бедажу dll и so
>> библиотеки jni. C-ный код дебажить то тоже нужно, особенно если его
>> несколько тысяч строк как у меня
.>Я просто подключаюсь MSVS express edition к процессу java.exe — всё прекрасно отлаживается. Кстати, прекрасно работают оба отладчика одновременно: Java и C. Под линухом не знаю, не пробовал...

Это да, но порой удобнее бывает, все-таки, инициировать отладку под студией чтобы не запускать процесс в яве и аттачить отладчик.
1)Мне чтобы отладиться нужно просто нажать f5.
2) Чтобы зааттачиться к яве нужно самому как-то запустить свое приложение отдельно и зааттачить отладчик студии к яве под которой работает мое приложение.
3) Бывает, что падает сам jni код, привем падает еще до того, как ты сможешь руками запустить отладчик и чтобы понять это лучше запустить ява-машину самому так как в этом случае сработают бряки в твоем процессе и сработают сразу
4) Я иногда использую гибридную отладку jni кода. Например я пишу большой кусок на C++. Чтобы его проверить первоначально, когда он еще вообще никак не отлажен я делаю так: создаю java объекты, которые нужны для вызова, запускаю функцию jni вызывая ее как обычную функцию на C. После отладки прогоняю тест вызывая функцию как java функцию. Обычно к этому моменту уже все работает и мне не надо аттачить отладчик студии много раз.

Пример:
jmethodID constructorID = p_env->GetMethodID (jniConnector, "<init>", "()V");
jobject connector = p_env->NewObject (jniConnector, constructorID);
jstring ip = p_env->NewStringUTF (str_ip.c_str());
jstring port = p_env->NewStringUTF (str_port.c_str());
jboolean bres = Java_xxx_xxxxxx_xxxxxx_jniConnector_init(p_env, 0, ip, port);

Поскольку я не использую this java объекта, то функция init спокойно принимает 0 в качестве instance-а объекта. Далее я отлажываю код, как обычный код C.

не убедил ?
https://wmspanel.com/nimble — Nimble Streamer media server for live and VOD HLS, RTMP, HTTP streaming

https://wmspanel.com/ — Control and reporting panel for Wowza and Nimble Streamer
Re[8]: jni. Чтение в C++ методе поля вложенного java объекта
От: . Великобритания  
Дата: 23.12.09 15:42
Оценка:
On 23/12/2009 11:46, Jakop wrote:

> Это да, но порой удобнее бывает, все-таки, инициировать отладку под

> студией чтобы не запускать процесс в яве и аттачить отладчик.
Просто в качестве debug command в Студии прописываешь java.exe с нужными ключами.
Я пытаюсь минимизировать сложность и количество С-кода, а уж тем более влияющего на запуск Машины.
В общем не убедительно.
Posted via RSDN NNTP Server 2.1 beta
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: jni. Чтение в C++ методе поля вложенного java объекта
От: Jakop Россия https://wmspanel.com
Дата: 23.12.09 23:20
Оценка:
Здравствуйте, ., Вы писали:

.>On 23/12/2009 11:46, Jakop wrote:


>> Это да, но порой удобнее бывает, все-таки, инициировать отладку под

>> студией чтобы не запускать процесс в яве и аттачить отладчик.
.>Просто в качестве debug command в Студии прописываешь java.exe с нужными ключами.
.>Я пытаюсь минимизировать сложность и количество С-кода, а уж тем более влияющего на запуск Машины.
.>В общем не убедительно.
В моем случае удобнее сделать как я описал потому как:
1) кроссплатформенно. Под линуксом просто пересобираю и запускаю. И дебажу gdb мой код как обычное приложение. gdb тоже позволяет указать targer для отладки динамической либы, но тест получится довольно сложный имхо. Пишем unit test на java компилим его, запускаем java с классом, который юзает еще никак не отлаженную либу. Мне кажется проще вначале локально оттестировать ее все ява машины, способом который я указал. В последний проекте было 4000 тысячи строк кода на Crypto Api(crypto pro есть и для linux и для solaris). Мне неплохо помогло в отладке то, что я видел какая именно функция машины фейлилась сразу в отладчике, без анализа явовских логов, которые в случае с jni не всегда очень информативны. Удобно, запустил, подебажил. Не спорю можно и java запускать как таргет, но см ниже почему еще было удобнее.
2) Минимизировать код на C стараюсь и я, но бывает что не получается.
3) Я тоже стараюсь не писать код, который валит виртуальную машину. Но если твоя либа выгружается из jar-а перед запуском, чтобы обеспечить hot deploy без предварительных инсталляций ее в систему, то бывает что хочешь ты того или нет ява валится.

В любом случае топикстартер-у придется запускать код явы из нативного приложения — мой вариант вполне ему подходит
А я просто привел пример как его использую я.
https://wmspanel.com/nimble — Nimble Streamer media server for live and VOD HLS, RTMP, HTTP streaming

https://wmspanel.com/ — Control and reporting panel for Wowza and Nimble Streamer
Re[4]: jni. Чтение в C++ методе поля вложенного java объекта
От: TnedutS  
Дата: 13.01.10 07:42
Оценка:
Здравствуйте, Jakop, Вы писали:

J>Здравствуйте, andrey_kon, Вы писали:


_>>Здравствуйте, Jakop, Вы писали:


J>>>Здравствуйте, TnedutS, Вы писали:


J>>>[skipped]

J>>>могу дать пример кода, который позволяет легко и просто дебажить подобные ситуации.
J>>>Код создает instance java-машины, создает твой джава класс и может вызывать его методы. Учень удобно дебажить jni с таким подходом. Надо ?

_>>Да, очень было бы полезно. У меня задача использовать в С++ программе Java классы (jar файлы стороннего производителя). Ваш код позволяет это сделать?


J>Да, я делаю это создавая instance java машины у себя в коде.


J>Вот для примера как можно создать java машыну и получить объект jclass.

J>Линковать нужно с jvm.lib. Инклуды и либы в папке с jdk.(см include, lib)

J>У меня подобный код в проекте осуществляет unit test и под windows и под linux.


J>

J>#include "jni.h"

J>int main(int argc, char* args[])
J>{
J>    JNIEnv *p_env;
J>    JavaVM *p_vm;
J>    JavaVMInitArgs arglist;
J>    JavaVMOption options[1];
J>    // тут у нас нужно указать библиотеки и их зависимости для отладки. У нашем пример допустим две     // либы и одна зависит от другой. Под линуксом вместо ; нужно ставить : иначе работать не будет
J>    options[0].optionString = "-Djava.class.path=C:\\project\\mylib1.jar;C:\\project\\mylib2.jar";
J>    options[0].extraInfo = 0;

    
J>    memset(&arglist, 0x0, sizeof(arglist));
J>    // версия jre которая нужная
J>    arglist.version = 0x00010006;

J>    jint ret_val = JNI_GetDefaultJavaVMInitArgs(&arglist);
    
J>    if(0 == ret_val)
J>    {
J>        arglist.nOptions = 1;
J>        arglist.options = options;
J>        // создаем java машыну. папка с должна быть в переменной PATH. Для отладки лучше использовать свойства в студии
J>        jint ret = JNI_CreateJavaVM(&p_vm, (void**)&p_env, &arglist);

J>        if(0 == ret)
J>        {
J>            // все клево машина создана
J>            jclass YourClass = p_env->FindClass("yourpackage/YourClass");
J>            if(YourClass)
J>            {
J>                 // класс найден в jar-ах которые мы указали выше
J>                 // ну а далее пользуемся богатейшим api ява машыны для создания/удаления объектов, вызова их методов итд
J>                 // все эти бонусы доступны через p_env
J>                 // например
J>                 // создаем экземпляр java.lang.Strung  
J>                 jstring subject = p_env->NewString((jchar*)L"hi", wcslen(L"hi"));
J>                 // получаем конструктор по умолчанию.
J>                 jmethodID constructor_id = p_env->GetMethodID(YourClass,  "<init>", "()V");
J>                 jobject instance = m_env->NewObject(YourClass , constructor_id);
J>                 // Получаем дескриптор метода public void setName(String val);
J>                 jmethodID yourClassMethod = p_env->GetMethodID(YourClass,  "setName", "(Ljava/lang/String;)V");
J>                 m_env->CallVoidMethod(instance , yourClassMethod, subject);

J>            }
J>        }
J>    }
J>    return 0;
J>}

J>


J>В общем что угодно можно делать. Люблю я это дело


Не сразу увидел сообщение.
Спасибо! Надо будет попробовать.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.