Partage
  • Partager sur Facebook
  • Partager sur Twitter

Création de DLL d'une API sur visual studio

Sujet résolu
    21 août 2024 à 17:55:27

    Bonjour,

    J'essaie de récupérer les données d'une API (swiss ephemeris), que j'aimerai utiliser en PHP, sous windows, mais pour ça je dois créer une DLL, qui si j'ai bien compris, sera crée à partir des fichiers .c et .h fournis.


    J'ai essayé plusieurs options, la première est la récupération par github sur ce lien : https://github.com/aloistr/swisseph
    E
    nsuite sur visual studio j'ai mis tout les .c et .h présents à la racine du dossier.


    J'ai pu générer le .dll mais lorsque j'essaie de l'appeler en PHP par FII ça me met que ça n'a pas pu charger correctement ce fichier.

    L'emplacement est bon, donc j'ai téléchargé dependency walker, de ce que j'ai trouvé il permets de voir si une librairie fonctionne correctement.
    Mais lorsque je le lance sur mon .dll, j'ai des erreurs  :

    Error: At least one required implicit or forwarded dependency was not found.
    Error: At least one module has an unresolved import due to a missing export function in an implicitly dependent module.
    Error: Modules with different CPU types were found.
    Error: A circular dependency was detected.
    Warning: At least one delay-load dependency module was not found.
    Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.




    Pour la compilation de base, j'avais modifié des fichiers, je pense que ça vient de ça, je connais très mal le C++ j'ai possiblement fait quelque chose qui ne fallait pas.
    Pour résumé les corrections, il y avait beaucoup de fonctions comme :

    strncpy(s, dli.dli_fname, len);
    
    // que j'ai remplacé par :
    strncpy_s(s, sizeof(s),  dli.dli_fname, len);

    Sinon la compilation ne fonctionnait pas, ça me renvoyait des erreurs (est-il possible de compiler malgré ces erreurs ?).

    Aussi plusieurs fichiers contenait un "main" que j'ai supprimé de partout pour n'en laisser qu'un, sinon j'avais des erreurs comme quoi le main était déjà appelé dans un autre fichier.


    Après j'ai trouvé une extension PHP utilisant aussi cette api : https://github.com/cyjoelchen/php-sweph

    Mais je bloque sur visual studio, j'ai mis comme précédemment les fichiers .c et .h à compiler, mais j'ai des erreurs de ce type : 

    Erreur (active)	E1696	impossible d'ouvrir le fichier source "php.h"	
    


    J'ai trouvé un tuto qui expliquait qu'il fallait rajouter dans les propriétés visual studio, dans C/C++ -> général -> répertoires #using supplémentaires certains éléments : 

    C:\php-src\main
    C:\php-src\Zend
    C:\php-src\TSRM
    C:\php-src

    Je les aient bien rajouté, mais j'ai toujours les mêmes erreurs, concernant le php.h et autres qui ne sont pas trouvés (par exemple le php.h est bien dans le dossier main que j'ai définis pourtant).

    Je commence à avoir fait le tour des possibilités et je bloque complètement, si des personnes s'y connaissant mieux peuvent m'aider, merci d'avance !


    • Partager sur Facebook
    • Partager sur Twitter
      22 août 2024 à 15:16:24

      Bon, j'y connais rien en PHP, je risque donc de dire de grosses conneries.

      Mais, en version courte, pourquoi ne pas prendre l'une des 2 dll qui sont dans zip "sweph.zip" du premier dépôt Github ?

      swisseph/windows at master · aloistr/swisseph · GitHub

      Mais bon, pour les sources, c'est une version pas à jour, qui utilise une solution Visual Studio 2015, avec une version de la C-Runtime correspondante, donc à faire de la "spéléo" dans les archives des redistribuable sur le site de Microsoft.

      Franchement, les mainteneurs de ce projet, ils en cognent de la "version" Windows.

      Créer une dll à jour demande un peu plus de connaissances de ce qui transparaît de vos manipulations précédentes.

      Pour ce qui est de ce qui existe dans la version des Dll compilées, vous pouvez toujours jeter un coup d'œil aux fichiers VB qui donnent les fonctions implémentées exportées et leurs signatures. Mais, bon, les "readme" indiquent que c'est pas forcement "à jour" avec la version des Dll. (LOL, ils s'en cognent vraiment de l'interopérabilité)

      Si vous maitrisez un peu "dependency walker", vous devriez facilement en extrait ces mêmes informations (noms des fonctions et leurs signatures) "à jour".

      Maintenant, mes remarques à 2 balles sur les actions que vous avez entreprises.

      API, c'est un terme très très vague, il n'y a pas que l'appel de fonctions exportées par des Dll qui peuvent être considérées comme API.

      Il y a d'autres formes d'API, comme l'utilisation d'une extensions PHP, comme ce que fait le second dépôt Github que vous mentionnez. Mais aussi des lancement d'exécutable autonomes, des WebMethodes (REST, SOAP, etc...), des composants COM ou XCOM, etc ...

      Pourquoi Windows ?

      Pourquoi que les sous-systèmes Windows qui supportent les Dll (CONSOLE et WINDOW) ?

      Pourquoi pas utilisez WSL, sous-système Windows "compatible" avec Linux, permettant d'utiliser "facilement" le contenu du second dépôt Github ?

      Voir, pourquoi pas une conteneurisation via Docker, Kubernetes, etc... ?

      Si c'est l'utilisation de Dlls qui vous pousse vers Windows/WINDOW, c'est plutôt un mauvais calcul, vu le nombre de portes que cela vous ferme. (surtout si vous ne maitrisez pas le C++ Windows)

      >qui si j'ai bien compris, sera crée à partir des fichiers .c et .h fournis.<

      Pas que, et vraisemblablement pas tous les .h ou .c. En particulier, avec Visual Studio, il y a pas mal de constantes de compilation à mettre en œuvre.

      >Ensuite sur visual studio j'ai mis tout les .c et .h présents à la racine du dossier.<

      Donc, se méfier de tout mettre, sans réfléchir.

      Surtout définir "MAKE_DLL" dans les constantes de compilation (cf. le fichier "swisseph/swephexp.h" ( swisseph/swephexp.h at aloistr/swisseph (github.com) )).

      Si vous n'avez pas défini cette constante de compilation, mais que vous avez quand même généré une Dll, il y a de fortes chances que votre Dll n'exporte aucune fonction. Pas très utile comme Dll.

      Vous pouvez facilement le vérifier avec "dependency walker" qui donne la liste des fonctions exportées par les Dll.

      >L'emplacement est bon<

      C'est à dire ? Quid des Dll utilisées par cette même Dll ? (Dlls qui elles-mêmes utilisent d'autres Dll, etc...)

      Utilisez un outil comme "Process Monitor" pour vérifier vraiment vos dires. Process Monitor - Sysinternals | Microsoft Learn

      >j'ai téléchargé dependency walker, de ce que j'ai trouvé il permets de voir si une librairie fonctionne correctement.<

      Non, ce n'est pas un outil de test, juste un outil de visualisation du "contenu" des Dll.

      >Mais lorsque je le lance sur mon .dll, j'ai des erreurs  :<

      L'ordre de recherche des Dll lors de leur chargement est fonction de nombreuses variables (type d'exécutable "chargeur" (32/64bits, service/processus,...), "l'utilisateur", le "working directory", variables d'environnement, des manifestes, etc...). Quand vous lancez "dependency walker", c'est dans le contexte de chargement dans "dependency walker" que l'analyse se fait. Ce qui peut expliquer pourquoi des "problèmes" peuvent apparaître dans "dependency walker" mais pas dans le contexte d'utilisation de la Dll "chapiteau" dans PHP (ou autres). Ou, à l'inverse, ne pas apparaître dans "dependency walker" et apparaître dans le contexte d'utilisation de la Dll.

      (L'utilisation de "Process Monitor" permet de vérifier dans quel ordre et où un processus récupère "ses" Dll)

      Mais ici, dans le contexte d'une Dll "chapiteau" "muette" (pas de fonctions exportées), c'est "normal" que "dependency walker" gueule.

      Je parle de Dll "chapiteau" parce qu'une Dll, très souvent, utilise d'autres Dlls, qui elles-mêmes utilisent d'autres Dlls, etc...

      Error: At least one required implicit or forwarded dependency was not found.

      Si vous avez utilisé les réglages par défaut de création d'une Dll à partir d'un code source C dans Visual Studio, il y a de très grandes chances que la Dll générée utilise la C-Runtime en Dll. (La C-Runtime ne fait pas partie de l'OS Windows, contrairement à Unix/Linux)

      Si vous n'avez pas installé (via un .msi des "Redistribuable Runtimes") cette C-Runtime correspondante à celle utilisée par Visual Studio, c'est normal que ce message d'erreur apparaisse. Quand vous débuguez dans Visual Studio, il customise l'environnement d'exécution pour que la "bonne" version de la C-Runtime soit chargée. Cette customisation n'existe pas "hors" de Visual Studio, ce qui peut expliquer pourquoi l'environnement PHP et "dependency walker" ne trouve pas cette C-Runtime.

      Si on a le nom de la Dll qui n'est pas trouvée, ça permettrait de valider ou d'invalider mon hypothèse de C-Runtime.

      Ca doit être "affiché" dans "dependency walker".

      Si on se réfère au fichier .sln et aux fichiers .vcxproj de création de Dll dans le .zip "sweph.zip" du premier dépôt Github, c'est la version "Lib(Static)" et non "Dll" de la C-Runtime qui est utilisée, ce qui rendrait l'installation de la C-Runtime non obligatoire. Mais il y a peut-être des dépendances à d'autres Dll.

      Les "implicit or forwarded dependency" dont parle le message d'erreur sont les Dlls qui doivent être déjà chargées ou qui seront chargées "en même temps" que la Dll "chapiteau" (ne pas attendre que le code exécutable demande le chargement de la Dll via un appel à "LoadLibrary"). Ce qui est le cas quand la C-Runtime est en Dll. (Mais ça peut aussi être d'autres Dlls)

      Error: At least one module has an unresolved import due to a missing export function in an implicitly dependent module.

      Je pense que c'est lié au fait que votre Dll est "muette" (qui n'exporte rien).

      Error: Modules with different CPU types were found.

      Vous êtes sûr de ne pas mélanger du 32 et du 64 bits ???

      Error: A circular dependency was detected.

      Je pense que c'est plus une "limitation" de "dependency walker" qu'un "vrai" problème. Car le chargeur de Dll de Windows gère les cycles dans les graphes de dépendances des Dll. Mais le graphe de dépendance des Dll "devrait" être acyclique, logiquement.

      Warning: At least one delay-load dependency module was not found.

      Les dépendances "delay_load", c'est un peu comme les dépendances "implicit", sauf qu'au lieu de demander à ce que la Dll soit chargée en même temps que la Dll chapiteau, on met en place une infrastructure qui charge la Dll dès que le code exécutable en aura besoin (dynamiquement). Très pratique, par exemple, quand le nom d'une Dll change entre 2 OS Windows qui doivent être supportés par la Dll. Donc, ne pas la trouver, c'est pas forcément une erreur, d'où le "Warning".

      Warning: At least one module has an unresolved import due to a missing export function in a delay-load dependent module.

      Je pense que c'est le même problème de la Dll "muette".

      >j'avais modifié des fichiers<

      Jamais très bon ça.

      >je pense que ça vient de ça<

      Non, si les modifications ne sont que celles que vous avez mentionnées, vous ne changez en rien l'interface exportée par la Dll. (Si elle n'était pas muette dû à des erreurs dans la configuration du projet Visual Studio)

      >Sinon la compilation ne fonctionnait pas, ça me renvoyait des erreurs (est-il possible de compiler malgré ces erreurs ?).<

      Vos modifications sont bonnes. Elles vont dans la direction d'une meilleure sécurité du code. On ne peut pas exclure que le reste du code utilise un effet de bord de l'ancienne version pour "tomber en marche".

      Mais si le code initial est "sain", vos modifications devraient être "ajoutées" à la codebase.

      Le plus probable, malheureusement, c'est que "strncpy_s" ne soit pas connu dans d'autres environnements de génération du projet que Visual Studio/MSVC.

      Oui, il est possible de compiler malgré ces "erreurs". Et les messages d'erreurs ont dû vous indiquer la "vraie" marche à suivre.

      Et le fichier .sln et les fichiers .vcxproj de création de Dll dans le .zip "sweph.zip" du premier dépôt Github dev(r)aient y répondre.

      Malheureusement, seul le projet swedll64 en Release implémente "correctement" le contournement du problème.

      Je pense que les autres configurations (Debug) et projets (swedll32) n'ont pas été testés/correctement configurés.

      Le principe du "contournement" est d'indiquer au compilateur qu'on va quand même utiliser des fonctions non sûres, vulnérables à des attaques par "buffer overflow". (Super :-°)

      Dans "propriétés du projet" -> "Propriétés de configuration" -> "C/C++" -> "Préprocesseur" :

      A la ligne "Définitions de préprocesseur" (les constantes de compilation), vous ajoutez (aussi bien dans la configuration "Release" que "Debug") ";_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE" juste avant ";%(PreprocessorDefinitions);".

      Si on regarde cette valeur dans le projet Visual Studio swedll64 en Release du zip "sweph.zip" :

      "WIN32;NDEBUG;_WINDOWS;MAKE_DLL;_CRT_SECURE_NO_WARNINGS;_CRT_NONSTDC_NO_DEPRECATE;%(PreprocessorDefinitions);"

      On voit bien les 2 constantes de compilations ajoutées pour le contournement mais aussi "MAKE_DLL" qui devrait permettre de ne plus générer une Dll "muette".

      >Aussi plusieurs fichiers contenait un "main" que j'ai supprimé de partout pour n'en laisser qu'un<

      Dans une Dll, il n'y a pas de fonction (point d'entré) "main". Il peut avoir une fonction "Dllmain", mais sa signature et sa fonction/utilité n'a rien à voir avec le "main".

      Vous avez donc mélangé du code utilisant la librairie avec du code implémentant la librairie. Si vous avez plusieurs "main", c'est très probablement que vous avez mélanger le code source de plusieurs exécutables (client de la librairie).

      Plutôt que de "tout" mettre dans la "marmite", c'est le processus inverse qu'il faut faire. Ajoutez un par un les éléments qui manque pour que la compilation réussisse "enfin". Le point de départ, c'est de prendre le fichier qui utilise la variable de compilation "MAKE_DLL" :

      swephexp.h ( swephexp.h )

      Dans ce fichier, il y a la déclaration de toutes les fonctions à exporter et les commentaires indiquent dans quel .c l'implémentation a été faite.

      Ajoutez au projet tous les .c d'implémentation que les commentaires indiquent.

      Configurez votre projet pour que les fichiers d'en-tête (.h) nécessaires soient trouvées.

      Vérifiez bien que la constante de compilation "MAKE_DLL" est défini dans la configuration que vous voulez générer.

      Si vous avez des erreurs d'édition de lien vous indiquant que des fonctions manquent, recherchez dans quel .c la fonction est implémentée et ajoutez là aux .c du projet.

      (Vous pouvez aussi vous servir des projets Visual Studio dans "sweph.zip" comme model, mais comme ils ne sont pas à jour, il peut manquer des choses)

      A la fin, vous devriez avoir généré une Dll "parlante" et "dependency walker" devrait vous montrer son interface (fonctions qu'elle exporte).

      >Après j'ai trouvé une extension PHP utilisant aussi cette api :<

      Ce projet n'utilise pas Visual Studio. Elle ne semble utiliser que la chaine de compilation "php-dev" uniquement.

      Vous devez donc, soit modifiez le code/configuration pour ne pas utiliser cette chaine de compilation mais la vôtre (bonjour le boulot), soit faire en sorte que vous installiez cette chaine de compilation et modifier les configurations pour qu'elle génère une Dll à la place d'une extension PHP (aussi beaucoup de boulot pour un truc moins pratique :-°).

      Pourquoi ne pas utiliser cette extension en modifiant votre contexte d'exécution (WSL, etc...) pour qu'elle fonctionne directement dedans ?

      >J'ai trouvé un tuto qui expliquait qu'il fallait rajouter dans les propriétés visual studio, dans C/C++ -> général -> répertoires #using supplémentaires<

      Rien à voir avec la choucroute.

      Vous devez confondre avec "propriétés du projet" -> "Propriétés de configuration" -> "Répertoires VS" -> "Général" -> "Répertoires includes".

      Vous devriez rapatrier la chaine de compilation "php-dev" plutôt que de tout refaire de zéro.

      • Partager sur Facebook
      • Partager sur Twitter
      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
        23 août 2024 à 11:35:54

        Bonjour,
        Merci beaucoup pour cette réponse très complète !

        Alors déjà pour la dll du dossier sweph c'est ce que j'avais commencé à faire de base, mais je n'avais pas accès à certaines fonctions, qui étaient censé bien être présente, donc j'ai essayé d'une autre manière (en voulant compiler moi même, mais sans grande réussite).
        Après j'ai refais quelques tests avec, je ne me rappelle plus du nom de ce que j'ai utilisé, mais il y a un programme qui permets de lister les fonctions présentes dans une DLL (autre que dependency walker), et au final celles que j'essayaient d'appeler sont bien dedans, mais j'ai pourtant bien une erreur : 

        Fatal error: Uncaught FFI\Exception: Attempt to call undefined C function 'swe_nod_aps'

        Si quelqu'un a une idée du souci ... Je ne comprends pas comment ça peut être undefined alors qu'elle est bien déclaré, et que certaines autres fonctions marchent correctement.

        Ensuite pour WSL j'ai essayé, mais la aussi j'ai un souci (sûrement une mauvaise configuration de ma part, j'ai absolument 0 connaissance sur le sujet de base).J'ai d'ailleurs crée un topic aussi la dessus, afin de trouver une solution, mais pour faire bref j'ai tout téléchargé, installé correctement, paramétré ubuntu dans le cli interpreter de PHPstorm, mais impossible d'utiliser les fonctions linux.

        Pour docker et compagnie de même 0 connaissance dessus, et la je commence à m'étaler un peu de partout, donc j'aurai aimé trouvé une solution dans ce que j'ai déjà entrepris, après si vous me dites que selon vous une de ces solutions est plus adaptés je regarderai de ce côté là.


        Pour la compilation je n'avais peut être pas rajouté l'en tête en effet je vais rester tout ça correctement.

        Et concernant le mélange de 32 et 64 bits c'est possible que je me sois embrouillé la aussi ! 
        Je vais tout remettre au clair, et prenant bien en compte vos instructions




        • Partager sur Facebook
        • Partager sur Twitter
          24 août 2024 à 22:56:09

          Attention, les noms des fonctions sont différents entre la version 32 bits et la version 64 bits.

          32 bits

          64 bits

          Je ne sais pas si "FFI" gère "correctement" le "mangling" 32bits de manière transparente.

          >mais je n'avais pas accès à certaines fonctions<

          Lesquelles, SVP ?

          Vous travaillez en 32bits ou en 64bits (les noms des fonctions sont différents)?

          >mais il y a un programme qui permets de lister les fonctions présentes dans une DLL (autre que dependency walker)<

          Pourquoi utiliser autre chose que "dependency walker", car ce programme donne explicitement la liste des fonctions exportées de la Dll ?

          "Process Monitor" permet de voir concrètement quelles Dll sont chargées et d'où elles "viennent".

          >comment ça peut être undefined alors qu'elle est bien déclaré<

          Qu'entendez-vous par "déclaré" ???

          Si vous êtes pas à l'aise sous Linux, et que les vieilles versions des Dll disposent de ce que vous avez besoin, migrer vers WSL peut être contre-productif. (Mais avoir plus de flèches à son arc est toujours utile)

          • Partager sur Facebook
          • Partager sur Twitter
          Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
            25 août 2024 à 10:32:00

            Bonjour, 

            Je suis en 64 bits, et j'ai bien pris le swedll64, donc à priori j'ai les bons noms de fonction.

            Alors pour la liste des fonctions qui ne fonctionnent pas, je les aient quasi toutes testées (celles qui sont bien affichés dans dependency walker, une fois cliqué sur le "swedll64", comme sur votre screen), seulement swe_julday et swe_calc fonctionnent ...

            le reste j'ai le même message d'erreur que précedemment : 

            Fatal error: Uncaught FFI\Exception: Attempt to call undefined C function 'swe_nod_aps'


            Juste que je ne connais pas trop dependecy walker, j'ai beaucoup d'infos afficher, alors que la, ça m'affichait directement les fonctions en brut, ce que je recherchais afin d'être sûre d'appeler correctement le bon nom de fonction. Mais je suis en train de regarder pour mieux comprendre dependecy.

            Par déclaré j'entendais qu'elles sont bien présentes dans la librairie.

            -
            Edité par Thbt 25 août 2024 à 10:36:43

            • Partager sur Facebook
            • Partager sur Twitter
              26 août 2024 à 6:37:39

              Il peut avoir des fonctions dans une Dll qui ne sont pas exportées.

              "dependecy walker" n'affiche que les fonctions exportées.

              Normalement, les programmes n'ont accès qu'aux fonctions et données exportées par une Dll.

              Je ne ferais pas très confiance dans le message d'erreur de "FFI". Il peut, peut-être, mal interpréter une erreur d'un autre type.

              Je vous recommande toujours d'utiliser "Process Monitor".

              La documentation fait référence à des fichiers de données. Il y a des chances que la nom récupération de ces fichiers entraine une erreur qui peut être mal comprise par "FFI".

              Dans "swephprg.pdf", chapitre 28 "Debugging and Tracing Swisseph", ils donnent des moyens pour tracer les appels aux fonctions de la Dll.

              • Partager sur Facebook
              • Partager sur Twitter
              Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                27 août 2024 à 10:10:46

                Bonjour, problème résolu !

                Au final il fallait "juste" définir toutes les fonctions que j'utilise dans le FII c def de cette manière : 

                $ffi = FFI::cdef("
                        void swe_set_ephe_path(const char *path);
                        int swe_calc(double tjd, int ipl, int iflag, double *xx, char *serr);
                        double swe_julday(int year, int month, int day, double hour, int gregflag);
                        int swe_pheno(double tjd_et, int ipl, int iflag, double *attr, char *serr);
                        int swe_houses(double tjd_ut, double geolat, double geolon, int hsys, double* cusps, double* ascmc);
                        int swe_calc_ut(double tjd_ut, int ipl, int iflag, double* xx, char* serr);
                    ", "C:\sweph\swedll64.dll");

                Merci aux personnes ayant répondu au sujet et m'ayant aider !

                • Partager sur Facebook
                • Partager sur Twitter
                  30 août 2024 à 12:42:33

                  euh sinon, il y a une solution directe pour php proposée (sauf erreur de ma part) https://github.com/cyjoelchen/php-sweph
                  • Partager sur Facebook
                  • Partager sur Twitter

                  Création de DLL d'une API sur visual studio

                  × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                  • Editeur
                  • Markdown