none
Création projet Visual Studio 2008 [Résolu] RRS feed

  • Question

  • Bonjour.

    J'ai une (très) bonne connaissance de la programmation C et une moyenne en C++, principalement sous Linux. J'entame un projet Visual C++ de type Windows Forms Application. J'ai essayé avec Visual Studio 2008 et Visual 2010, mais je préfère continuer en 2008.

    Ma question va paraître stupide, mais voila : lorsque j'ai conçu mes formes et défini les actions des boutons, j'ai besoin de coder ce que ces actions doivent faire. J'ai ouvert un nouveau source dans le projet pour y mettre tout le "métier" nécessaire (dans ce cas, traitement de données sur sockets). Arrivé là, je ne parviens pas à faire compiler une seule ligne du code C que j'y mets, j'ai des erreurs et des warnings principalement dûs à des problèmes de conversions de types. Exemple : une constante chaîne "Ok" est refusée en paramètre de strcpy pour impossibilité de convertir le type XXX en type YYY. Il me manque probablement des headers ou des paramètres de projet, impossible de trouver lesquels .....



    mercredi 11 janvier 2012 13:25

Réponses

  • Vous manquez d'un peu de recul. ;-)

     

    Une "Windows Forms Application" est une application .NET car elle utilise le Framework .NET, donc codée avec un langage .NET.

    C++ de base n'est pas un langage .NET, C++/CLI est une évolution du C++ pour en faire un langage compatible .NET.

    Vos problèmes semble correspondre en partie à des problèmes d'interopérabilité entre du code dit Natif (n'utilisant pas le Framework .NET) et du code dit managé (utilisant le Framework .NET et en particulier des classes dites managé comme des Forms ou les String^).

    String^ n'est pas une std::string loin s'en faut.

    Pour des applications simples, vous n'avez pas à utiliser l'interopérabilité.

    N'utilisez des std::string que dans du code natif et utilisez string^ pour le code natif.

     

    L'autre partie des problèmes viennent de mauvaises habitudes des codeurs C qui passe plus facilement avec des compilateurs type GCC qu'avec VC++ et Win32.

    Le C++ n'est pas du "C with classes", une chaine constante est un type différent d'une chaine non constante, une chaine ASCII est différentes d'une chaîne UNICODE, et les primitives Win32 et de la C-Runtime prenant des chaines de caractères en paramètre sont différentes pour chacun de ces type.

    Lisez bien le message d'erreur, il vous indiquera XXX et YYY. Et si le compilateur dit qu'ils sont différent c'est qu'il le sont.

    Donnez nous XXX et YYY et on vous donnera l'erreur. C'est un classique pour un débutant sous Windows. ;-)

     

    P.S.: Ne jamais mettre le code métier dans le code de traitement des actions de l'utilisateur mais utilisez des Design Pattern Document/Vue, MVC, MVP ou encore MVVM.


    Paul Bacelar, Ex - MVP VC++
    mercredi 11 janvier 2012 14:44
    Modérateur
  • Cool, c'est le "problème" le plus classique.

    Win32 supporte encore les APIs avec des chaînes en ASCII (plus pour très longtemps, j'espère) mais dispose des même APIs avec des chaînes en UNICODE.

    Pour avoir du code source qui fonctionne aussi bien avec des chaines ASCII et UNICODE, M$ a "inventé" le TCHAR.

    Cette chose est soit un char (caractère ASCII) soit un w_char (caractère UNICODE) en fonction d'une constante de compilation.

    ShellExecute prend comme paramètre des LPCTSTR, des pointeurs (P) long(L), mais bon ça c'est historique car il n'y a plus de pointeurs courts ;-), sur des chaines constantes (C) de TCHAR (TSTR).

    En fait ShellExecute n'est qu'un define soit sur ShellExecuteA si la constante de compilation indique d'utiliser des caractères ASCII, soit sur ShellExecuteW si la constante de compilation indique d'utiliser des caractères UNICODE.

    Je pense que vous avez deviné que la constante de compilation dans votre projet indique d'utiliser des caractères UNICODE.

    Et cela est très bien, ne cherchez pas à la changer SVP. (Les API ASCII sont bien plus lentes que les API UNICODE).

    Si vous voyez le message d'erreur, le LPCWSTR correspond donc à une chaine UNICODE -> W.

    Là, vous vous dite, je suis chez les dingos. ;-)

    Mais comme on est des feignants, il y a une astuce magique. ;-)

    Pour que le code source compile aussi bien en ASCII qu'en UNICODE, donc quelque soit la valeur de constante de compilation (qui peut changer d'un projet à un autre), il suffit de toujours passer un pointeur sur une chaîne de TCHAR.

    La MACRO TEXT() permet de convertir, en ligne, une chaine ASCII en une chaine de TCHAR. Donc aucune conversion quand la constante de compilation indique d'utiliser les API ASCII et une conversion en chaîne UNICODE quand cette constante de compilation indique d'utiliser les API UNICODE.

    Il suffit donc d'ajouter l'appel à la MACRO :

    -ShellExecute(0, TEXT("open"), monfic, 0, 0, SW_SHOWNORMAL);

    et que

    - monfic soit un LPCTSTR et non un LPCSTR, donc un tableau de TCHAR et non un tableau de char.

     

    Le mécanisme doit être appliqué à tous les "char" passés en paramètre des APIs.

    Moi, je vous conseille de n'utiliser que des TCHAR partout et ne jamais utiliser de char.


    Paul Bacelar, Ex - MVP VC++
    mercredi 11 janvier 2012 16:45
    Modérateur

Toutes les réponses

  • Vous manquez d'un peu de recul. ;-)

     

    Une "Windows Forms Application" est une application .NET car elle utilise le Framework .NET, donc codée avec un langage .NET.

    C++ de base n'est pas un langage .NET, C++/CLI est une évolution du C++ pour en faire un langage compatible .NET.

    Vos problèmes semble correspondre en partie à des problèmes d'interopérabilité entre du code dit Natif (n'utilisant pas le Framework .NET) et du code dit managé (utilisant le Framework .NET et en particulier des classes dites managé comme des Forms ou les String^).

    String^ n'est pas une std::string loin s'en faut.

    Pour des applications simples, vous n'avez pas à utiliser l'interopérabilité.

    N'utilisez des std::string que dans du code natif et utilisez string^ pour le code natif.

     

    L'autre partie des problèmes viennent de mauvaises habitudes des codeurs C qui passe plus facilement avec des compilateurs type GCC qu'avec VC++ et Win32.

    Le C++ n'est pas du "C with classes", une chaine constante est un type différent d'une chaine non constante, une chaine ASCII est différentes d'une chaîne UNICODE, et les primitives Win32 et de la C-Runtime prenant des chaines de caractères en paramètre sont différentes pour chacun de ces type.

    Lisez bien le message d'erreur, il vous indiquera XXX et YYY. Et si le compilateur dit qu'ils sont différent c'est qu'il le sont.

    Donnez nous XXX et YYY et on vous donnera l'erreur. C'est un classique pour un débutant sous Windows. ;-)

     

    P.S.: Ne jamais mettre le code métier dans le code de traitement des actions de l'utilisateur mais utilisez des Design Pattern Document/Vue, MVC, MVP ou encore MVVM.


    Paul Bacelar, Ex - MVP VC++
    mercredi 11 janvier 2012 14:44
    Modérateur
  • Merci de cette réponse.

    Je vois que vous avez parfaitement compris mon problème, et j'accepte sans restrictions votre remarque sur les mauvaises habitudes que l'on prend en C ! Je suis même tout à fait d'accord là-dessus.

    C'est vrai que je débute en applications .NET, et j'ai des règles à apprendre.

    Je vais plancher sur ce projet et revenir rapidement avec les erreurs exactes, mais je vois déjà ce qu'il faudrait que je fasse.

    mercredi 11 janvier 2012 15:02
  • Plus précisément, voila le genre d'erreur :

    Le code est :

    ShellExecute(0, "open", monfic, 0, 0, SW_SHOWNORMAL); 

    L'erreur :

    tcp4.cpp(79) : error C2664: 'ShellExecuteW' : cannot convert parameter 2 from 'const char [5]' to 'LPCWSTR'

     tcp4.cpp(80) : error C2664: 'ShellExecuteW' : cannot convert parameter 3 from 'const char [14]' to 'LPCWSTR'


    mercredi 11 janvier 2012 15:17
  • Cool, c'est le "problème" le plus classique.

    Win32 supporte encore les APIs avec des chaînes en ASCII (plus pour très longtemps, j'espère) mais dispose des même APIs avec des chaînes en UNICODE.

    Pour avoir du code source qui fonctionne aussi bien avec des chaines ASCII et UNICODE, M$ a "inventé" le TCHAR.

    Cette chose est soit un char (caractère ASCII) soit un w_char (caractère UNICODE) en fonction d'une constante de compilation.

    ShellExecute prend comme paramètre des LPCTSTR, des pointeurs (P) long(L), mais bon ça c'est historique car il n'y a plus de pointeurs courts ;-), sur des chaines constantes (C) de TCHAR (TSTR).

    En fait ShellExecute n'est qu'un define soit sur ShellExecuteA si la constante de compilation indique d'utiliser des caractères ASCII, soit sur ShellExecuteW si la constante de compilation indique d'utiliser des caractères UNICODE.

    Je pense que vous avez deviné que la constante de compilation dans votre projet indique d'utiliser des caractères UNICODE.

    Et cela est très bien, ne cherchez pas à la changer SVP. (Les API ASCII sont bien plus lentes que les API UNICODE).

    Si vous voyez le message d'erreur, le LPCWSTR correspond donc à une chaine UNICODE -> W.

    Là, vous vous dite, je suis chez les dingos. ;-)

    Mais comme on est des feignants, il y a une astuce magique. ;-)

    Pour que le code source compile aussi bien en ASCII qu'en UNICODE, donc quelque soit la valeur de constante de compilation (qui peut changer d'un projet à un autre), il suffit de toujours passer un pointeur sur une chaîne de TCHAR.

    La MACRO TEXT() permet de convertir, en ligne, une chaine ASCII en une chaine de TCHAR. Donc aucune conversion quand la constante de compilation indique d'utiliser les API ASCII et une conversion en chaîne UNICODE quand cette constante de compilation indique d'utiliser les API UNICODE.

    Il suffit donc d'ajouter l'appel à la MACRO :

    -ShellExecute(0, TEXT("open"), monfic, 0, 0, SW_SHOWNORMAL);

    et que

    - monfic soit un LPCTSTR et non un LPCSTR, donc un tableau de TCHAR et non un tableau de char.

     

    Le mécanisme doit être appliqué à tous les "char" passés en paramètre des APIs.

    Moi, je vous conseille de n'utiliser que des TCHAR partout et ne jamais utiliser de char.


    Paul Bacelar, Ex - MVP VC++
    mercredi 11 janvier 2012 16:45
    Modérateur
  • He bien, c'est parfait : j'ai procédé comme cela et je n'ai plus d'erreurs de compilation. Reste à faire marcher le projet !!

     

    Merci beaucoup

    mercredi 11 janvier 2012 19:18