Partage
  • Partager sur Facebook
  • Partager sur Twitter

Problème de DLL

Passage de fonction d'une dll C/C++ vers un programme C#

    23 juin 2021 à 17:00:07

    >Par rapport au C++ et Cs, il y a bien deux paramètres à chaque

    Peut-être mais au niveau nom de fonction, c'est la foire à la saucisse :

    - AcquirePic

    - AcqImg

    - AcquireImages

    Faudrait accorder les violons.

    S'ils se distinguent juste pour leurs types et nombre de paramètre, la surcharge, en C++ ou en C#, c'est pas fait pour les chiens.

    Pour moi, l'implémentation de "NetString2gcstring" est toujours aussi foireuse.

    La fonction est peut-être maintenant accessible en .NET, mais c'est toujours une coquille vide.

    Les ligne 78 à 88 du code du wrapper NodeMap sont complètement aux fraises. Lancez des exceptions avec un message d'erreur pertinent et laissez les logs dans un std::cout aux programmeurs du dimanche.

    >SystemPtr system = System::GetInstance();

    OK, c'est bien un mécanisme de Singleton, mais qui est enraciné au niveau de l'objet "system".

    Pas très malin d'utiliser un namespace C++ "System" quand le namespace principale .NET est aussi "System".

    Vous devriez utiliser un alias lors du "using namespace System;" pour ne pas confondre les 2 namespaces et éviter les collisions de nom.

    Dans l'exemple que j'ai donné, la classe NodeMap était la racine "Singleton".

    Comme c'est "System" qui semble être la racine, vous n'avez qu'à transférer la fonction "CheckNativeSingleton" dans le wrapper de System, et appeler cette méthode en préambule des fonctions de System. (J'ai ajouté une classe intermédiaire "Toto" pour faire "comme si" on devait passer par des interfaces intermédiaires entre ISystem et INodeMap :

    //Wrapper.cpp
    #include "pch.h"
     
    #include "EnTete.h" //En-tête Genicam
     
    #include <string>
    #include <iostream>
    #include <vcclr.h>
     
    //Raccouci d'appel
    using namespace GENAPI_NAMESPACE;
    using namespace GENICAM_NAMESPACE;
    GenApiNatif = namespace System;
      
    // Wraper
    namespace WrapperNET
    {
        GenApiNatif::gcstring NetString2gcstring(String^ name)
        {
            GenApiNatif::gcstring retval{};
     
            // Les bidouilles avec MarshalString pour remplir "retval" avec le contenu de "name"
     
            return retval;
        }
     
        //Sous-partie Wrapper
        namespace GenApi
        {
    
           //IToto
            public ref class Toto
            {
                GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IToto* nativeIToto = __nullptr;
     
                Toto(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IToto* nativeIToto_)
                {
                    nativeIToto = nativeIToto_;
                }
            };
    
    ....
    
           //INode
            public ref class Node
            {
                GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INode* nativeINode = __nullptr;
     
                Node(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INode* nativeINode_)
                {
                    nativeINode = nativeINode_;
                }
            };
    
           //INodeMap
            public ref class NodeMap
            {
                GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap* nativeINodeMap = __nullptr;
     
                NodeMap(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap* nativeINodeMap_)
                {
                    nativeINodeMap = nativeINodeMap_;
                }
    
            public:
      
                //Retrieves the node from the central map by Name
                Node^ GetNode(String^ Name)
                {
                    GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INode* p_node = nativeINodeMap.GetNode(NetString2gcstring(Name));
      
                    if(p_node != __nullptr)
                    {
                        //Création de nodeWrapper
                        Node^ nodeWrapper = gcnew Node(p_node);
      
                        //Retour de la fonction GetNode
                        return nodeWrapper;
                    }
                    else
                    {
                        throw exception("GetNode(" + Name + ") n'a pas trouvé de noeud.");
                    }
                }
    
            };
    
    ....
    
            //System
            public ref class System
            {
                GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ISystem * nativeISystem = __nullptr;
    
                bool CheckNativeSingleton()
                {
                    if(nativeISystem == __nullptr)
                    {
                        nativeISystem = GENICAM_INTERFACE GENAPI_DECL_ABSTRACT GenApiNatif::System::GetInstance();
                    }
     
                    return (nativeISystem != __nullptr);
                }
    
            public:
      
                //Retrieves the toto from system
                Toto^ GetToto()
                {
                    if(CheckNativeSingleton())
                    {
                        GENICAM_INTERFACE GENAPI_DECL_ABSTRACT IToto * p_toto = nativeISystem.GetToto();
      
                        if(p_toto != __nullptr)
                        {
                            //Création de nodeWrapper
                            Toto^ totoWrapper = gcnew Toto(p_toto);
      
                            //Retour de la fonction GetToto
                            return totoWrapper ;
                        }
                        else
                        {
                            throw exception("Impossible de récupérer le Toto.");
                        }
                    }
                    else
                    {
                        throw exception("Impossible de trouver le Singleton System");
                    }
                }
            };
        }
    }


    (Cette implémentation ne fait pas de la classe System un Singleton, je vous laisse voir comment est implémenté un Singleton en .NET, si vous voulez renforcer la concordance entre l'API Native et l'API .NET)

    >autre fonction qui quant à elle se trouve être appelé dans le main

    OK, il vous reste juste à regarder dans le main comment les variables "nodeMap" et nodeMapTLDevice sont initialisées.

    >Pointer.h (en rapport à CEnumerationPtr):

    OK, il ne s'agit pas de CComPtr mais d'une solution pas très élégante à base de template et de dynamic_cast (attention à ne pas supprimer le RTTI des options de compilation, entre autre).

    >Il y a des imports/ exports dans certains fichiers telle que GenApiDll.h

    Quand je parle d'export, c'est des symboles exportés (cf. les outils comme DUMPBIN), pas des .h.

    >gcstring = std::string

    Vous en n'êtes sûr ?

    Vérifiez l'implémentation de gcstring, SVP, car std::string, c'est implémentation dépendant (mais je crois que la norme spécifie que std::string est propriétaire de son buffer, au moins par défaut).

    • Partager sur Facebook
    • Partager sur Twitter
    Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
      24 juin 2021 à 10:19:25

      Oui, effectivement pour le nom, je l'ai modifie au fur et à mesure de mes essais.

      Est ce que Toto me permettrait de faire des intermédiaires entre deux classes, donc j'ai cet appel du SDK,

      IEnum iAcquisitionMode = nodeMap.GetNode<IEnum>("AcquisitionMode");
                      if (iAcquisitionMode == null || !iAcquisitionMode.IsWritable)
                      {
                          Console.WriteLine("Unable to set acquisition mode to continuous (node retrieval). Aborting...\n");
                          return -1;
                      }

      Le mien:

      Node iAcquisitionMode = nodeMap.GetNode("AcquisitionMode");
                      if (iAcquisitionMode == null || !iAcquisitionMode.IsWritable)
                      {
                          Console.WriteLine("Unable to set acquisition mode to continuous (node retrieval). Aborting...\n");
                          return -1;
                      }

      Et derriere j'ai ca :

      IEnumEntry iAcquisitionModeContinuous = iAcquisitionMode.GetEntryByName("Continuous");
                      if (iAcquisitionModeContinuous == null || !iAcquisitionMode.IsReadable)
                      {
                          Console.WriteLine(
                              "Unable to set acquisition mode to continuous (enum entry retrieval). Aborting...\n");
                          return -1;
                      }

      Donc ma fonction GetEntryByName dépend de Node, mais je n'arrive pas à le rendre dépendant pour qu'il la reconnaisse.

      Est ce que Toto le permettrait ?

      Oui j'ai des symboles (.pdb)

      Voici la class de gcstring

      class GCBASE_API gcstring
          {
      #       if defined(_MSC_VER) && !defined(PHARLAP_WIN32)
          public:
                  //! Helper class for storing shared-ownership wchar_t *
                  class GCBASE_API gcwchar 
                  {
                  public:
                      //! Creates a buffer of \a n wchar_ts
                      explicit gcwchar( size_t n );
      
                      // copy constructor
                      gcwchar( const gcwchar &rhs );
      
                      // assignment operator
                      gcwchar & operator = (const gcwchar &rhs);
      
                      //! Gets the c-string.
                      const wchar_t * c_str() const;
      
                      //! Gets the length of the buffer.
                      size_t length() const;
      
                      //! destructor 
                      ~gcwchar();
                  private:
                      class impl;
                      impl *m_pimpl;
                  };
      
      #       endif






      -
      Edité par VincentL10 24 juin 2021 à 10:19:55

      • Partager sur Facebook
      • Partager sur Twitter
        25 juin 2021 à 11:01:20

        >Est ce que Toto me permettrait de faire des intermédiaires entre deux classes,

        Toto, c'est un exemple/placeholder, vous devez voir un exemple C++ qui permet de construire toute la chaîne d'objets, de "SystemPtr system" vers l'objet qui vous intéresse.

        Vous allez calquer votre API .NET sur l'API C++ Native.

        Vous allez "wrapper" les "CxxxPtr" par vos instances de classe .NET. Comme vous pouvez le voir dans le code que je vous ai posté, c'est juste fournir le CxxxPtr en paramètre du constructeur de votre classe de wrapping .NET, le stocker dans un champ, et s'en servir dans les méthodes de la classe .NET, qui ne sont que des méthodes passe-plat (plus conversion des String en gcstring, mais pas beaucoup plus).

        La fonction "GetNode" de votre variable "nodeMap" dans le code natif est une fonction template. Essayez de faire de cette méthode, une méthode générique en .NET (on verra après pour les restrictions). Ainsi, vous aurez le même typage "fort" en .NET quand C++ natif.

        Donc le code devrait ainsi ressembler à :

        EnumW iAcquisitionMode = nodeMap.GetNode<EnumW>("AcquisitionMode");

        (J'ai préféré nommé le nom du wrapper de "IEnum" "EnumW" pour éviter une collision de nom avec le mot-clé potentiel "Enum")

        Avec cette méthode générique, la variable "iAcquisitionMode" sera du "bon" type.

        >Donc ma fonction GetEntryByName dépend de Node

        Non, plus vraisemblablement de "IEnum/EnumW", c'est dans ces interfaces/classe que ce trouverait la déclaration de cette méthode.

        Et le terme "dépend" est assez flou.

        >Oui j'ai des symboles (.pdb)

        Pas ces symboles, qui ne servent qu'au débogueur, les symboles EXPORTÉS, DUMPBIN est ton ami.

        >Voici la class de gcstring

        Ce n'est qu'un bout, et on est même pas sûr que c'est un bout qui est vraiment utilisé dans votre cadre d'utilisation (plateformes, compilateurs, options de compilation, etc...)

        • Partager sur Facebook
        • Partager sur Twitter
        Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
          25 juin 2021 à 11:34:07

          Donc au lieu de créer Node, NodeMap... indépendamment je créer un "EnumW" qui prendra la fonction GetNode et qui donnera créera une méthode générique et qui donnera accès aux autres sans devoir les refaire dans le cpp (je pense qui faudra les refaire mais je préfère demander) ?

          Voici la définition C# de IEnum du SDK

          namespace SpinNET.GenApi
          {
              public interface IEnum : IValue
              {
                 EnumValue Value { get; set; }
                 string[] Symbolics { get; }
                 EnumEntry[] Entries { get; }
               
                 IEnumEntry GetEntryByName(string name);
              }
          }



          • Partager sur Facebook
          • Partager sur Twitter
            25 juin 2021 à 13:25:30

            >Donc au lieu de créer Node, NodeMap... indépendamment je créer un "EnumW"

            Non, vous utilisez la méthode GetNode<EnumW> d'une instance de la classe NodeMap pour obtenir un "EnumW" correctement initialisé.

            En gros l'implémentation de GetNode<> :

            public ref class NodeMap
            {
            ....
                        generic <class ItemType> where ItemType : Node
                        ItemType^ GetNode(String^ Name)
                        {
                            //Faut trouver une bidouille qui donne le type ItemTypeNatif en fonction de ItemType
                            GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ItemTypeNatif* p_item = nativeINodeMap.GetNode<ItemTypeNatif>(NetString2gcstring(Name));
               
                            if(p_node != __nullptr)
                            {
                                //Création de Wrapper
                                ItemType^ itemWrapper = Activator::CreateInstance<ItemType>(p_item);
               
                                //Retour de la fonction GetNode
                                return itemWrapper ;
                            }
                            else
                            {
                                throw exception("GetNode(" + Name + ") n'a pas trouvé l'item.");
                            }
                        }
            ....
            }

            Je pense que dans le code source de SpinNET, il doit avoir des exemples de code de mapping entre les types natifs et les type .NET.

            >Voici la définition C# de IEnum du SDK

            Vous avez donc le code source de SpinNET et l'extrait que vous nous montrez indique que le niveau d'abstraction de SpinNET, du moins dans la partie que vous montrez, n'est pas très "élevé" par rapport à l'API C++.

            Je ne comprends toujours pas pourquoi vous ne voulez pas utiliser SpinNET.

            Le code que vous postez est un code "miroir" des définitions de l'API native, pour que l'implémentation .NET fonctionne "comme" l'API Native.

            Si vous ne voulez pas utiliser SpinNET, vous devez comprendre comment fonctionne l'API Native.

            • Partager sur Facebook
            • Partager sur Twitter
            Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
              25 juin 2021 à 13:48:44

              Je pense que dans le code source de SpinNET, il doit avoir des exemples de code de mapping entre les types natifs et les type .NET.

              S'il s'agit d'un cpp, je n'ai que le côté exécution des applications (D'autres appels sont fait)

              Vous avez donc le code source de SpinNET et l'extrait que vous nous montrez indique que le niveau d'abstraction de SpinNET, du moins dans la partie que vous montrez, n'est pas très "élevé" par rapport à l'API C++.

              Ce sont les morceaux de classe à partir des métadonnées

              Je ne comprends toujours pas pourquoi vous ne voulez pas utiliser SpinNET.

              Les en-têtes sont partiellement différentes et je veux utiliser ceux d'origine.

              Si vous ne voulez pas utiliser SpinNET, vous devez comprendre comment fonctionne l'API Native.

              Je vais rechercher cela.

              • Partager sur Facebook
              • Partager sur Twitter
                28 juin 2021 à 10:05:07

                //Wrapper.cpp
                #include "pch.h"	//Headers
                #include <string>
                
                #include <msclr/marshal_cppstd.h>	//NetString2gcstring
                #include <msclr/marshal.h>
                #include <vcruntime_exception.h>	//Exception
                
                //Raccouci d'appel
                using namespace GENAPI_NAMESPACE;
                using namespace GENICAM_NAMESPACE;
                using namespace System;
                using namespace Runtime::InteropServices;
                using namespace std;
                //GenApiNatif = namespace System;
                
                #pragma warning(disable : 4715)
                
                namespace WrapperNET // Wrapper
                {
                	gcstring NetString2gcstring(String^ name) //Conversion de String en gcstring
                	{
                		const char* cstr = (const char*)(Marshal::StringToHGlobalAnsi(name)).ToPointer();
                		gcstring retval = cstr;
                		Marshal::FreeHGlobal(IntPtr((void*)cstr));
                
                		return retval;
                	}
                
                	namespace GenApi //Sous-partie Wrapper
                	{
                		public ref class Node //INode
                		{
                			GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INode* nativeINode = __nullptr;
                
                		public:
                			Node(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INode* nativeINode_) { nativeINode = nativeINode_; }
                		};
                                public ref class ItemTypeNatif
                		{
                			GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ItemType* nativeItemType = __nullptr;
                
                		public:
                			ItemType(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ItemType* nativeItemType_) { nativeItemType = nativeItemType_; }
                		};
                		public ref class NodeMap //INodeMap
                		{
                			GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap* nativeINodeMap = __nullptr;
                
                			NodeMap(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }
                
                		public:
                			generic <class ItemType> where ItemType : Node
                			ItemType GetNode(String^ Name) //Retrieves the node from the central map by Name
                			{
                				GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ItemTypeNatif* p_item = nativeINodeMap->GetNode<ItemType>(NetString2gcstring(Name));
                
                				if (p_item != __nullptr)
                				{
                					ItemTypeNatif^ itemWrapper = Activator::CreateInstance<ItemType>(p_item);	//Création de nodeWrapper
                					return itemWrapper;															//Retour de la fonction GetNode
                				}
                				else {throw ("GetNode(" + Name + ") n'a pas trouvé de noeud."); }
                
                			};
                		};
                	}
                }
                Avec ce code effectuer, je rencontre plusieurs erreurs, telle qu'a la ligne 15 avec le using System, il ne m'accepte pas la commande,

                aussi à la ligne 50 "GENICAM_INTERFACE", définition de la classe non autorisée dans une fonction de membre classe managé

                                           "ItemType", nom de type non autorisé

                aussi ligne 54 "^", impossible d'utiliser cette indirection et "Activator::CreateInstance" fonction correspondante surchargée introuvable.

                Pour la fonction NetString2gcstring, je n'ai trouvé que celle-ci qui fonctionne et je cherche pour le type ItemTypeNatif en fonction de ItemType qui est plus complexe que ce que je pensais

                -
                Edité par VincentL10 28 juin 2021 à 10:27:26

                • Partager sur Facebook
                • Partager sur Twitter
                  29 juin 2021 à 11:24:50

                  Ligne 15, Oups :

                  namespace GenApiNatif = System;


                  Ligne 50, quel cochonnerie ces MACRO.

                  Je ne connais pas l'expansion en ligne de ces cochonneries. Faites en sorte que cela est un sens in-fine.

                  Genre, peut-être :

                  NodeMap(INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }


                  >"ItemType", nom de type non autorisé

                  Message et numéro d'erreur complet, SVP.

                  Pour les "generics", je me suis basé sur cette documentation :

                  https://docs.microsoft.com/fr-fr/cpp/extensions/generic-functions-cpp-cli?view=msvc-160

                  (Vérifiez les pré-conditions)

                  >fonction correspondante surchargée introuvable.

                  S'il a des problèmes dans les types en "amont", c'est "normal".

                  >Pour la fonction NetString2gcstring, je n'ai trouvé que celle-ci qui fonctionne

                  Pourquoi "les autres" ne fonctionneraient pas ???

                  Ce n'est pas parce que vous galérez avec la syntaxe que le premier bidule qui "compile" est LA bonne solution.

                  Comme je vous l'ai déjà répété, vérifiez l'implémentation de l'opérateur "=" que vous utilisez pour vérifier l'ownership de la chaîne de caractère.

                  • Partager sur Facebook
                  • Partager sur Twitter
                  Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                    29 juin 2021 à 12:08:01

                    Je me suis avancer à ce code j'ai cherché à retrouvé ce résultat :
                    EnumW iAcquisitionMode = nodeMap.GetNode<EnumW>("AcquisitionMode");
                    

                    Sans succès

                    J'en suis arrivé à la :

                    //Wrapper.cpp
                    #include "pch.h"	//Headers
                    #include <string>
                    
                    #include <msclr/marshal_cppstd.h>	//NetString2gcstring
                    #include <msclr/marshal.h>
                    #include <vcruntime_exception.h>	//Exception
                    
                    //Raccouci d'appel
                    using namespace GENAPI_NAMESPACE;
                    using namespace GENICAM_NAMESPACE;
                    using namespace System;
                    using namespace Runtime::InteropServices;
                    using namespace std;
                    //GenApiNatif = namespace System;
                    
                    namespace WrapperNET // Wrapper
                    {
                    	gcstring NetString2gcstring(String^ name) //Conversion de String en gcstring
                    	{
                    		const char* cstr = (const char*)(Marshal::StringToHGlobalAnsi(name)).ToPointer();
                    		gcstring retval = cstr;
                    		Marshal::FreeHGlobal(IntPtr((void*)cstr));
                    
                    		return retval;
                    	}
                    
                    	public ref class NodeMap //INodeMap
                    	{
                    		GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap* nativeINodeMap = __nullptr;
                    
                    	public:
                    		NodeMap(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }
                    	};
                    
                    	public ref class Node //INode
                    	{
                    		GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INode* nativeINode = __nullptr;
                    
                    	public:
                    		Node(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INode* nativeINode_) { nativeINode = nativeINode_; }
                    	};
                    
                    	namespace GenApi //Sous-partie Wrapper
                    	{
                    		public class ItemType //ItemType
                    		{
                    			GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ItemTypeNatif* nativeItemTypeNatif = __nullptr;
                    
                    		public:
                    			ItemType(GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ItemTypeNatif* nativeItemTypeNatif_) { nativeItemTypeNatif = nativeItemTypeNatif_; }
                    		};
                    
                    		public ref class NodeMap //INodeMap
                    		{
                    			GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap* nativeINodeMap = __nullptr;
                    
                    			NodeMap(INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }
                    
                    		public:
                    			generic <class ItemType> where ItemType : Node
                    
                    				ItemType GetNode<ItemType>(String^ Name) //Retrieves the node from the central map by Name
                    			{
                    				GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ItemTypeNatif* p_item = nativeINodeMap->GetNode<ItemType>(NetString2gcstring(Name));
                    
                    				if (p_item != __nullptr)
                    				{
                    					ItemType itemWrapper = Activator::CreateInstance<ItemType>();			//Création d'une instance
                    					return itemWrapper;														//Retour de la fonction GetNode
                    				}
                    				else { throw ("GetNode(" + Name + ") n'a pas trouvé de noeud."); }
                    
                    			};
                    		};
                    	}
                    }
                    

                    Avec trois erreurs ligne 63, "GetNode":

                    C2143 erreur de syntaxe : absence de ';' avant '<', ligne 63
                    C3282 les listes de paramètres génériques ne peuvent apparaître que sur les classes, structures ou fonctions managé, ligne 63
                    C2334 jetons inattendus avant '{'; corps apparent de la fonction ignoré, ligne 64

                    Pour "NetString2gcstring", des messages d'erreurs à chaque fois, je vais reregarder

                    -
                    Edité par VincentL10 29 juin 2021 à 12:08:44

                    • Partager sur Facebook
                    • Partager sur Twitter
                      29 juin 2021 à 12:58:51

                      Commencez toujours par la première erreur.

                      Ligne 62, c'est quoi cette ligne vide à la con ???

                      Le "generic" ligne 61 est associé à la ligne 63, donc, soit vous fusionnez ces lignes soit vous les mettez l'une derrière l'autre, mais on ajoute pas des lignes blanches "pour faire jolie".

                      • Partager sur Facebook
                      • Partager sur Twitter
                      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                        29 juin 2021 à 13:21:06

                        public ref class NodeMap //INodeMap
                                {
                                    GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap* nativeINodeMap = __nullptr;
                         
                                    NodeMap(INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }
                         
                                public:
                                    generic <class ItemType> where ItemType : Node
                                    ItemType GetNode<ItemType>(String^ Name) //Retrieves the node from the central map by Name
                                    {
                                        GENICAM_INTERFACE GENAPI_DECL_ABSTRACT ItemTypeNatif* p_item = nativeINodeMap->GetNode<ItemType>(NetString2gcstring(Name));
                         
                                        if (p_item != __nullptr)
                                        {
                                            ItemType itemWrapper = Activator::CreateInstance<ItemType>();         //Création d'une instance
                                            return itemWrapper;                                                     //Retour de la fonction GetNode
                                        }
                                        else { throw ("GetNode(" + Name + ") n'a pas trouvé de noeud."); }
                         
                                    };
                                };
                        Comme ca ?
                        • Partager sur Facebook
                        • Partager sur Twitter
                          29 juin 2021 à 13:35:39

                          Oui, et remplacez les "GENICAM_INTERFACE GENAPI_DECL_ABSTRACT" par ce qui est nécessaire au compilateur pour retrouver le type (et pas le déclarer/définir).
                          • Partager sur Facebook
                          • Partager sur Twitter
                          Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                            29 juin 2021 à 13:42:51

                            generic <class ItemType> where ItemType : Node
                            public ref class NodeMap //INodeMap
                            {
                            	INodeMap* nativeINodeMap = __nullptr;
                            
                            	NodeMap(INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }
                            
                            public:
                            	ItemType GetNode<ItemType>(String^ Name) //Retrieves the node from the central map by Name
                            	{
                            		ItemTypeNatif* p_item = nativeINodeMap->GetNode<ItemType>(NetString2gcstring(Name));
                            
                            		if (p_item != __nullptr)
                            		{
                            			ItemType itemWrapper = Activator::CreateInstance<ItemType>();			//Création d'une instance
                            			return itemWrapper;														//Retour de la fonction GetNode
                            		}
                            		else { throw ("GetNode(" + Name + ") n'a pas trouvé de noeud."); }
                            
                            	};
                            };
                            Ce qui donne cela, par l'erreur C3282, j'ai du passer le generic... au début de la classe.
                            • Partager sur Facebook
                            • Partager sur Twitter
                              29 juin 2021 à 15:19:15

                              Là, c'est la classe que vous rendez générique, pas la fonction, ce n'est absolument pas la même chose.

                              Vérifiez les conditions de compilation, comme indiquer dans la documentation dont j'ai donné l'URL.

                              • Partager sur Facebook
                              • Partager sur Twitter
                              Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                29 juin 2021 à 15:37:57

                                public ref class NodeMap //INodeMap
                                {
                                    INodeMap* nativeINodeMap = __nullptr;
                                 
                                    NodeMap(INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }
                                 
                                public:
                                    generic <typename ItemType> //where ItemType : Node
                                    ItemType GetNode<ItemType>(String^ Name) //Retrieves the node from the central map by Name
                                    {
                                        ItemTypeNatif* p_item = nativeINodeMap->GetNode<ItemType>(NetString2gcstring(Name));
                                 
                                        if (p_item != __nullptr)
                                        {
                                            ItemType itemWrapper = Activator::CreateInstance<ItemType>();         //Création d'une instance
                                            return itemWrapper;                                                     //Retour de la fonction GetNode
                                        }
                                        else { throw ("GetNode(" + Name + ") n'a pas trouvé de noeud."); }
                                 
                                    };
                                };
                                On est sous condition /clr si je me trompe pas

                                -
                                Edité par VincentL10 29 juin 2021 à 15:41:34

                                • Partager sur Facebook
                                • Partager sur Twitter
                                  29 juin 2021 à 16:11:19

                                  >On est sous condition /clr si je me trompe pas

                                  Oui

                                  Vous remplacez "class" par "typename", pourquoi pas.

                                  Vous mettez en commentaire la vérification du type de base de "ItemType", pourquoi pas. Faudrait juste essayer après de limiter le type de "ItemType", mais en attendant, on fera avec.

                                  En fonction du type effectif de "ItemType", il faudra peut-être remplacer les "ItemType" pas des "ItemType^".

                                  Pourquoi "Activator::CreateInstance" n'a plus d'argument, ligne 15 ?

                                  Peut-être qu'un "gcnew" à la place de "Activator::CreateInstance" est possible.

                                  Ca compile maintenant ?

                                  • Partager sur Facebook
                                  • Partager sur Twitter
                                  Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                    29 juin 2021 à 16:40:32

                                    J'ai reregarder le lien que vous m'avez donner, et je l'ai vu comme ca, le commentaire du "where ItemType : Node" c'était juste pour vérifier un truc mais je peux le laisser. le typename est pris du lien, je n'ai pas encore la nuance entre typename et class (ou vite fait), 

                                    Le "^" provoque des erreurs de type E2201,

                                    Il n'acceptait pas le p_item (je le remets pour l'instant) car le CreateInstance je l'appréciais pas, je vais voir par gcnew.

                                    Avec l'erreur E0864, même après compilation, il m'indique que GetNode n'est pas un modèle.

                                    Cela engendre aussi qu'il ne traite pas la suite.

                                    public ref class NodeMap //INodeMap
                                    {
                                        INodeMap* nativeINodeMap = __nullptr;
                                      
                                        NodeMap(INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }
                                      
                                    public:
                                        generic <typename ItemType> where ItemType : Node
                                        ItemType GetNode<ItemType>(String^ Name) //Retrieves the node from the central map by Name
                                        {
                                            ItemTypeNatif* p_item = nativeINodeMap->GetNode<ItemType>(NetString2gcstring(Name));
                                      
                                            if (p_item != __nullptr)
                                            {
                                                ItemType itemWrapper = gcnew ItemType(p_item);         //Création d'une instance
                                                return itemWrapper;                                    //Retour de la fonction GetNode<>
                                            }
                                            else { throw ("GetNode(" + Name + ") n'a pas trouvé de noeud."); }
                                      
                                        };
                                    };

                                    Faut-il créer un classe ItemType comme ci-dessous ? (Créer ou non les erreurs ne changent pas)

                                    public ref class ItemType 
                                    {
                                        ItemTypeNatif* nativeItemTypeNatif = __nullptr;
                                      
                                        ItemType(ItemTypeNatif* nativeItemTypeNatif_) { nativeItemTypeNatif = nativeItemTypeNatif_; }
                                    }





                                    -
                                    Edité par VincentL10 29 juin 2021 à 16:40:58

                                    • Partager sur Facebook
                                    • Partager sur Twitter
                                      30 juin 2021 à 21:02:15

                                      J'ai un peu mélangé template et generic. :-°

                                      >je n'ai pas encore la nuance entre typename et class

                                      "class", c'est juste pour les classes, typename, c'est pour les classes, les structures, les enums, etc...

                                      >Faut-il créer un classe ItemType comme ci-dessous ?

                                      Non, c'est une classe générique.

                                      J'ai fais quelques tests rapidement mais j'ai pas réussi à faire en sorte que "Activator::CreateInstance" utilise un constructeur avec paramètre.

                                      Je vous conseille encore une fois de voir le code source (ou reverse_engineerer) de SpinNET pour voir comment ils font leurs instanciations.

                                      En mettant les constructeurs et les champs des classes "Node" publique, on devrait avoir un code comme celui-ci d"opérationnel" :

                                      public ref class Node
                                      {
                                      public:
                                          INode* nativeINode = __nullptr;
                                         
                                          Node(INode* nativeINode_) { nativeINode = nativeINode_; }
                                          Node(){} //rustine Activator::CreateInstance
                                      };
                                      
                                      public ref class EnumW : Node
                                      {
                                      public:
                                          IEnum* nativeIEnum = __nullptr;
                                         
                                          EnumW(IEnum* nativeIEnum_) { nativeIEnum = nativeIEnum_; }
                                          EnumW(){}//rustine Activator::CreateInstance
                                      };
                                      
                                      public ref class NodeMap //INodeMap
                                      {
                                      public:
                                          INodeMap* nativeINodeMap = __nullptr;
                                         
                                          NodeMap(INodeMap* nativeINodeMap_) { nativeINodeMap = nativeINodeMap_; }
                                          NodeMap(){}//rustine Activator::CreateInstance
                                         
                                          generic <typename ItemType> where ItemType : Node
                                          ItemType GetNode(String^ Name) //Retrieves the node from the central map by Name
                                          {
                                              ItemTypeNatif* p_item = nativeINodeMap->GetNode<ItemType>(NetString2gcstring(Name));
                                         
                                              if (p_item != __nullptr)
                                              {
                                                  ItemType itemWrapper = safe_cast<ItemType>(Activator::CreateInstance(ItemType::typeid/*,p_item*/); //Création d'une instance
                                                  itemWrapper->nativeIEnum = p_item; //rustine Activator::CreateInstance
                                                  return itemWrapper;                                    //Retour de la fonction GetNode<>
                                              }
                                              else { throw ("GetNode(" + Name + ") n'a pas trouvé de noeud."); }
                                         
                                          };
                                      };



                                      • Partager sur Facebook
                                      • Partager sur Twitter
                                      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                        1 juillet 2021 à 9:44:44

                                        Non, c'est une classe générique.

                                        En cherchant à partir de votre code, le ItemTypeNatif n'est pas définit

                                        >Je vous conseille encore une fois de voir le code source (ou reverse_engineerer) de SpinNET pour voir comment ils font leurs instanciations.

                                        INodeMap& nodeMap = cam.GetNodeMap();

                                        cam fait référence à IManagedCamera propre à SpinNET avec sa fonction (instanciation de l'exécutable Acqui.cs)

                                        Pour Acqui.cpp, avec pCam correspondant à CameraPtr propre aussi à SpinNET:

                                        INodeMap& nodeMap = pCam->GetNodeMap();
                                        public ref class EnumW : Node
                                        {
                                        public:
                                            IEnum* nativeIEnum = __nullptr;
                                            
                                            EnumW(IEnum* nativeIEnum_) { nativeIEnum = nativeIEnum_; }
                                            EnumW(){}//rustine Activator::CreateInstance
                                        };

                                        Je ne possède pas la classe IEnum, elle propre au code d'application C# qui est la classe fille de IValue.

                                        Dans votre exemple vous appellez "nativeIEnum" par rapport à la ligne du C#:

                                        IEnum iAcquisitionMode = nodeMap.GetNode<IEnum>("AcquisitionMode");

                                        Par la suite, la fonction est réutiliser par exemple pour

                                        IEnumEntry iAcquisitionModeContinuous = iAcquisitionMode.GetEntryByName("Continuous");

                                        et

                                        IString iDeviceSerialNumber = nodeMapTLDevice.GetNode<IString>("DeviceSerial);
                                        INodeMap& nodeMapTLDevice = cam.GetTLNodeMap();

                                        Est ce que pour pouvoir les utiliser je devrais faire de la même manière ? :

                                        if (p_item != __nullptr)
                                                {
                                                    ItemType itemWrapper = safe_cast<ItemType>(Activator::CreateInstance(ItemType::typeid/*,p_item*/); //Création d'une instance
                                                    itemWrapper->nativeIEnum = p_item; //rustine Activator::CreateInstance
                                                    return itemWrapper;                                    //Retour de la fonction GetNode<>
                                                }








                                        -
                                        Edité par VincentL10 1 juillet 2021 à 10:24:37

                                        • Partager sur Facebook
                                        • Partager sur Twitter
                                          1 juillet 2021 à 14:23:03

                                          >En cherchant à partir de votre code, le ItemTypeNatif n'est pas définit

                                          "ItemTypeNatif", c'est le type natif correspondant à la classe wrapper "ItemType".

                                          Il faut trouver un procédé pour passer du type paramétré du generic vers le type paramétré du template.

                                          J'ai pas ça sous la main, mais ça doit être disponible dans le code source de SpinNet.

                                          >cam fait référence à IManagedCamera propre à SpinNET avec sa fonction

                                          Voyez dans le code source de SpinNet comment c'est implémenté.

                                          >instanciation de l'exécutable Acqui.cs

                                          ??? Ca veut dire quoi ???

                                          >Pour Acqui.cpp, avec pCam correspondant à CameraPtr propre aussi à SpinNET:

                                          "CameraPtr" doit être assez proche des autres "PointerPtr".

                                          Si vous voulez rester proche de SpinNet, faites les mêmes classes avec les mêmes implémentations.

                                          Si vous voulez une API plus proche de l'API Native, "démontez" l'implémentation de SpinNet pour voir comment c'est fait.

                                          Mais il semble que SpinNet a été conçu pour simplifier l'API.

                                          >Je ne possède pas la classe IEnum, elle propre au code d'application C#

                                          L'application C# utilise l'API SpinNet ou l'API Native. Dans mes souvenirs, les interfaces I... étaient déclarées en C++, non ?

                                          >Dans votre exemple vous appellez "nativeIEnum" par rapport à la ligne du C#:

                                          Je suis perdu entre votre API Native et l'API .NET/C (et elle vient d'où ? de votre conception ? de SpinNET ? ....)

                                          Vous devez avoir une liste des classes déclarées/définies dans votre API Native, c'est vos briques de base.

                                          La tête de l'API .NET C#, c'est à vous de voir, soit très proche de l'API Native et donc vous devez comprendre comment elle fonctionne "intrinsèquement" (quit à voir comment SpinNET s'en sert pour implémenter la sienne d'API) ; soit c'est plus proche de SpinNET et vous devez comprendre comment elle l'implémente à partir de ces briques de base.

                                          Toujours pas de dépôt Git ?

                                          Le code d'exemple que j'au donné, c'est l'approche "clonage de l'API Native", donc aucun lien avec SpinNet. SpinNet, c'est juste pour voir leur implémentation des patterns de création.

                                          Si l'usage des templates dans une classe managée devient trop complexe, rien n'interdit de faire des instanciations "en dur".

                                          //nodeMap.GetNode<IEnum>("AcquisitionMode");
                                          nodeMap.GetNode_IEnum("AcquisitionMode");

                                          faudra juste ajouter des cast.

                                          C'est quoi votre API Native et l'API C# que vous voulez offrir ?

                                          • Partager sur Facebook
                                          • Partager sur Twitter
                                          Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                            2 juillet 2021 à 12:22:08

                                            >J'ai pas ça sous la main, mais ça doit être disponible dans le code source de SpinNet.

                                            Je ne possède que le programme de l'application. Le programme en C++ reconnait les différents appels tel que:

                                            CEnumerationPtr ptrAcquisitionMode = nodeMap.GetNode("AcquisitionMode");

                                            Alors qu'en C#, cela devient :

                                            IEnum ptrAcquisitionMode = nodeMap.GetNode<IEnum>("AcquisitionMode");

                                            Pour une même création d'objet :

                                            C++ :  (Le programme reconnait toutes les classes Genicam)

                                            INodeMap& nodeMap = pcam->getNodeMap():

                                            C# :

                                            INodeMap nodeMap = cam.getNodeMap():


                                            >??? Ca veut dire quoi ???

                                            C'était juste une indication pour vous indiquez ou se trouvait les lignes de codes

                                            >Si vous voulez rester proche de SpinNet, faites les mêmes classes avec les mêmes implémentations.

                                            Déjà fait dans le passé, en remplacer les en-têtes SpinNET par celles Genicam pour celles qui sont identiques et cela génère des erreurs dont celles de symboles de mémoire

                                            >Si vous voulez une API plus proche de l'API Native, "démontez" l'implémentation de SpinNet pour voir comment c'est fait.

                                            Vraimet désolé si c'est une répétition mais ou puis-je la trouver ?

                                            >L'application C# utilise l'API SpinNet ou l'API Native. Dans mes souvenirs, les interfaces I... étaient déclarées en C++, non ?

                                            Celles SpinNET, ils ont tous refait même les classes Genicam

                                            >Toujours pas de dépôt Git ?

                                            Oups, je m'en occupe dès que possible

                                            >Si l'usage des templates dans une classe managée devient trop complexe, rien n'interdit de faire des instanciations "en dur".

                                            Visual Studio me va t'il pas prendre cette fonction GetNode_IEnum pour une fonction indépendante et non GetNode ?

                                            >C'est quoi votre API Native et l'API C# que vous voulez offrir ?

                                            Je souhaite qu'à partir de Genicam, je puisse faire une application avec les fonctions qui sont disponible. Je ne sers de l’exécutable C# de SpinNET pour avoir un repère.


                                            J'ai réaliser cela : 

                                            #include "pch.h"
                                            
                                            #include <iostream>
                                            #include <ctime>
                                            
                                            #pragma warning (disable : 4996)
                                            
                                            using namespace std;
                                            using namespace GenTL;
                                            
                                            //Variables Globales
                                            TL_HANDLE* hTL;
                                            uint32_t* NumInterfaces;
                                            bool8_t* pbChanged;
                                            uint64_t iTimeout;
                                            char* IfaceID;
                                            //size_t& bufferSize;
                                            size_t* bufferSize;
                                            IF_HANDLE* hNewIface;
                                            uint32_t* NumDevices;
                                            char* DeviceID;
                                            DEV_HANDLE* hNewDevice;
                                            uint32_t* NumStreams;
                                            char* StreamID;
                                            size_t* buffersize = 0;
                                            DS_HANDLE* hNewStream;
                                            
                                            //Fonctions
                                            void InitLib(void) { GCInitLib(); }
                                            
                                            TL_HANDLE OpenTL(void) { TLOpen(hTL); return hTL; }
                                            
                                            IF_HANDLE OpenFirstInterface(TL_HANDLE hTL)
                                            {
                                            	TLUpdateInterfaceList(hTL, pbChanged, iTimeout);
                                            	TLGetNumInterfaces(hTL, NumInterfaces);
                                            	if (NumInterfaces > 0)
                                            	{
                                            		// First query the buffer size
                                            		TLGetInterfaceID(hTL, 0, IfaceID, bufferSize);
                                            		// Open interface with index 0
                                            		TLOpenInterface(hTL, IfaceID, hNewIface);
                                            		return hNewIface;
                                            	}
                                            }
                                            
                                            DEV_HANDLE OpenFirstDevice(IF_HANDLE hIF)
                                            {
                                            	IFUpdateDeviceList(hIF, pbChanged, iTimeout);
                                            	IFGetNumDevices(hTL, NumDevices);
                                            	if (NumDevices > 0)
                                            	{
                                            		// First query the buffer size
                                            		IFGetDeviceID(hIF, 0, DeviceID, bufferSize);
                                            		// Open interface with index 0
                                            		IFOpenDevice(hIF, DeviceID, 0, hNewDevice); // 0 rajouté
                                            		return hNewDevice;
                                            	}
                                            }
                                            
                                            DS_HANDLE OpenFirstDataStream(DEV_HANDLE hDev)
                                            {
                                            	// Retrieve the number of Data Stream 
                                            	DevGetNumDataStreams(hDev, NumStreams);
                                            
                                            	if (NumStreams > 0)
                                            	{
                                            		// Get ID of first stream using
                                            		DevGetDataStreamID(hDev, 0, StreamID, buffersize);
                                            		// Instantiate Data Stream
                                            		DevOpenDataStream(hDev, StreamID, hNewStream);
                                            	}
                                            
                                            	return hNewStream;
                                            }
                                            
                                            void CloseDataStream(DS_HANDLE hStream) { DSClose(hStream); }
                                            
                                            void CloseDevice(DEV_HANDLE hDevice) { DevClose(hDevice); }
                                            
                                            void CloseInterface(IF_HANDLE hIface) { IFClose(hIface); }
                                            
                                            void CloseTL(TL_HANDLE hTL) { TLClose(hTL); }
                                            
                                            void CloseLib(void) { GCCloseLib(); }
                                            
                                            // Main
                                            int main()
                                            {
                                            	time_t actuel = time(0);															// Déclaration de l'heure
                                            	tm *ltm = localtime(&actuel);
                                            	int Heure[3] = { ltm->tm_hour, ltm->tm_min, ltm->tm_sec };							// Mise des valeurs dans un tableau
                                            
                                            	InitLib();																			// Ouverture de la Libraire
                                            	TL_HANDLE hTL = OpenTL();															// Ouverture TL
                                            	IF_HANDLE hIface = OpenFirstInterface(hTL);											// Ouverture Interface
                                            
                                            	if (hIface == 0) { cout << " - Erreur : Interface fermée\t-" << endl; }				// Erreur
                                            
                                            	DEV_HANDLE hDevice = OpenFirstDevice(hIface);										// Ouverture du premier Appareil
                                            
                                            	if (hDevice == 0) {	cout << " - Erreur : Appareil fermé\t-" << endl; }				// Erreur
                                            
                                            	DS_HANDLE hStream = OpenFirstDataStream(hDevice);									// Ouverture du flux de données
                                            
                                            	if (hStream == 0) { cout << " - Erreur : Flux de donné fermé\t-" << endl; }			// Erreur
                                            
                                            	cout << "Reconnue" << endl;
                                            	cout << "- " << Heure[0] << "h " << Heure[1] << "m " << Heure[2] << "s -" << endl;	// Affichage de l'heure
                                            
                                            	// Code à mettre
                                            
                                            	CloseDataStream(hStream);															// Fermeture du flux de données
                                            	CloseDevice(hDevice);																// Fermeture du premier appareil
                                            	CloseInterface(hIface);																// Fermeture de l'Interface
                                            	CloseTL(hTL);																		// Fermeture du TL
                                            	CloseLib();																			// Fermeture de la Librairie
                                            
                                            	return 0;
                                            }

                                            Ceci est un code partiel trouver dans la documentation mais lorsque je l'exécute j’obtiens 18 erreurs du type:

                                            Program.obj : error LNK2001: symbole externe non résolu __imp_IFUpdateDeviceList

                                            Et ceci pour chaque fonction appelée. Faut-il aussi déclarer les fonctions ?


                                            -
                                            Edité par VincentL10 2 juillet 2021 à 13:39:53

                                            • Partager sur Facebook
                                            • Partager sur Twitter
                                              5 juillet 2021 à 13:54:54

                                              >Je ne possède que le programme de l'application.

                                              Oui, mais aussi la Dll contenant l'assembly de SpinNet, qui, sauf preuve du contraire, est facilement désassemblable et donc avoir UN code source équivalent. (Vous êtes sûr que le code de SpinNet n'est pas en Open Source ?)

                                              >Le programme en C++ reconnait les différents appels tel que ..."GetNode"

                                              Le code C++ utilise une des fonctionnalités du C++, la déduction de type template à la compilation.

                                              Le code C# utilise une des fonctionnalités de .NET, l'utilisation de type générique au runtime, mais utilise/cast le résultat de l'appel de manière "statique".

                                              La manière de faire est donc un peu différente entre les 2 codes mais au final, le résultat est le même, disposer d'une API fortement typées mais en utilisant un seul nom de fonction "GetNode" pour l'ensemble des types de nœuds possibles.

                                              Pour arriver à quelque chose, vous devez maîtriser un minimum .NET ET le C++ Natif.

                                              Si ces codes vous semblent "bizarres", c'est que vous n'avez pas encore l'aisance nécessaire. Révisez un peu les bases des langages/technologies.

                                              >Pour une même création d'objet :

                                              Je ne vois pas trop de différence entre les 2 codes. Le code C++ utilise un pointeur au lieu d'une référence. Ce qui est assez discutable, mais bon, c'est vraiment un détail sans grand importance.

                                              Si ces codes vous semblent "bizarres", ...

                                              Le Design Pattern utilisé ici, c'est le même, une "Factory Method". C'est un peu ce qui était fait dans mes exemples de code.

                                              Le code C# utilise-t-il SpinNET ?

                                              Si oui, suivez les Design Pattern qu'il utilise, à défaut de vous en servir directement. Il semble utiliser les mêmes que l'API C++, donc proche de l'API Native et ils doivent être compatibles avec une API .NET (par définition).

                                              >C'était juste une indication pour vous indiquez ou se trouvait les lignes de codes

                                              Je ne comprends toujours pas.

                                              >Déjà fait dans le passé, en remplacer les en-têtes SpinNET par celles Genicam .... des erreurs dont celles de symboles de mémoire

                                              Codes et message d'erreurs complets, SVP.

                                              >Vraimet désolé si c'est une répétition mais ou puis-je la trouver ?

                                              Comme déjà indiqué, soit via un Git si c'est de l'OpenSource, soit via le désassemblage de l'assembly.

                                              >Celles SpinNET, ils ont tous refait même les classes Genicam

                                              Oui, peut-être, mais à la vue du code C# et C++ de votre poste, les classes SpinNet semble être de simples wrappers autour des classes Natives, un peu ce que vous cherchez à faire, non ?

                                              Si votre objectif, c'est juste d'ajouter de nouvelles fonctions de Genicam à l'API, pourquoi ne pas juste ajouter ces fonctions aux wrappers de SpinNet ? (Voire faire du copier-coller du code SpinNet (le vrai ou le résultat du désassemblage) si vous ne voulez pas d’adhérence à l'assembly SpinNet)

                                              >Visual Studio me va t'il pas prendre cette fonction GetNode_IEnum pour une fonction indépendante et non GetNode ?

                                              Visual Studio n'est qu'un IDE, il ne fait que remonter les informations des compilateurs et des débogueurs. Il ne fait rien d'autres.

                                              Le compilateur ne fait pas de choix, c'est vous qui indiquer ce que le compilateur doit faire. S'il comprend de travers, c'est à vous de corriger votre code ou vos "réglages". C'est quoi une "fonction indépendante" ? Si c'est une fonction libre, ça pas possible en .NET, c'est pour cela qu'une API C (pas C++) n'est pas directement transposable en .NET (sans passer par une classe statique, par exemple).

                                              La méthode "GetNode_IEnum" n'aura aucun lien "automatique" avec "GetNode", c'est votre code qui fera le pont. C'est le plus gros inconvénient de cette approche "low tech".

                                              >Je souhaite qu'à partir de Genicam,

                                              Mais c'est quoi précisément ce "Genicam" ?

                                              Vous nous sortez une API C++ dans certains extraits mais le dernier code que vous avez posté est clairement une API C, pas C++.

                                              >Je ne sers de l’exécutable C# de SpinNET pour avoir un repère.

                                              Ok, comme source d'inspiration, mais rien ne vous empêche de récupérer le code pour le faire évoluer ou pour vous inspirer, car eux, ils ont déjà contourné toutes les difficultés de "l'exercice".

                                              >Ceci est un code partiel trouver dans la documentation

                                              Les codes des documentations ne sont pas des exemples à prendre au pied de la lettre car ils prennent beaucoup de liberté avec les règles de codages élémentaires pour "simplifier" le code et aller à l'essentiel de le chose à documenter.

                                              Ce code est donc tout pourri, mais c'est peut-être venu de la documentation, mais c'est à vous de programmer "proprement".

                                              >mais lorsque je l'exécute j’obtiens 18 erreurs du type:

                                              Vous ne l'exécutez pas car l'édition de lien (le machin entre la compilation et la création du ou des fichiers binaires/exécutables) est tombé en erreur.

                                              La compilation c'est "bien" passé (sinon, il y aurait une erreur de compilation, pas une erreur d'édition de lien), ce n'est donc pas des problèmes de "déclaration". Les fichiers d'en-tête (.h) sont donc, à priori, correctement inclus et la configuration du compilateur est OK.

                                              Il y a ce type d'erreur d'édition de lien quand : soit on oubli d'ajouter un .lib ( ou un .lib d'une Dll) à la liste des fichiers d'entrée de l'éditeur de lien, soit on configure mal l'éditeur de lien pour lui indiquer où chercher ces fameux .lib (peu probable (cf. message d'erreur) mais après avoir corriger le premier cas, on se prend souvent le second).

                                              Donc, vérifiez dans la documentation comment configurer les options de l'éditeur de lien.

                                              Quelques remarques sur le dernier code posté:

                                              C'est une API C et non C++ qui est utilisé.

                                              C'est une API qui est donc plus "lourde" à utiliser et est forcément plus éloigner d'une API utilisable en .NET (qui n'accepte pas de fonctions libres, et en C, il n'y a QUE des fonctions libres).

                                              Une API C++ "de base" sera forcsment plus simple à wrapper qu'une API C, car l'utilisation de concept C++ comme le RAII réduirait drastiquement la quantité de code et améliorerait énormément la "qualité" du code. (plus de Open et de Close à la con, remplacés par les Design Pattern de création et les appels automatiques des destructeurs).

                                              Les remarques suivantes sont donc dans le cadre d'une utilisation de l'API C dans un code C++ Natif, et non dans le cadre d'une utilisation d'une API C++ correctement conçue où le code serait radicalement plus simple.

                                              Ligne 6 : c'est quoi ce cache-misère tout pourri. S'ils sont pas foutu de gérer correctement obsolescence de leur code, ça promet. ON DÉGAGE CETTE COCHONNERIE.

                                              Ligne 8 et 9 : comme les noms de type des différentes API semblent adorer des trucs comme "System", n'utilisez pas ces using A LA CON !

                                              Le code est toujours plus clair sans ces "using" qu'avec.

                                              Ligne 11 à 26 : des variables globales, sérieux. o_O:colere2:

                                              On dégage tout ça et on utilise correctement le passage de paramètres. (et les champs quand on fait du "vrai" C++).

                                              Ligne 29 : Utilisation habituelle en C d'une fonction d'initialisation d'une librairie, mais qui n'a de sens que dans un context de code "portable" d'une librairie statique. Normalement, vous n'êtes pas dans ce cadre, vous êtes dans un code non portable utilisant des Dll (Windows).

                                              Est-ce qu'il y a une peau de banane qui empêche d'utiliser "DllMain" ??? (cf. la documentation)

                                              L'API C++ devrait être différente sur ce point , non ?  (je l'espère)

                                              Ligne 31 à 83 : Code super sale, utilisant des variables globales à tir-la-Rigaud, extrêmement fragile (exceptions, thread-safety, etc...), qui devrait être supprimé avantageusement en utilisant correctement de Dessign Pattern de création de l'API C++ et les destructeurs.

                                              Ligne 85 : Comme ligne 29, pour du déchargement (sic!).

                                              Ligne 88 à 120 : Code purement C avec toutes les idiomatiques du C (tableau à la C, des pointeurs à la con, etc...). C'est pas parce qu'on utilise une API C qu'on doit utiliser les mêmes "aberrations" dans le code client, qui lui est censé être en C++ et pas être pollué (ou très légèrement) par l'utilisation d'une API C.

                                              Vous devez maîtriser assez le C++ pour ne pas avoir à recopier ces "cochonneries" venant d'une documentation "C" et non d'une documentation C++.

                                              Avant de tout refaire en "vrai" C++, je pense que vous devriez investir quelques heures à la correction des erreurs d'édition de lien de ce "machin", car cela vous permettra de mieux appréhender comment fonctionne la compilation et l'édition de lien en C++ (Ce code est un vieux truc en C mais vous utilisez un compilateur C++ pour le compiler/lié).

                                              • Partager sur Facebook
                                              • Partager sur Twitter
                                              Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                                5 juillet 2021 à 14:30:24

                                                Quand j'utilises des logiciels tel que depends.exe ou dllexp.exe pour ouvrir la dll, il me le met en erreur. Le SpinNET n'est pas open source, on peut trouver ce que je possède sur internet mais rien de plus, pas de fichiers sources qui détaille les méthodes. 

                                                Le code C# utilise-t-il SpinNET ?

                                                Oui pour les exemples

                                                >Oui, peut-être, mais à la vue du code C# et C++ de votre poste, les classes SpinNet semble être de simples wrappers autour des classes Natives, un peu ce que vous cherchez à faire, non ?

                                                oui c'est cela, mais pas totalement, car les fichiers qu'ils sont en commun sont les même et j'y arrive pas les refaire (problème de symbole de mémoire).

                                                >Ok, comme source d'inspiration, mais rien ne vous empêche de récupérer le code pour le faire évoluer ou pour vous inspirer, car eux, ils ont déjà contourné toutes les difficultés de "l'exercice".

                                                J'y ai pas accès, j'ai que leur en-têtes, dlls et symboles.

                                                >Ligne 6

                                                Leur code n'est que partiel et très incomplet, j'ai essayé de corriger les erreurs en fonctions de ce qu'elles m'indiquait.

                                                >Ligne 11 à 26

                                                Beaucoup d'erreur à cause de la déclaration des variables, j'ai suivi le mieux que possible en fonction de la documentation.

                                                Est-ce qu'il y a une peau de banane qui empêche d'utiliser "DllMain" ??? (cf. la documentation)

                                                Je possède plusieurs dll mais aucune qui se démarque des autres (peut-être le GenAPI mais il n'est pas mis en avant).

                                                On ne peut pas les ajouter en référence: "Vérifier que ce fichier est accessible et qu'il s'agit d'un assembly ou d'un composant COM valide.

                                                >Ligne 31 à 83

                                                Provenant de la documentation


                                                *******************************************************************

                                                Ce projet qui m'a été assigné a été mis en suspend (pour les langages C++, C#). Je tenais à vous remercier pour votre patience et votre aide que vous m'avez apporté tous au long de ce problème. En vous souhaitant une bonne continuation pour la suite.

                                                -
                                                Edité par VincentL10 5 juillet 2021 à 16:11:17

                                                • Partager sur Facebook
                                                • Partager sur Twitter
                                                  5 juillet 2021 à 17:11:40

                                                  >Quand j'utilises des logiciels tel que depends.exe ou dllexp.exe pour ouvrir la dll, il me le met en erreur.

                                                  Quelles "erreurs", SVP.

                                                  Ce type d'outil émet souvent des "warning", car cela demande des configurations pointilleuses pour que cela fonctionne de manières optimum.

                                                  Depends.exe (et vraisemblablement dllexp.exe) sont fait pour donner les API/symboles exportés "Natifs", pas les API type COM ou .NET.

                                                  Il faut utiliser des outils dédiés à la technologie employée pour avoir plus d'information que juste 2 fonctions qui se battent en duel, ou totalement noyé dans une API native qui ne vous intéresse pas.

                                                  Si vous cherchez à analyses l'API de SpinNet, Visual Studio contient tout ce qu'il faut pour récupérer automatiquement l'API publique des assemblies .NET (pas une Dll au pif) que vous ajoutez à un projet.

                                                  >Le SpinNET n'est pas open source, ... sur internet mais rien de plus, pas de fichiers sources qui détaille les méthodes.

                                                  Si l'assembly SpinNET n'est pas spécifiquement protégé contre le reverse-engeneering, vous n'avez besoin que du fichier .dll de l'assembly pour récupérer UN code source via des outils comme "Reflector" ou le byte-code IL via des outils comme ILDASM (vue la complexité assez faible envisagée du code de SpinNet, le byte-code peut être très simple et donc facilement transposable en un langage .NET quelconque).

                                                  >(Le code C# utilise-t-il SpinNET ?)Oui pour les exemples

                                                  OK, le code C# et le code C++ que vous donnez sont très proches. Alors le code C++ donnés est-il une utilisation C++ de l'API de SpinNET (une API C++ native ou une API .NET utilisée par du code C++/CLI ?) ? Si ce n'est pas le cas, c'est que SpinNET a une API très proche de l'API C++ Native et est donc une coquille assez vide et le "vrai" code utile est dans la librairie C++ Native (donc assez facile de "schinter" SpinNet).

                                                  Si l'API C++ (Genicam ?) est assez éloigné de l'API SpinNet, il serait bien plus éclairant d'avoir un code C++ qui utilise cette API plutôt qu'un code C++ qui utilise SpinNet.

                                                  >oui c'est cela, mais pas totalement, car les fichiers qu'ils sont en commun sont les même et j'y arrive pas les refaire (problème de symbole de mémoire).

                                                  Fichiers en commun ??? C'est pas clair.

                                                  Montrez le ou les fichiers des classes natives, les fichiers des classes SpinNet (ou ce foutu fichier .dll contenant l'assembly SpinNet que vous ne nous avez toujours pas fourni malgré de multiples demandes !), ce que vous avez bricolé et les messages d'erreurs exacts (parce que "symbole de mémoire", ça veut absolument rien dire).

                                                  >J'y ai pas accès, j'ai que leur en-têtes, dlls et symboles.

                                                  Comme indiqué mainte et mainte fois, vous n'avez besoin que de la Dll de l'assembly et un desassembleur.

                                                  Vous faites porter à la documentation un chapeau bien trop grand pour lui.

                                                  C'est clairement une documentation C d'une API C que vous avez suivi. Elle documente comment appeler ce qu'elle présente en C 'à l'arrache".

                                                  C'est à vous de comprendre un peu comment fonctionne une API C et aussi le langage C++.

                                                  Ce n'est pas une documentation de comment coder en C, ou en C++ mais juste de l'API.

                                                  Donc ne suivez pas "bêtement" la documentation.

                                                  Vos "justifications" montrent que vous êtes complètement à la rue au niveau C et C++.

                                                  Commencez par apprendre un minimum sur ces langages et pas vous attendre à ce que cela se mettent à fonctionner par l'action du Saint-Esprit.

                                                  Tant que vous ne comprenez pas mes remarques (ce que je déduis de votre plaidoyer "c'est pas moi, c'est comme ça dans la documentation"), c'est que vous ne maîtrisez pas assez ni le C, ni le C++.

                                                  Si vous utilisez une API C++, pas besoin de maîtrise le C, mais alors pourquoi nous en coller une plâtrée d'utilisation d'API C comme exemple ?

                                                  >Je possède plusieurs dll mais aucune qui se démarque des autres

                                                  Je crois qu'on n'a déjà eu ce type de discussion mais avec les fichiers d'en-tête.

                                                  La réponse est la même : Utilisation des en-tête "chapeaux".

                                                  Les en-têtes permettent de faire le lien avec la ou les lib à utiliser et la ou les lib à utiliser donne la ou les dll à utiliser (et à comprendre).

                                                  >On ne peut pas les ajouter en référence: ... et qu'il s'agit d'un assembly ou d'un composant COM valide.

                                                  Je crois que j'ai déjà répondu à ce problème depuis le début, on ne peut pas ajouter une référence dans un projet .NET à une Dll qui n'est pas un assembly .NET.

                                                  Vous devez au minimum comprendre comment s'articules les différentes API (C, C++, SpinNet). Qui utilise qui. etc...

                                                  Avez-vous au moins lu la documentation dans son ensemble ?

                                                  Si vous avez du mal à comprendre des passages, on est là. ;)

                                                  P.S.: Essayez de répondre aux questions qu'on vous pose (toutes) (et pas juste à quelques-unes au pif), SVP.

                                                  • Partager sur Facebook
                                                  • Partager sur Twitter
                                                  Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                                    8 juillet 2021 à 14:42:39

                                                    Par exemple pour depends.exe:

                                                    Errors were detected when processing SpinNETd_v140.dll. See the log window for details.


                                                    Fichiers en commun ??? C'est pas clair.

                                                    Oui les titres et les contenus (à quelques lignes près) sont les mêmes mais pas les espace des noms entre autre.

                                                    >Montrez le ou les fichiers des classes natives,les fichiers des classes SpinNet

                                                    INodeMap.h de Genicam

                                                     GENICAM_INTERFACE GENAPI_DECL_ABSTRACT INodeMap
                                                        {
                                                            //! Retrieves all nodes in the node map
                                                            virtual void GetNodes(NodeList_t &Nodes) const =  0;
                                                    
                                                            //! Retrieves the node from the central map by Name
                                                            virtual INode* GetNode( const GENICAM_NAMESPACE::gcstring& Name) const = 0;
                                                    
                                                            //! Invalidates all nodes
                                                            virtual void InvalidateNodes() const =  0;
                                                    
                                                            //! Connects a port to a port node with given name
                                                            virtual bool Connect( IPort* pPort, const GENICAM_NAMESPACE::gcstring& PortName) const = 0;
                                                    
                                                            //! Connects a port to the standard port "Device"
                                                            virtual bool Connect( IPort* pPort) const = 0;
                                                    
                                                            //! Connects a port to a port node with given name
                                                            virtual bool Connect(IPortStacked* pPort, const GENICAM_NAMESPACE::gcstring& PortName) = 0;
                                                    
                                                            //! Connects a port to the standard port "Device"
                                                            virtual bool Connect(IPortStacked* pPort) = 0;
                                                    
                                                            //! Get device name
                                                            /*! The device name identifies a device instance, e.g. for debugging purposes.
                                                            The default ist "Device". */
                                                            virtual GENICAM_NAMESPACE::gcstring GetDeviceName() = 0;
                                                    
                                                            //! Fires nodes which have a polling time
                                                            virtual void Poll( int64_t ElapsedTime ) = 0;
                                                    
                                                            //! Returns the lock which guards the node map
                                                            virtual CLock& GetLock() const = 0;
                                                    
                                                            //! Get the number of nodes in the map
                                                            virtual uint64_t GetNumNodes() const  = 0;
                                                    
                                                            //! Parse all Swissknife equations
                                                            virtual bool ParseSwissKnifes( GENICAM_NAMESPACE::gcstring_vector *pErrorList = NULL ) const = 0;
                                                    
                                                            //! Create a new write concatenator object
                                                            virtual CNodeWriteConcatenator *NewNodeWriteConcatenator() const = 0;
                                                    
                                                            //! Execute the transaction
                                                            virtual bool ConcatenatedWrite(CNodeWriteConcatenator *, bool featureStreaming = true, GENICAM_NAMESPACE::gcstring_vector *pErrorList = NULL) = 0;
                                                    
                                                        };
                                                    
                                                    

                                                    Voici, le INodeMap.h de SpinNET

                                                            interface SPIN_API_ABSTRACT INodeMap
                                                            {
                                                                /**
                                                                 * Retrieves all nodes in the node map
                                                                 */
                                                                virtual void GetNodes(NodeList_t & Nodes) const = 0;
                                                    
                                                                /**
                                                                 * Retrieves the node from the central map by Name
                                                                 */
                                                                virtual INode* GetNode(const GenICam::gcstring& Name) const = 0;
                                                    
                                                                /**
                                                                 * Invalidates all nodes
                                                                 */
                                                                virtual void InvalidateNodes() const = 0;
                                                    
                                                                /**
                                                                 * Connects a port to a port node with given name
                                                                 */
                                                                virtual bool Connect(IPort * pPort, const GenICam::gcstring& PortName) const = 0;
                                                    
                                                                /**
                                                                 * Connects a port to the standard port "Device"
                                                                 */
                                                                virtual bool Connect(IPort * pPort) const = 0;
                                                    
                                                                /**
                                                                 * Get device name
                                                                 * The device name identifies a device instance, e.g. for debugging purposes.
                                                                 * The default is "Device".
                                                                 */
                                                                virtual GenICam::gcstring GetDeviceName() = 0;
                                                    
                                                                /**
                                                                 * Fires nodes which have a polling time
                                                                 */
                                                                virtual void Poll(int64_t ElapsedTime) = 0;
                                                    
                                                                /**
                                                                 * Returns the lock which guards the node map
                                                                 */
                                                                virtual CLock& GetLock() const = 0;
                                                    
                                                                /**
                                                                 * Get the number of nodes in the map
                                                                 */
                                                                virtual uint64_t GetNumNodes() const = 0;
                                                            };

                                                    >Vos "justifications" montrent que vous êtes complètement à la rue au niveau C et C++.

                                                    Oui je n'ai pas beaucoup d'expérience dans ces langages

                                                    Avez-vous au moins lu la documentation dans son ensemble ?

                                                    Oui et pas qu'une fois

                                                    >P.S.: Essayez de répondre aux questions qu'on vous pose (toutes) (et pas juste à quelques-unes au pif), SVP.

                                                    Oups, excusez moi pour ca, il se peut que j'y passe à côté.

                                                    • Partager sur Facebook
                                                    • Partager sur Twitter
                                                      12 juillet 2021 à 12:43:28

                                                      >Par exemple pour depends.exe:

                                                      C'est pas très explicite.

                                                      Le plus simple, c'est de nous fournir cette Dll.

                                                      Avec un nom pareil : "SpinNETd_v140.dll", il y a de grandes chances qu'elle n'offre pas qu'une API .NET et/ou COM, mais aussi une API C ou C++.

                                                      >Oui les titres et les contenus (à quelques lignes près) sont les mêmes mais pas les espace des noms entre autre.

                                                      Ces fichiers montrent que SpinNet fournit aussi une API C++ natif proche de celle de Genicam.

                                                      Le changement d'espace de noms permet de "facilement" travailler en même temps avec l'API de SpinNet et l'API Genicam sous-jacente.

                                                      Les fichiers que vous donnez montrent que l'une des API proposée par SpinNet est extrêmement proche de l'API Genicam.

                                                      Vous, ce que vous avez à faire, du moins dans un premier temps, c'est exactement la même chose que l'API C++ "standard" de SpinNet mais en .NET.

                                                      Comme SpinNet fournit aussi une API .NET, il y a des chances que cette déclaration d'API .NET existe aussi dans SpinNET. Mais comme il n'ai pas nécessaire d'avoir un ou des .h pour s'en servir mais juste la Dll en elle-même, il est peu probable que vous ayez l'API .NET .NET sous la forme d'un .h.

                                                      Si ce .h existe, il devrait être très proche de celui que vous avez posté : un autre espace de nom, des "^" .NET à la place des "*" des pointeurs nu C++ natifs et peut-être une ou deux MACRO différentes.

                                                      Il doit être possible de trouver des outils qui extraient ces API .NET en fichier .h. Exactement comme "#import" dans un fichier source pour que Visual Studio génère les fichiers .h des composants COM (il y a des chances que l'API .NET de SpinNet soit doublonnée en API COM).

                                                      Si ces outils, qui ne sont pas absolument nécessaire à la bonne utilisation de l'API .NET, n'existent pas, il est assez facile de créer les .h soit même en utilisant les données récupérées via les désassembleurs .NET que vous semblez délibérément snober.

                                                      Dans les grandes lignes, pour faire une API .NET à partir d'une API C++, c'est de remplacer les types Natif C++ par leur équivalent .NET, remplacer les pointeurs nus "*" et les références C++ "&" par des références .NET "^".

                                                      • Partager sur Facebook
                                                      • Partager sur Twitter
                                                      Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.
                                                        16 juillet 2021 à 11:44:42

                                                        >Les fichiers que vous donnez montrent que l'une des API proposée par SpinNet est extrêmement proche de l'API Genicam.

                                                        Pour moi, a travers plusieurs vérifications, il s'agit de copier-coller à quelques lignes près.

                                                        > il est peu probable que vous ayez l'API .NET .NET sous la forme d'un .h.

                                                        Effectivement, je ne la possède pas.

                                                        >il est assez facile de créer les .h

                                                        J'ai tenter en redéfinissant en fonction de Genicam, sans succès

                                                        >récupérées via les désassembleurs .NET

                                                        Comme par exemple dotPeek ?


                                                        • Partager sur Facebook
                                                        • Partager sur Twitter
                                                          16 juillet 2021 à 14:16:48

                                                          >il s'agit de copier-coller à quelques lignes près.

                                                          Pas très étonnant, mais les quelques lignes "près" peuvent faire la différence entre une API simple d'utilisation et une horrible à utiliser.

                                                          Mais, vous, vous devez faire une API .NET pas une API C++. Voir ci-après "dotPeek".

                                                          >Comme par exemple dotPeek ?

                                                          Oui, et il est peut-être possible qu'il génère du C++/CLI, pour que vous ayez des exemples concrets d'interface .NET déclarées en C++/CLI, et assez proche de celles que vous voulez .

                                                          Normalement, SpinNet a fait le wrapping de l'API C++ vers une API .NET. En récupérant via dotPeek, ce code de wrapping, vous avez fait la plus gros du travail. Vous n'aurez qu'à légèrement modifié l'API .NET pour quelle colle à la vôtre et pas à l'une de SpinNet.

                                                          • Partager sur Facebook
                                                          • Partager sur Twitter
                                                          Je recherche un CDI/CDD/mission freelance comme Architecte Logiciel/ Expert Technique sur technologies Microsoft.

                                                          Problème de DLL

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