none
Interface C++ - VB.NET RRS feed

  • Discussion générale

  • Bonjour,

    Ma solution VS2010 comporte deux projets : l'un en C++, l'autre en VB.Net.

    L'une des classes C++ comporte une fonction qui prend comme valeur une structure FORME telle que définie ci-dessous.

     

    	typedef struct stpointf {
    		double x;
    		double y;
    	} POINTF;
    
    	typedef struct stforme {
    		std::vector<POINTF> axe1;
    		std::vector<POINTF> axe2;
    	} FORME;
    
    

     

    la fonction en question est la suivante :

     

    FORME Reconnait(char * pathImage, POINTF p, double echelle);
    

     

    Ma question : comment accéder à cette fonction et récupérer sa valeur depuis le projet VB.Net ?

    L'éditeur de texte du projet VB ne montre d'ailleurs pas cette fonction membre. Cela provient peut-être justement du type de valeur, non connue en VB.NET...

    Merci de votre aide.

     


    Alain
    vendredi 15 avril 2011 21:53

Toutes les réponses

  • VB.NET ne connait "nativement" que les types .NET (ou managé, c'est la même chose).

    Généralement, quand on découpe une solution en plusieurs projets, on essaye de réduire le couplage entre les différents éléments.

    Cela évite d'avoir le syndrome du monolithe, où on dispose de plein de briques mais où l'on est obligé de toutes les prendre et de ne prendre quelles.

    Cela réduit à néant tout l'intérêt de découper un projet.

    Je dis cela car passer une structure contenant des std::vector entre 2 composants logiciels, même tout les 2 en C++, les rendent très dépendants.

    Il faut compiler les deux composants avec les mêmes compilateurs, les mêmes options de compilation, les mêmes éventuelles librairies.

    C'est pas super souple comme approche.

    Je ne suis pas en train de répondre précisément à votre question, mais je trouve que vous vous trouvez dans cette situation car vous avez vraisemblablement fait un mauvais choix en amont du problème.

    Si vous êtes dans un environnement .NET, vous pouvez vous permettre plus de flexibilité entre les types d'objets passés entre les modules (assemblies en .NET) car c'est un des atouts d'avoir un Framework système comme .NET. Mais dans ce cas, utilisez des objets .NET comme les List<T>(http://msdn.microsoft.com/fr-fr/library/6sh2ey19.aspx), ou encore mieux, des IList<T> (http://msdn.microsoft.com/fr-fr/library/5y536ey6.aspx), pour rendre les utilisateurs ces structures indépendants de son implémentation.

    Si vous avez récupéré ces structures depuis du code C++ natif, il reste l'option du C++/CLI pour wrapper ces structures dans des objets .NET, voir faire un mapping direct mémoire entre .NET et le code natif, mais c'est bien plus complexe et avec des classes comme std::vector, cela ne sera que très moyennement portable.

    Je pense donc que le plus simple est de revoir votre mode de communication entre ces modules logiciels.


    Paul Bacelar, Ex - MVP VC++
    samedi 16 avril 2011 17:37
    Modérateur
  • Bonsoir Paul et merci pour votre réponse.

    Mais le monde n'est pas si simple et on n'a pas toujours le choix des armes. Je travaille en principe sous .NET (VB et C#) et je m'en trouve très bien. Mais mon problème est ici celui de l'intégration de travaux importants réalisés en C++ natif par d'autres équipes et utilisant, entre autres, des bibliothèques publiques (comme ITK, spécialisée en traitement d'images).

    Comme il n'est pas question de réécrire tout cela en C#, n'est-ce pas ? Il est alors nécessaire d'intégrer ces logiciels et de passer des structures de données d'un monde à l'autre (par exemple celles de la fonction Reconnait ci-dessus).

    S'il n'est pas possible de les passer en paramètres mémoire, il est au moins possible des les passer dans un fichier texte. Mais j'avoue que ce n'est guère glorieux.

    Vous évoquez l'option du C++/CLI pour wrapper ces structures dans des objets .NET, voir faire un mapping direct mémoire entre .NET et le code natif. C'est sans doute LA solution. Comment la mettre en oeuvre . Avez-vous un exemple ?

    Je précise que l'intégration se fait en source.

    Merci de vos avis.

     


    Alain
    samedi 16 avril 2011 21:00
  • Si l'interface d'une librairie utilise des structures contenant des std::vector, elle n'est vraiment pas bien conçu. Ou vous utilisez la mauvaise interface.

    Le passage d'un langage .NET au C++ natif utilisant des std::vector, devrait, pour moi, utiliser le C++/CLI.

    Vous créez une classe .NET en C++/CLI qui encapsulera les accès à la classe native "FORME".

    Votre code VB.NET n'aura plus qu'à utiliser cette classe de wrapping à la place de la classe native "FORME".

     

    L'autre solution, est complexe et extrêmement fragile. A moins d'avoir de très grosses contraintes de performances et très très peux en portabilité, je vous déconseille d'essayer le mapping mémoire.


    Paul Bacelar, Ex - MVP VC++
    dimanche 17 avril 2011 00:32
    Modérateur
  • Bonjour Paul, Merci de votre réponse.

    Mais je ne suis pas sur de tout comprendre...

    Je dispose de :

    - des classes sources en C++ natif (développés sous Unix) et que je dois utiliser,

    - des classes en C++/CLI (donc en code managé n'est-ce pas ?), à écrire, où sont définies les structures POINTF et FORME

    - une appli en VB.NET

    Il y a donc deux adaptations d'interface à réaliser : VB.NET <-> C++/CLI d'une part, et C++/CLI<->C++ natif d'autre part.

    Les structures POINTF et FORME ne sont pas imposées par le C++ natif. C'est moi qui les propose, peut-être à tort si je comprends bien...

     

    Les données à passer entre VB.NET et C++/CLI sont :

    VB.NET --> C++/CLI : un path vers un fichier, les coordonnées d'un point et un scalaire.

    C++/CLI --> VB.NET : une liste de points (leurs coordonnées) de longueur variable. C'est pour cela que j'ai pensé à utiliser un vecteur. Y-a-t'il une meilleure solution ?

     

    A cette heure, je n'ai pas d'informations précises sur l'interface C++/CLI <-> C++ natif.

    Merci de votre aide.


    Alain
    lundi 18 avril 2011 10:02
  • Exemple fait à l'arrache de code C++/CLI, création d'un type .NET "FormeNet" utilisable directement en VB.NET (Les tuples c'est en .NET4.0) et d'une classe "MyAPI" contenant une méthode static appelable en VB.NET et utilisant les structures C++ que vous nous avez fournis.

    Ce n'est qu'un exemple qui montre que le C++/CLI attaque aussi bien le Natif C++ que les types .NET.

    FormeNet.h :

     

    #pragma once
    
    using namespace System;
    using namespace System::Collections::Generic;
    
    ref class FormeNet
    {
    public:
    	FormeNet(void){};
    
    	property IList<Tuple<Double,Double>^>^ axe1;
    	property IList<Tuple<Double,Double>^>^ axe2;
    };
    

     

    // CPlusPlusCliDll.h
    
    #pragma once
    #include <vector>
    #include <string>
    #include <Vcclr.h>
    #include "form.h"
    
    using namespace System;
    
    typedef struct stpointf {
    	double x;
    	double y;
    } POINTF;
    
    typedef struct stforme {
    	std::vector<POINTF> axe1;
    	std::vector<POINTF> axe2;
    } FORME;
    
    FORME tata(std::string path, double x, double y, double echelle)
    {
    	FORME retValue;
    	POINTF POINTF1;
    	POINTF POINTF2;
    
    	POINTF1.x = 0;
    	POINTF1.x = 1;
    	POINTF2.x = 3;
    	POINTF2.x = 4;
    
    	retValue.axe1.push_back(POINTF1);
    	retValue.axe2.push_back(POINTF2);
    	return retValue;
    }
    
    namespace CPlusPlusCliDll {
    
    	public ref class MyAPI
    	{
    		static FormeNet^ toto(String^ path, Double x, Double y, Double echelle)
    		{
    			FormeNet^ retValue = gcnew FormeNet();
    
    			pin_ptr<const wchar_t> w_path = PtrToStringChars(path);
    
    			size_t convertedChars = 0;
    			size_t sizeInBytes = ((path->Length + 1) * 2);
    			errno_t err = 0;
    			char  *ch = (char *)malloc(sizeInBytes);
    
    			err = wcstombs_s(&convertedChars, 
    				ch, sizeInBytes,
    				w_path, sizeInBytes);
    			if (err != 0)
    				printf_s("wcstombs_s failed!\n");
    
    
    			FORME titi = tata(ch,x,y,echelle);
    
    			retValue->axe1 = gcnew List<Tuple<Double,Double>^>();
    			retValue->axe2 = gcnew List<Tuple<Double,Double>^>();
    
    			for(int i=0 ; i< titi.axe1.size(); i++)
    			{
    				retValue->axe1->Add(gcnew Tuple<Double,Double>(titi.axe1[i].x,titi.axe1[i].y));
    			}
    
    			for(int i=0 ; i< titi.axe2.size(); i++)
    			{
    				retValue->axe2->Add(gcnew Tuple<Double,Double>(titi.axe2[i].x,titi.axe2[i].y));
    			}
    
    			return retValue;
    		}
    	};
    }
    
    
    

    Paul Bacelar, Ex - MVP VC++
    mardi 19 avril 2011 00:53
    Modérateur
  • Bonjour Paul,

    Un grand merci pour ce code que je n'ai pas encore essayé mais qui me parait très clair. Je ne l'aurais pas trouvé tout seul, notamment quant à l'usage des Tulpes et des conversions de string.

    Si je comprends bien, le problème de la portabilité 32/64 bits se situe uniquement au niveau de l'allocation du buffer ch dans la méthode toto. En 64 bits le sizeInBytes doit être x 2.

    Je crois savoir qu'il est possible d'accéder aux paramètres système qui fournissent l'information. Savez-vous comment on fait ?

    Merci encore pour votre aide.

    Bien cordialement


    Alain
    mardi 19 avril 2011 08:05
  • Houlà, mon code à l'arrache n'a pas été conçu pour lu code mixte 32/64bits.

    Le "*2" dans le code n'est pas lié à du 32/64Bits mais au fait que toutes les chaînes .NET sont en UNICODE 16-bits et que "char" est un type contenant un caractère ASCII-étendu 8-bits.

    Le "*2", c'est qu'une chaîne de x caractères "wide" (UNICODE) peut être convertis en une chaîne de caractères MBS (ASCII-étendu spéciale M$) 2 fois plus longues, en terme de nombre de caractères.


    Paul Bacelar, Ex - MVP VC++
    mardi 19 avril 2011 23:18
    Modérateur
  • J'ai mis en place votre code et rectifié une toute petite erreur (dans la fonction tata où manque l'affectation des y). Tout fonctionne bien, le code VB.NET communique effectivement avec le C++ et les paramètres sont bien passés. Je vous remercie encore beaucoup.

    Cependant, mon appli doit s'exécuter indifféremment sur des ordi 32 et 64 bits. C'est le cas actuellement avec du code managé seul. L'introduction de C++ poserait de sérieuses difficultés ?

    Merci et bonne journée.


    Alain
    mercredi 20 avril 2011 06:42
  • Bonjour, AchLog,

     

    Est-ce que les dernières explications de Paul sont utiles pour résoudre votre problème ? Merci pour partager les résultats, afin que d'autres personnes avec le même problème puissent profiter de cette solution.

     

    Bonne journée,

    Cipri


    Suivez MSDN sur Twitter   Suivez MSDN sur Facebook


    Ciprian DUDUIALA, MSFT  
    •Nous vous prions de considérer que dans le cadre de ce forum on n’offre pas de support technique et aucune garantie de la part de Microsoft ne peut être offerte.

    mercredi 20 avril 2011 09:19
  • Bonjour Ciprian,

    Oui, les informations transmises par Paul sont, non seulement utiles, mais essentielles pour résoudre le problème que je rencontre.

    Je crois avoir voté "utile" les concernant. Cependant, toutes les questions ne sont pas entièrement résolues.

    Je ne sais pas comment faire mieux pour les partager avec d'autres personnes. Merci d'éclairer ma lenterne.

    Bonne journée à vous.


    Alain
    mercredi 20 avril 2011 12:27
  • Ce code C++/CLI est mixte, un partie managée donc compilée à la volée dans un environnement qui peut être 32 ou 64 bits. Mais une autre partie est native et le format du module généré par le compilateur est fonction des réglages du compilateur et ne pourra être que 32bits ou que 64bits.

    Il est impossible d'executer du code 32 bits dans un processus 64 bits et vis-versa.

    La limitation dû à la dll C++/CLI mixte existe aussi avec les dll natives.

    Ce n'est donc pas C++/CLI qui restreint les possibilités.

    A moins d'être en code managé de bout en bout, vous ne pourrez pas avoir le même code qui s'exécute en 32 ou 64 bits.

    Vous pouvez porter tout le code natif en code managé et vous pourrez avoir un seul exécutable.

    Mais vous pouvez aussi générer 2 versions des dll natives et mixtes, le programme d'installation se chargeant d'installer les bonnes versions correspondant à l'OS 32 ou 64 bits.

    Vous pouvez aussi interdire l'usage du 64bits pour le code managé, et dans ce cas, vous serrez toujours en 32bits sur n'importe quel OS.

    La compatibilité 32/64 bits doit-elle être au niveau binaire, ou au niveau du source ?


    Paul Bacelar, Ex - MVP VC++
    mercredi 20 avril 2011 20:51
    Modérateur
  • Merci Paul. Votre réponse est très claire.

    Il est nécessaire que l'appli puisse être utilisée indifféremment par des utilisateurs équipés de machines 32 ou 64 bits.

    La compatibilité doit donc seulement être au niveau source.

    Dans sa version actuelle (VB + C# uniquement) cette appli fonctionne en 32 bits, dans des environnements 32 et 64 bits. Pour des raisons que j'ignore encore, elle ne fonctionne pas correctement en 64 bits sur des environnements 64 bits...

    Donc deux solutions pour l'intégration du C++ :

    1. tout le code est managé et on interdit toujours l'usage du 64 bits,
    2. tout le code natif ne peut passer en code managé (pour une hypothétique raison que j'ignore aujourd'hui). Dans ce cas, la dll mixte peut-elle être générée en mode 32/64 bits et être utilisée par l'appli en mode 32 bits seulement (je fais l'hypothèse que le code natif 32 bits ne peut s'exécuter sur un processeur 64) ?

    Reste la mise en oeuvre concrète et, bien sur, l'obfuscation... Je vais faire un essai de déploiement ClicOnce qui est la méthode que j'ai adoptée.

    Pour info, comment paramétrer VS2010 pour générer automatiquement deux versions de dll ? 

    Autre question aussi : peut-on envisager de séparer en deux la dll mixte : une pour le C++/CLI, une autre pour le code natif ?

    Bonne journée à vous.

     


    Alain
    jeudi 21 avril 2011 07:43
  • Première tentative de déploiement ClicOnce :

    J'ai signé l'assembly .Net et tenté la publication.

    Message : Error 1 Unable to emit assembly: Referenced assembly 'Myco' does not have a strong name RecFormes

    Myco c'est la dll mixte (que j'ai renommée ainsi), RecFormes c'est l'assembly .Net.
    Comment signer la dll Myco ?

    Alain
    jeudi 21 avril 2011 12:39
  • 1. Si tout le code est managé, vous ne devriez pas avoir à interdire l'usage du 64bits. A moins que le problème soit dans le code managé non 64 bits "compliante", ce qui est assez rare en .NET pure.

    2. le code natif 32 bits peut s'exécuter sur un processeur 64 bits car ils disposent tous d'un mode 32bits (peut-être pas pour l'Itanium, remarqué).

    Le code natif ne peut être à la fois 32 bits et 64 bits, ce genre de gymnastique est à réserver aux drivers très très très bas niveau. Donc out of scope de .NET ou tout autre code en mode user.

    Pour générer automatiquement les deux versions de dll, moi j'utiliserait plutôt MSBUILD directement, dans TFS ou pas.

    Pour faire cela facilement, vous n'avez qu'à créer des configurations supplémentaires à coté de Debug et Release, genre Debug64 et Release64, en clonant les configurations existantes, puis en modifiant les version "64" pour cibler des plateforme 64 bits.

    Dans MSBUILD, il est facile de faire appel à Visual Studio deux fois pour la même solution mais avec des configurations cibles différentes.

    Mais sans MSBUILD, vous pouvez toujours créer les configurations supplémentaires, mais vous les compilerez les unes après les autres pour avoir les deux versions.

    Vous pouvez tout à fait diviser la dll mixte en deux pour n'avoir que du code managé dans l'une et du code natif dans l'autre, mais le problème du passage d'un monde managé .NET au monde natif restera aussi complexe qu'avec VB.NET. Le C++/CLI en managé pure n'a pas d'avantage en terme d'interopérabilité par rapport au C# ou au VB.NET.


    Paul Bacelar, Ex - MVP VC++
    jeudi 21 avril 2011 19:57
    Modérateur
  • VS2010 génère automatiquement un fichier .snk lors de la signature de l'assembly principal (VB.Net).

    Il n'est donc pas nécessaire d'utiliser sn.exe.

    J'ai recopié ce fichier dans le répertoire source de la DLL et j'ai fournit le chemin comme indiqué dans le lien ci-dessus.

    La compli fonctionne bien, la publication ClickOnce aussi mais l'installation échoue avec l'erreur : Strong name signature not valid for this assembly Myco.dll.

    Je réessaierai demain.

    Merci Paul pour votre aide.


    Alain
    jeudi 21 avril 2011 21:59
  • Vérifiez bien que la dll est "totalement" signés et non avec une signature "retardé".

    Vérifiez bien que ClickOnce prend la version complètement signé et non une non signé ou partiellement qui traînerait sur le disque. Donc vérifiez les propriétés "Copy Local" et "Specific Version" qui doivent être à "True" toutes les deux.


    Paul Bacelar, Ex - MVP VC++
    jeudi 21 avril 2011 23:42
    Modérateur
  • Bonjour Paul,

    Je suis en déplacement à l'étranger et, malheureusement, je ne peux plus me connecter à mon site de déploiement. Je ne peux donc pas poursuivre les tests en cours.

    Désolé d'interrompre notre échange qui m'a déjà beaucoup fait progresser.

    Je reprendrai contact dans quelques jours.

    Bien cordialement

     


    Alain
    mardi 26 avril 2011 08:34
  • Bonjour Paul,

    De retour sur le sol français, je reprends ce sujet important pour moi.

    Entre-temps, j'ai fait une mise à jour de VS2010 : j'ai installé le SP1.

    J'ai fait de nouveaux essais de déploiement ClickOnce de mon appli de test et, ma fois, cela fonctionne bien maintenant (effet SP1 ou autre ?).

    J'ai même réalisé un second programme de test en WPF qui référence ma dll en C++ et, Ok, le déploiement s'est effectué avec succès.

    Donc tout va bien.

    Reste une seule question à résoudre, celle de la compatibilité 32/64 bits de mon appli globale, dont vous m'avez donné les clés, ci-dessus, le 21 avril.

    Je pense que je choisirai de faire une configuration supplémentaire avec VS car je crains de me lancer dans MSBUILD...

    Je vous remercie encore pour votre aide sur ce projet.

    Bien cordialement.

     


    Alain
    mercredi 4 mai 2011 08:51