IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Entrée/Sortie Java Discussion :

JNI avec Visual Studi 2015 + Java 1.8.0.60 - Exception : segment de mémoire endommagé


Sujet :

Entrée/Sortie Java

  1. #1
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut JNI avec Visual Studi 2015 + Java 1.8.0.60 - Exception : segment de mémoire endommagé
    Bonjour à tous et à toutes.

    Gros problème qui me bloque depuis plus de 24h00 !
    J'utilise une librairie (Shapelib) depuis des années dans un soft, librairie appelé depuis JNI (wrapé par SWIG).
    Ce code fonctionnait correctement.

    Nous sommes passé à java 1.8.0.60 + Visual Studio 2015 SP3.
    Une méthode de la shapelib ne fonctionne pas : ça ne retourne pas la bonne chaîne de caractères...

    Je creuse et je fais un projet java contenant une seule classe :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    package test;
     
    import javax.swing.JOptionPane;
     
    public class Main {
    	public static void main(String[] args) {
    		run();
    	}
     
    	public static void run() {
    		JOptionPane.showConfirmDialog(null, "Title");
    	}
     
    }
    On ne peut pas faire plus simple !

    Un petit programme en C++ sous Visual Studio 2015.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    // ShapeLibConsole.cpp : Defines the entry point for the console application.
    //
     
    #include "stdafx.h"
    #include "jni.h"
    #include <jni_md.h>
    #include <stdlib.h>
    #include <string>
    #include <iostream>
     
    int main()
    {
        JavaVM *jvm;   /* denotes a Java VM */
        JNIEnv *pEnv;  /* pointer to native method interface */
        jint square;
        jboolean not;
     
        JavaVMInitArgs vm_args; /* JDK/JRE 6 VM initialization arguments */
     
        JavaVMOption *options = new JavaVMOption[4];
     
        options[0].optionString = "-Djava.class.path=D:/Dev/Workspace/Geomaps/TestJava/TestShapelib/bin";
        options[1].optionString = "-Djava.library.path=D:\\Dev\\Workspace\\Geomaps\\TestJava\\TestShapelib\\jar";  /* set native library path */
        options[2].optionString = "-verbose:jni";                   /* print JNI-related messages */
        options[3].optionString = "-Djava.compiler=NONE";           /* disable JIT */
     
        vm_args.version = JNI_VERSION_1_4;
        vm_args.options = options;
        vm_args.nOptions = 3;
        vm_args.ignoreUnrecognized = true;
     
     
        int res = JNI_CreateJavaVM(&jvm, (void **)&pEnv, &vm_args);
        if (res != JNI_OK)
        {
            exit(EXIT_FAILURE);
        }
        else
        {
            std::cout << "JVM load succeeded: Version ";
            jint ver = pEnv->GetVersion();
            std::cout << ((ver >> 16) & 0x0f) << "." << (ver & 0x0f) << std::endl;
        }
     
        char szBuffferTEST[2048];
        strncpy_s(szBuffferTEST, 2048, "Salut c'est ma chaine !", 2048);
        jstring jarg1 = pEnv->NewStringUTF(szBuffferTEST);
        jboolean isCopy;
     
        char *arg1 = (char *)(pEnv)->GetStringUTFChars(jarg1, &isCopy);
     
        strncpy_s(szBuffferTEST, 2048, "Voila, ça, c'est ma deuxieme chaine !", 2048);
        arg1 = szBuffferTEST;
     
        if (arg1 != nullptr)
        {
            jclass cls1 = pEnv->FindClass("test/Main");  // try to find the class
            if (cls1 == nullptr)
            {
                std::cerr << "ERROR: class not found !";
            }
            else
            {                                  // if class found, continue
                std::cout << "Class MyTest found" << std::endl;
                jmethodID mid = pEnv->GetStaticMethodID(cls1, "run", "()V");  // find method
                if (mid == nullptr)
                {
                    std::cerr << "ERROR: method void mymain() not found !" << std::endl;
                }
                else
                {
                    pEnv->CallStaticVoidMethod(cls1, mid);                      // call method
                    std::cout << std::endl;
                }
            }
     
            if (isCopy)
            {
                (pEnv)->ReleaseStringUTFChars(jarg1, (const char *)arg1);
            }
            jthrowable jth = pEnv->ExceptionOccurred();
            if (jth != nullptr)
            {
                printf("There was an exception Getting Value \n");
                pEnv->ExceptionDescribe();
                pEnv->ExceptionClear();
            }
            char *argRet = (char *)(pEnv)->GetStringUTFChars(jarg1, nullptr);
     
            pEnv->DeleteLocalRef(jarg1);
        }
     
        return 0;
    }
    En faisant ce code, il faut bien entendu dans Environnement des propriétés projet / debugging modifier le path :
    PATH=$(PATH);C:\Program Files (x86)\Java\jdk1.8.0_60\jre\bin;C:\Program Files (x86)\Java\jdk1.8.0_60\jre\bin\client

    Et là presque tout fonctionne, le chargement du .class, l'appel de la méthode java statique.
    Tout, sauf l'appel de la méthode :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (pEnv)->ReleaseStringUTFChars(jarg1, (const char *)arg1);
    elle provoque une erreur :
    Exception thrown at 0x77AAED0B (ntdll.dll) in ShapeLibConsole.exe: 0xC0000374: Un segment de mémoire a été endommagé (parameters: 0x77AE4270).
    Or c'est exactement ce problème là que j'avais dans le 'vrai' programme et que je débuggais (attach to process + debug directement dans la shapelib).
    Là, dans ce cas, pas de shapelib, pas de JNI mais une simulation via du code c++ de ce qui est exécuté par le wrapper SWIG.

    C'est incompréhensible, ça pue le bug Visual / Java...

    Une idée ?
    Ce qui pourrait être bien c'est d'avoir le fichier pdb + code source pour débugger DANS la fonction ReleaseStringUTFChars parce que là, il faut débugger en assembleur...

    Je ne pense pas trouver de réponse, je n'ai pas trouvé ce genre de problème ailleurs... Mais sait-on jamais.

  2. #2
    Expert confirmé Avatar de yildiz-online
    Homme Profil pro
    Architecte de domaine
    Inscrit en
    Octobre 2011
    Messages
    1 444
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Architecte de domaine

    Informations forums :
    Inscription : Octobre 2011
    Messages : 1 444
    Points : 4 563
    Points
    4 563
    Par défaut
    Quand tu fais un GetStringUtfChars tu as un const char*, avec un segment mémoire alloué pour ce char*, le caster en char* pour le manipuler n'est pas prudent.

    Considère le en tant que ce qu'il est, un const char*, et ça ira.
    PXL le retro-gaming facile: Essayez-le

    Yildiz-Engine an open-source modular game engine: Website
    Yildiz-Online a 3D MMORTS in alpha: Facebook page / Youtube page

  3. #3
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    C'est tout à fait ça, en fait le problème n'est pas là.
    Ce programme d'exemple est foireux car vite fait même si j'ai perdu du temps là dessus.

    Le problème identifié est SWIG utiliser un StringBuilder au lieu d'un String
    Et en fait c'est là qu'est le problème : passer un String à une méthode JNI pour récupérer la valeur dans ce String est impossible.
    C'est un problème SWIG et franchement c'est hyper compliqué !

  4. #4
    Expert confirmé Avatar de yildiz-online
    Homme Profil pro
    Architecte de domaine
    Inscrit en
    Octobre 2011
    Messages
    1 444
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Architecte de domaine

    Informations forums :
    Inscription : Octobre 2011
    Messages : 1 444
    Points : 4 563
    Points
    4 563
    Par défaut
    Les Strings en JNI c'est tout à fait faisable, par contre faut être sur que l'endianness soit le même entre la JVM et le l'hote du code JNI
    PXL le retro-gaming facile: Essayez-le

    Yildiz-Engine an open-source modular game engine: Website
    Yildiz-Online a 3D MMORTS in alpha: Facebook page / Youtube page

  5. #5
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Passer un String à la JNI est possible pour passer un char * au programme C.
    Là, le problème c'est que ce char * est rempli (strcpy) par le programme C et doit être récupéré côté Java, passer un String java qui est immuable n'est donc pas une bonne idée.
    Bien entendu, il est hors de question de modifier le prototype de la librairie C qui est une librairie libre, on va pas se la patcher à chaque mise à jour (et en plus c'est cracra)

  6. #6
    Expert confirmé Avatar de yildiz-online
    Homme Profil pro
    Architecte de domaine
    Inscrit en
    Octobre 2011
    Messages
    1 444
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations professionnelles :
    Activité : Architecte de domaine

    Informations forums :
    Inscription : Octobre 2011
    Messages : 1 444
    Points : 4 563
    Points
    4 563
    Par défaut
    Ca marche dans les 2 sens, voici une function qui retourne une String:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    JNIEXPORT jstring JNICALL Java_be_yildiz_module_graphic_ogre_OgreNode_getName(
        JNIEnv* env,
        jobject,
        POINTER pointer) {
        LOG_FUNCTION
        return env->NewStringUTF(
        		YZ::Node::get(pointer)->getName().c_str());
    }
    sachant que YZ::Node::get(pointer)->getName() retourne une std::string
    PXL le retro-gaming facile: Essayez-le

    Yildiz-Engine an open-source modular game engine: Website
    Yildiz-Online a 3D MMORTS in alpha: Facebook page / Youtube page

  7. #7
    Membre chevronné
    Inscrit en
    Mai 2006
    Messages
    1 364
    Détails du profil
    Informations forums :
    Inscription : Mai 2006
    Messages : 1 364
    Points : 1 984
    Points
    1 984
    Par défaut
    Je ne connais pas bien JNI (juste eu des utilisations tres basiques il y a un moment) mais l'utilisation que tu en fais me parait extremement bizarre.

    Avec
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    strncpy_s(szBuffferTEST, 2048, "Salut c'est ma chaine !", 2048);;
    jstring jarg1 = pEnv->NewStringUTF(szBuffferTEST);
    Tu crées un Objet String qui contient ton texte: "Salut c'est ma chaine !".

    Ensuite, tu fais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    char *arg1 = (char *)(pEnv)->GetStringUTFChars(jarg1, &isCopy);
    Donc tu recuperes un pointeur sur ta chaine (en supprimant le const au passage, ce qui est probablement une erreur).

    Ensuite, tu fais:
    Ce qui fait pointer la variable locale "arg1" sur le tableau local "szBuffferTEST" (mais ne change pas le contenu du String puisque tu ne fais que changer le pointeur) et qui te fait perdre au passage le pointeur sur la chaine originale. Vu comment tu l'utilises, ca ressemble bien a une fonction qui alloue un buffer et qui attend un appel pour la liberation de celui-ci (j'imagine lors de l'appel à ReleaseStringUTFChars).

    Pour finir, tu fais:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (pEnv)->ReleaseStringUTFChars(jarg1, (const char *)arg1);
    Sauf erreur de ma part, à ce moment, arg1 pointe sur un tableau local. Ce qui expliquerait bien le segfault...

  8. #8
    Membre habitué

    Inscrit en
    Février 2007
    Messages
    250
    Détails du profil
    Informations personnelles :
    Âge : 53

    Informations forums :
    Inscription : Février 2007
    Messages : 250
    Points : 162
    Points
    162
    Par défaut
    Bonjour et merci pour vos réponses...
    C'est complètement 'merdé' sur ce programme de test fait à la va vite... Et finalement le problème ne vient pas de là...
    C'est l'appelant à une méthode qui prenait un String (pour appeler du C via JNI) or on ne peut modifier un String dans une méthode...

    Tout cela encapsulé dans SWIG pour une librairie écrite en C...

    J'ai fini par comprendre, modifié et fini ce fichier .i nécessaire à SWIG...
    Je pense qu'il est possible de simplifier encore... Mais bon, là ça marche, c'est presque propre...

    Le fichier retravaillé est disponible à cette adresse : SWIG utiliser un StringBuilder au lieu d'un String

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Débutant] Création d'un exécutable avec Visual Studio 2015 en VB
    Par Marroux dans le forum Visual Studio
    Réponses: 3
    Dernier message: 10/09/2016, 13h06
  2. Installer OpenCV 3.0.0 avec Visual Studio 2015
    Par ChiiChii dans le forum OpenCV
    Réponses: 2
    Dernier message: 29/01/2016, 15h18
  3. Réponses: 0
    Dernier message: 09/12/2015, 11h15
  4. Problème avec Visual Studio 2015 sous Windows 10
    Par Harrylechienfou dans le forum ASP.NET
    Réponses: 7
    Dernier message: 05/12/2015, 15h50
  5. Mode Plan avec Visual Studio 2015 Community Edition
    Par Pichar dans le forum Visual Studio
    Réponses: 0
    Dernier message: 31/08/2015, 22h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo