Tag d'archives : C++

Ce matin, Lorenzo a réussi à faire fonctionner l’exécutable en l’appelant avec Java. J’ai alors modifié l’exécutable de sorte à ce qu’il renvoie le XML sur la sortie standard et non dans un fichier. Sur demande de Nicolas R., j’ai également ajouté une autre fonctionnalité à cet exécutable, qui est de renvoyer la liste des périphériques sous la forme d’un flux JSON. Ainsi, si l’on ne passe aucun paramètre à l’exécutable, le flux sera rendu sous le format JSON, tandis que si on lui passe “1″ pour paramètre, ce sera sous le format XML. Lorenzo a ensuite pu aisément récupérer ces flux pour les parser et récupérer les chaînes de caractères dont il avait besoin, et cela grâce aux classes Java dédiées à JSON, disponibles ici.

J’ai ensuite, sur demande de Nicolas R., crée une archive contenant l’exécutable, son code, ainsi qu’un Readme expliquant comment compiler le code sous Visual Studio afin de générer l’exécutable.  Puis j’ai poursuivi la lecture de la doc de Gstreamer.

Aujourd’hui, j’ai poursuivi la lecture de la documentation de Gstreamer. Parallèlement à cela, Lorenzo a testé mon exécutable. Malheureusement, à l’instar de la DLL, celui-ci ne fonctionnait que si Visual C++ 2008 Express était installé. Cette solution était donc également à abandonner. Nicolas H. nous a alors suggéré de tenter de récupérer toutes les DLL nécessaires au fonctionnement de la mienne, et de chercher d’autre part, dans le code source de Video Lan, ce qui était utilisé pour lister les périphériques.

Seulement, il semblerait que Video Lan utilise également DirectShow. Quant à la récupération des DLL, elle était inutile car, même en désinstallant Visuel Studio, le Framework .NET et toutes les DLL qu’il utilise restent installées. Comme l’exécutable ne fonctionnait tout de même pas lorsque Lorenzo désinstallait Visual, cela ne pouvait donc pas provenir des DLL. En recherchant un peu sur Internet, j’ai alors réalisé que mon projet était mal configuré, et que les exécutables que je générais n’étaient donc pas utilisables sur un autre ordinateur. En le reconfigurant, l’exécutable à nouveau généré marchait parfaitement sur l’ordinateur de Lorenzo. J’ai alors également tenté de réécrire ma DLL sous cette nouvelle configuration. Mais pour une raison que j’ignore, cette nouvelle DLL marche très bien avec un projet Visual C++, mais ses fonctions ne renvoient rien lorsqu’elles sont utilisées avec la JNA. De son côté, Lorenzo a rencontré un problème similaire avec l’exécutable : lorsqu’il l’exécute “à la main”, le fichier XML est bien généré, mais lorsqu’il l’appelle depuis le code Java, rien ne se produit.

J’ai achevé le code de l’exécutable ce matin. J’ai essayé au mieux de ne pas utiliser de code managé, afin d’éviter par tous les moyens d’avoir besoin du Framework .NET au moment de l’exécution (on ne sait jamais). Cet exécutable crée un fichier XML listant tous les périphériques et codecs utilisables sur l’ordinateur. Il liste ainsi 4 catégories de dispositifs : VideoDevices, AudioDevices, VideoCompressors et AudioCompressors. Chaque dispositif détecté est encore entouré d’une balise “<Device>”.

Cependant, j’ai eu un petit souci avec les caractères spéciaux, qui n’étaient pas reconnus par Internet Explorer. J’ai donc rajouté la balise <?xml version=”1.0″ encoding=”iso-8859-1″?> à mon fichier XML, mais cependant, si l’on en croit cet article, un risque persiste. Il faudra donc voir lors des tests si l’on rencontre un quelconque problème.

De plus, à chaque appel de l’exécutable, une fenêtre console apparaît pendant quelques centièmes de secondes. Ceci est un peu ennuyeux, mais une des libraires nécessaires à la création de l’exécutable impose qu’il s’agisse d’une application console.

Parallèlement à cela, j’ai commencé à lire la documentation de Gstreamer. Jusqu’à présent, son principe de fonctionnement me paraît assez clair, car il a de grandes similitudes avec DirectShow.

J’ai encore effectué quelques recherches à partir du code du projet que m’avait soumis Nicolas R., ainsi que la fameuse fonction sendMessage() de la DLL User32 qui permet d’ouvrir une boîte de dialogue contrôlant le flux vidéo d’un appareil connecté à l’ordinateur. Malheureusement, il semblerait bien qu’il n’y ait aucun moyen d’accéder aux informations de la webcam. On ne peut contrôler que le dispositif de capture du flux de la webcam, et toutes les manipulations sur le flux vidéo dans ce projet sont faites à partir des données transmises pas ce dispositif. De plus, on ne peut pas accéder aux informations contrôlées pas la boîte de dialogue affichée par sendMessage().

J’ai donc ensuite commencé à coder un exécutable qui permettrait d’afficher sur la console, la liste des périphériques audio et vidéo, ainsi que la liste des compresseurs. Pour cela, j’ai simplement repris le code de ma DLL. Cependant, je rencontre quelques soucis à l’affichage, car je n’arrive pas à trouver en quel type caster l’objet BSTR que je récupère, afin de l’afficher en entier sur la console. Pour le moment, seule l’initiale du nom du périphérique s’affiche.

Une coupure d’Internet dans la matinée nous a fait perdre un peu de temps.

La solution proposée par la JMF n’étant pas très concluante, Nicolas R. m’a montré le code Java qu’il avait réalisé permettant de récupérer le nom des périphériques audio. Ce code fonctionne très bien et renvoie bien la même liste de périphériques audio que le faisait ma DLL. J’ai donc tenté de partir de ce code et de voir s’il existait quelque chose d’équivalent pour les périphériques vidéo. Malheureusement, il semblerait bien que non. La JMF reste la seule alternative, mais afin de l’utiliser correctement, il faut utiliser parallèlement une application appelée la JMFRegistery. Tant qu’un périphérique vidéo n’est pas enregistré dans la JMFRegistery, il ne pourra pas être détecté par la JMF. Par ailleurs, sur Internet, quelques personnes conseillent, dans ce genre de situation, de créer une DLL en C qui sera ensuite appelée par Java. Retour à la case départ.

Sur les conseils d’un Internaute, j’ai téléchargé et installé Dependency Walker, un logiciel qui permet de tracer le graphe de dépendance d’une DLL à toutes les autres DLL qu’elle utilise. Ma DLL dépend d’un nombre très important d’autres DLL, et il est difficile de déterminer lesquelles appartiennent au framework .NET. Nous avons cependant tout de même tenté d’en installer quelques-unes sur l’ordinateur de Lorenzo, mai rien n’y fait, Eclipse ne réussit toujours pas à utiliser ma DLL.

J’ai ensuite jeté un coup d’œil au code dont Nicolas R. s’est inspiré, que vous pouvez trouver ici. En le testant, il ne m’a renvoyé qu’un seul périphérique : celui permettant la capture des images de la webcam, tout comme le faisait la JMF. Pour permettre le choix de la webcam, l’auteur de ce code a simplement appelé une fonction de la DLL User32 qui affiche un boîte de dialogue contenant la liste déroulante des webcams connectées.

Après avoir quelque peut “fignolé” ma dll et avoir écrit des fonctions Java permettant de récupérer les périphériques audio et vidéo, ainsi que leurs codecs, j’ai fourni à Lorenzo le .jar et la dll dont il aurait besoin pour son projet. Malheureusement, après les avoir installés et après avoir reconfiguré le projet de sorte à ce qu’il prenne ces deux fichiers en compte, lorsqu’il a essayé d’utiliser une des fonctions java dans son code, cela lui renvoyait l’erreur suivante :

java.lang.UnsatisfiedLinkError: Unable to load library ‘DShowUtil’: The specified module could not be found.

À travers ce que nous avons compris dans certains forums, il semblerait que problème vienne du fait que ma dll utilise elle-même d’autres librairies que ne sont pas installées, ou que le projet n’arrive pas à trouver. Nous avons alors entrepris d’installer le SDK de Windows fournissant DirectShow, mais cela n’a rien changé. Nous avons ensuite installé Visual C++ 2008 Express, dans le but de compiler les BaseClasses de DirectShow, mais même sans compiler ces classes, le code a soudain fonctionné. Lorsque  nous avons désinstallé le SDK de Microsoft, le code fonctionnait toujours, mais plus après avoir désinstallé Visual Studio. Pour que Eclipse puisse utiliser cette dll, Visual C++, et donc le Framework .NET doivent donc nécessairement être installés sur l’ordinateur. Ceci pose un gros problème, car on ne peut pas demander aux utilisateurs d’installer un framework aussi lourd pour une seule petite fonctionnalité. J’ai donc jeté un coup d’œil, parallèlement, à ce qui était possible de faire avec la JMF, mais il semblerait que ça ne soit pas les bons périphériques qui sont retournés. Je vais cependant tenter de creuser encore un peu de ce côté-là.

Voici à présent un petit tutoriel expliquant comment compiler la DLL :

- Téléchargez et installez Visual C++ 2008 Express

-Téléchargez et installez Windows SDK for Windows Server 2008 and .NET Framework 3.5

- Sous Visual, ouvrez le projet contenant la DLL (Ouvrir > Projet, puis ouvrez le dossier DShowUtils et sélectionnez le ficher DshowUtils)

- Dans les propriétés de l’éditeur de liens (Projet > Propriétés > Propriétés de configuration > Editeur de liens > Entrée > Dépendances supplémentaires) , ajoutez les librairies suivantes : user32.lib, ole32.lib, oleaut32.lib, quartz.lib, strmiids.lib.

Nicolas R. m’a montré comment indiquer le chemin de la DLL à la JNA. J’ai donc pu écrire un bout de code utilisant les fonctions de ma DLL. Ce code affiche le premier élément de chaque liste (périphériques vidéo, périphériques audio, compresseurs vidéo et compresseurs audio). Cela fonctionne, mais selon les cas, le programme est interrompu et renvoie l’exception suivante :

#
# An unexpected error has been detected by Java Runtime Environment:
#
#  EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0×7c921639, pid=448, tid=1348
#
# Java VM: Java HotSpot(TM) Client VM (11.2-b01 mixed mode, sharing windows-x86)
# Problematic frame:
# C  [ntdll.dll+0x11639]
#
# An error report file with more information is saved as:
# C:\Documents and Settings\Administrateur\workspace\JNATest\hs_err_pid448.log
#
# If you would like to submit a bug report, please visit:
#   http://java.sun.com/webapps/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.
#

J’ai effectué quelques recherches sur Internet, indiquant que cela pouvait venir de la JVM. Mais j’ai également vérifié les logs de ces erreurs, et ils semblerait que cela soit plutôt dû à un problème de pointeurs. J’ai donc modifié un peu le code de ma dll, mais le problème persiste selon les cas.

Après plusieurs modifications de la DLL, et avec l’aide de Nicolas R., il s’est finalement avéré que l’objet récupéré n’était pas le bon, puisqu’il s’agissait du premier élément de chaque liste, et non de la liste en elle-même. J’ai ensuite dû faire attention au fait que lorsqu’aucun périphérique n’est connecté, l’exception précédente était renvoyée. J’ai donc fait en sorte que l’on vérifie que l’énumérateur de périphériques est bien crée, et que, dans le cas contraire, renvoie un tableau vide et une taille nulle. Cela m’a pris un certain temps car je n’avais pas tout de suite réalisé que l’absence de périphériques empêchait la création de cet énumérateur.

J’ai continué à travailler à la résolution de mon exception. Ne réussissant pas à trouver de solution, on m’a fait savoir qu’en dernier recours, je pouvais éventuellement abandonner le code C++ pour la JMF de Java. Celle-ci permet en effet de récupérer la liste des périphériques vidéo et audio connectés, comme vous pouvez le voir ici.

J’ai cependant tenté de poursuivre l’implémentation en C++. Au bout d’un moment, j’ai fini par comprendre que le problème venait du tableau de string que je renvoyais. J’ai remplacé ce dernier par un TCHAR**, qui est donc un type non managé, mais l’erreur persistait, ce que j’ai trouvé très étrange. En effet, il ne fallait pas oublier de recopier le fichier de la DLL dans le répertoire du projet qui l’utilisait! J’ai eu par la suite quelques petites erreurs classiques dont il fut bien plus simple de venir à bout, et j’ai finalement réussi à faire fonctionner mon projet avec le code de la DLL, même si celui-ci n’est pas encore parfait.

À présent, j’essaye d’importer cette DLL avec l’aide de la JNA. La seule difficulté à laquelle je me heurte pour le moment est de faire en sorte que la JNA trouve cette DLL.

J’ai effectué plusieurs tentatives afin d’écrire une dll qui fonctionne. En effet, de nombreux sites donnaient des informations contradictoires et de plus, il existe apparemment plusieurs manières d’écrire une dll sous Visual C++ 2008 Express. J’ai finalement trouvé cette solution qui est très facile à mettre en place et qui fonctionne très bien. Cependant, il y a deux petits détails qui ont étés omis dans ce post, qui sont les suivants : il faut également penser à ajouter le répertoire où se trouve le header de votre librairie à l’ensemble des répertoires d’Include du projet utilisant la ddl, (Outils -> Options -> Projets et solutions -> Répertoires de VC++ -> Afficher les répertoires pour : Fichiers Include), et il faut ajouter votre fichier dll au répertoire Debug de ce même projet.

J’ai ensuite implémenté les fonctions que je désirais dans ma DLL. Malheureusement, cela ne fonctionnait pas, car j’utilisais des types managés. Pour pouvoir contourner ce problème, au lieu d’utiliser dllexport dans la librairie, il suffit d’écrire la librairie comme n’importe quel projet Visual C++, et d’y ajouter un fichier de définition. Cela m’a pris un peu de temps car je ne savais pas comment ajouter un tel fichier dans un projet C++. Enfin, il faut utiliser DLLImport dans le projet qui utilise cette librairie, comme indique à la fin de cet article.

Mon projet compile enfin avec la DLL, mais malheureusement, je rencontre une exception à l’exécution, indiquant qu’il est impossible de marshaler les types génériques. Je travaille actuellement à sa résolution.

J’ai finalement réussi à faire fonctionner l’émetteur vidéo. Malheureusement, il semblerait bien qu’il ne réussisse toujours pas à envoyer les données au récepteur. Qui-plus-est, le récepteur provoque à présent des erreurs à l’exécution, et je n’arrive pas à déterminer d’où cela vient.  En effet, il semblerait qu’il se ferme lorsqu’on lance un nouveau thread, chose qu’il ne faisait pas auparavant.

Parallèlement, pour pallier à mes problèmes de JNA, on m’a conseillé d’écrire une DLL contenant des fonctions qui réalisent le remplissage des listes déroulantes, auxquelles on ne passerait en paramètre que des objets de type simple. J’ai donc écrit une telle DLL, mais j’ai rencontré quelques soucis à la compilation, car les propriétés du projet n’étaient pas bien configurées. J’essaie à présent d’utiliser certaines des fonctions de cette DLL dans un projet, mais je rencontre là aussi, quelques difficultés.