FAQ VC++ et MFCConsultez toutes les FAQ
Nombre d'auteurs : 20, nombre de questions : 545, dernière mise à jour : 5 avril 2013 Ajouter une question
Cette faq a été réalisée pour répondre aux questions les plus fréquement posées sur le forum Développement Visual C++
Je tiens à souligner que cette faq ne garantit en aucun cas que les informations qu'elle contient sont correctes ; Les auteurs font le maximum, mais l'erreur est humaine. Si vous trouvez une erreur, ou si vous souhaitez devenir redacteur, lisez ceci.
Sur ce, je vous souhaite une bonne lecture. Farscape
- Que faire si le ClassView n'affiche pas toutes les classes ?
- Pourquoi la complétion automatique ne fonctionne pas ou plus ?
- Comment éviter d'avoir des problèmes avec les « includes » ?
- Comment indiquer l'emplacement des includes externes dans un projet ?
- Comment gérer l'arborescence des sources dans un projet ?
- Comment avoir le browser de code dans un projet Visual ?
- Comment activer l'option RTTI du compilateur?
- Comment sauvegarder le paramétrage de l'environnement Visual 6.0 ?
- Comment construire la librairie wxWidgets avec Visual 6.0 ?
- Comment inclure un fichier ressource d'une librairie dans le projet maître ?
- Comment faire apparaître dans ClassWizard certains messages Windows ?
- Comment désactiver un warning de compilation avec les STL ?
- Comment créer un modèle de projet avec VC 6.0 ?
- Comment créer une bibliothèque statique avec VC.Net ?
- Comment démarrer avec Visual C++ Toolkit 2003 ?
- Pourquoi mon code, pourtant valide, ne compile pas sous Visual C++ 6 ?
- Quelques raccourcis utiles de Visual C++ 6.0
- Comment sont initialisées les variables pointeurs ?
- Comment intégrer des sources C dans un projet C++ ?
- Comment inclure l'appel de l'aide avec un fichier .chm dans un programme MFC ?
- Que faire avec l'erreur de compilation C1010 (en-tête précompilé) ?
- Comment voir des informations de debbuggage/tracage simplement sans gestion avancée de log ?
- Comment augmenter la mémoire réservée par défaut de mon exécutable lors d'un stack overflow ?
- Comment éviter l'erreur la dll msvcr80d.dll est introuvable avec VC 2005 ?
- Comment personnaliser l'affichage des objets dans le débogueur ?
- Comment rajouter une extension de fichier prise en charge comme un .C ?
- Comment gérer la cohabitation de deux fonctions ayant le même nom dans l'API Win32 et le framework.net ?
- Comment lier une bibliothèque statiquement ?
- Comment régler l'édition des liens avec les bibliothèques statiques ?
- Comment demander une élévation des droits de l'application sous vista ?
- Où se procurer le dernier SDK de Visual 6.0 ?
- UAC et virtualisation des accès fichiers sous Vista
- Comment régler l'UAC avec visual 2008 ?
- Restriction de l'usage des classes templates avec les MFC et Visual 2008
- Comment rendre une application compatible terminal serveur ?
- Comment obtenir le chemin Windows virtualisé dans un environnement Terminal Serveur ?
- Comment régler la plateforme de distribution sous Visual 2008 ?
Avant toute chose vérifier que les includes des classes sont bien situés dans le dossier «headers files» et non dans le dossier « external dependencies » .
Si ce n'est pas le cas procéder à un glisser déplacer avec la souris des fichiers dans le dossier «headers files».
Pour regénérer le « ClassView » la méthode la plus directe consiste à :
- Fermer le projet.
- Détruire avec l'explorateur les fichiers : .clw ,.opt ,.ncb situés dans le répertoire du projet.
- Ouvrir à nouveau le projet lancer le « ClassView ».
- Il détecte l'absence du fichier et vous ouvre alors une boîte de dialogue vous demandant de lui ré indiquer les fichiers sources.
- Indiquer le répertoire source du projet.
- Valider.
Logiquement on doit recouvrer l'usage de « intellisens » et le « ClassView » doit être opérationnel.
Plusieurs raisons possibles à ce problème :
Les includes des classes concernées ne sont pas dans le dossier header files mais dans le dossier external dependencies.
Solution faire un glisser déplacer des fichiers en question dans le bon dossier.
La ou les classes en questions ont été rajoutées manuellement ou par l'insertion d'un nouveau fichier include dans le projet sans passer par le générateur de classe.
Reconstruire le classview comme indiquer dans la FAQ : Que faire si le ClassView n'affiche pas toutes les classes ?
En appliquant la technique suivante :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #ifndef _COOLTOOLBAR_H #define _COOLTOOLBAR_H class CMyBt; class COOlButtonTemplate { //.................. //............... CMyBt *m_pbt; }; class CMyBt { public: COOlButtonTemplate m_cooltoolbar; }; #endif //_COOLTOOLBAR_H |
A la première inclusion, le symbole _COOLTOOLBAR_H est défini, ce qui évite les inclusions supplémentaires du même code.
C'est une procédure standard dans les fichiers .h ,il est conseillé de faire de même dans tous vos fichiers .h pour éviter les problèmes liés à la duplication des définitions.
Visual C++ l'incorpore automatiquement dans tous les fichiers de définitions qu'il crée.
La déclaration « class CMyBt ; » au dessus de la classe « COOlButtonTemplate » permet l'utilisation de la classe avant sa déclaration, très pratique dans le cas où deux classes doivent contenir une variable de la classe voisine de manière croisée comme dans l'exemple ci-dessus.
Pour indiquer dans visual l'emplacement des includes externes au projet deux possibilités :
headers de librairies privés:
sélectionner le menu tools / options .
Onglet directories.
Voir les différentes catégories de fichiers avec la combobox "show directories for":
Rajouter les différents chemins.
Headers propres au projet:
Sélectionner le menu : projects / settings .
onglet c++
Catégorie : preprocessor
Rajouter les chemins séparés par des ; dans l'édit "additional includes directories".
Notes :les includes référencés dans les sources du projet apparaissent directement dans le dossier headers files.
Le classview s'appuie sur les headers présents à cet emplacement.
Les autres références externes au projet sont placées automatiquement dans "external dependencies" .
Il est possible de déplacer des headers de ce dossier par glisser déplacer dans le dossier header files pour disposer du classview.
Pour rajouter des fichiers dans un dossier spécifique il faut faire add sur le dossier en question.
Il n'y a pas de limitations sur l'emplacement des fichiers à inclure dans un projet il faut juste veiller à fournir les références pour les headers par l'une des deux méthodes précitées
L'arborescence d'un projet Visual n'est pas figée ,
On peut créer des sous répertoires par un click droit avec l'option « new folder » sur le dossier sources ou headers files.
Pensez à récupérer la liste des extensions de fichiers gérée par dossier en faisant click droit propriétés sur un dossier existant (sources ou headers) et procéder par copier coller pour recopier les extensions.
Ensuite il suffira de déplacer les fichiers par glisser déplacer ou de les insérer par un click droit « add files ».
On pourra organiser les sources par thèmes exemple :
Les sources concernant les différents outils,
Les gestions de fichiers,
Les traitements etc ...
On procédera de la même manière pour les « includes »
Pour disposer du browser de code sélectionner :
Le menu projects option settings puis l'onglet C++ »
Dans la zone category sélectionner listing files .
Puis cocher « generate browse info. ».
Le fichier se construit et suivra l'évolution du projet.
Pour accéder au browser sélectionner le ClassView faire click droit et sélectionner une des trois options :
- references ;
- derived classes ;
- base classes.
L'option RTTI ou Run-Time Type Information permet l'utilisation du mot clef dynamic_cast . (Voir documentation MDSN)
Pour l'activer sélectionner le menu Projects option Settings Onglet C++ .
Category : C++ Language.
Cocher :Enable Run-Time Type Information (RTTI).
L'environnement de Visual 6.0 peut être sauvegardé en exportant la clef suivante de la base de registre:
HKEY_CURRENT_USER\Software\Microsoft\Devstudio\6.0
Pour sauvegarder uniquement les chemins des includes ,des librairies etc ..:
HKEY_CURRENT_USER\Software\Microsoft\DevStudio\6.0\Build System\Components\Platforms\Win32 (x86)\Directories
Cette librairie était anciennement connue sous le nom wxWindows.
Pour commencer
Il faut télécharger le fichier wxMSW-2.4.2-setup.zip à l'adresse : http://www.wxwidgets.org/
Lancer le Setup.
Dans visual 6.0 faire :
File Open Workspace :
Ouvrir le fichier C:\wxWindows-2.4.2\src\wxWindows.dsp
Ensuite le menu “project” option “batch Build” et bouton build.
Rajouter les chemins des librairies et includes dans le menu :
Tools Options Directories :
Path Includes:
C:\wxWindows-2.4.2\include
C:\wxWindows-2.4.2\include\wx
Path Lib:
C:\wxWindows-2.4.2\lib
Un exemple pour faire un essai :
C:\wxWindows-2.4.2\samples\grid\grid.dsw
Enjoy.
Au niveau de la librairie :
Attention au départ des numéros d'id des contrôles dans le fichier resources.h
Celui-ci doit être renommé pour éviter les conflits avec le projet maître (voir ci-dessous).
Au niveau du projet maître :
il suffira de rajouter dans le menu :
View - ressources includes :
Dans la première partie de la fenêtre après axfres.h le fichier ressource de la librairie (dont il faudra changer le nom pour éviter les conflits :autre chose que resource.h !)
Exemple ici MyLibRes.h
Code c++ : | Sélectionner tout |
1 2 3 | #include "afxres.h" #include "MyLibRes.h" |
Deuxième partie de la fenêtre:
Il faudra rajouter le fichier .rc de la librairie ici MyLib.rc.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #define _AFX_NO_SPLITTER_RESOURCES #define _AFX_NO_OLE_RESOURCES #define _AFX_NO_TRACKER_RESOURCES #define _AFX_NO_PROPERTY_RESOURCES #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) #ifdef _WIN32 LANGUAGE 12, 1 #pragma code_page(1252) #endif #include "res\MonProjet.rc2" // non-Microsoft Visual C++ edited resources #include "l.fra\afxres.rc" // Standard components #include "l.fra\afxprint.rc" // printing/print preview resources #include "MyLib.rc" #endif |
Et procéder à la compilation des ressources.
Exemple : Prenons le cas d'une fenêtre CDialog où l'on voudrait intercepter le message WM_ACTIVATE.
Il n'apparaît pas par défaut dans la liste des messages de la boîte de dialogue .
Pour y remédier sélectionner l'onglet "Class Info" et mettre dans la liste déroulante "message filter": Windows à la place de Dialog ,revenir ensuite sur l'onglet "message maps" maintenant le message est disponible…
Lors de la compilation de sources employant les STL on rencontre souvent le Warning C4786 avec un message à rallonge ressemblant à ceci :
warning C4786: 'blablabla ' : identifier was truncated to '255' characters in the debug information
Ce qui est souvent bénin, la solution pour enlever ce Warning est de rajouter la ligne suivante dans stdafx.h
Code c++ : | Sélectionner tout |
1 2 |
#pragma warning(disable:4786) |
Après le lancement de l'option File new, sélectionner l'option Custom AppWizard
Entrer le nom du projet qui servira de modèle dans l'édit Project Name :
Exemple : MyCustomApp.
Valider le choix par le bouton Ok.
Sur le deuxième volet laisser l'option : An existing Projet
Cliquer sur le bouton Next pour choisir le projet source qui servira de modèle de génération.
Valider le choix par le bouton Finish.
Le projet est généré il ne reste plus qu'à le compiler et le linker.
Un nouveau type de projet sera alors disponible dans la boîte de dialogue de création de projets.
Description visuelle du procédé : MakeCustomApp.pdf
Il faut choisir le type : projet win32, et dans les paramètres application cocher l'option bibliothèque statique.
Après l'installation du setup il ne reste plus qu'à paramétrer les variables d'environnements liées au compilateur.
La distribution du toolkit fournit un fichier vcvars32.bat qui contient les différentes valeurs qui pourront être inscrites pour plus de souplesse dans les variables d'environnements systèmes :
Pour cela il faut accéder à l'icône système (sous Windows XP ou 2000) située dans le panneau de configuration.
Sélectionner ensuite l'onglet avancé puis le bouton : variables d'environnements sur les variables systèmes.
Modifier ou rajouter les valeurs suivantes du fichier de commandes vcvars32.bat situé dans le répertoire
C:\Program Files\Microsoft Visual C++ Toolkit 2003
Set PATH=C:\Program Files\Microsoft Visual C++ Toolkit 2003\bin;%PATH%
Set INCLUDE=C:\Program Files\Microsoft Visual C++ Toolkit 2003\include;%INCLUDE%
Set LIB=C:\Program Files\Microsoft Visual C++ Toolkit 2003\lib;%LIB%
Voilà il ne reste plus qu'à ouvrir une boîte de commande MSDOS et de tester la construction d'un projet fournit pour exemple dans le toolkit
Ou de lancer la construction par un fichier de commandes directement à partir de l'explorateur windows.
Visual C++ 6 est sorti en 1998, et donc n'inclus pas la dernière norme du C++ , datant de la même année. Il en résulte certains manques, et des résultats parfois étonnants avec du code pourtant parfaitement valide.
Voici quelques exemples de codes standards mais qui ne compilent pas sous VC++ 6
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | for (int i = 0; i < 50; ++i) { // ... } for (int i = 0; i < 20; ++i) // Erreur : "i : redefinition" { // ... } |
Une solution (de derrière les fagots) pour corriger ce manque : inclure systématiquement nos boucles dans un if, ainsi la portée sera bien limitée
Code c++ : | Sélectionner tout |
#define for if (true) for
Code c++ : | Sélectionner tout |
1 2 3 4 5 | struct A { static const int Taille = 12; // Erreur : "syntaxe non valide" int Tab[Taille]; }; |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | struct A { void Func() { } }; std::vector<A> Tab; std::for_each(Tab.begin(), Tab.end(), std::mem_fun_ref(A::Func)); // Erreur : fonction 'void' renvoyant une valeur |
Code c++ : | Sélectionner tout |
1 2 3 4 5 | std::set<int> s; std::vector<int> v(s.begin(), s.end()); // Erreur : "conversion impossible" std::list<int> l; l.sort(std::less<int>()); // Erreur |
Touche | Action |
CTRL+F2 | marquer une ligne / F2 : passer à la marque suivante |
ALT+F8 | indentation de la sélection |
CTRL+E | identifier les accolades, parenthèses, crochets |
CTRL+Espace | complétion automatique |
CTRL+SHIFT+Espace | prototype de la fonction |
F4 | aller à la 1ere erreur de compilation, puis erreur suivante |
Le compilateur suivant les cas à l'exécution peut affecter des valeurs spécifiques à une variable pointeur, Connaître ces valeurs peut s'avérer fort utile en mode Debug :
0xFDFDFDFD indique une adresse en dehors de l'espace d'adressage du processus.
0xDDDDDDDD indique que la mémoire vient d'être libérée.
Attention ça ne veut pas dire que l'instruction delete va mettre cette valeur dans un pointeur.
0xCDCDCDCD mémoire globale non initialisée par exemple un pointeur membre d'une classe .
0xCCCCCCCC mémoire locale (pile /stack ) non initialisée par exemple un pointeur déclaré dans une fonction membre d'une classe.
On comprend l'utilité de toujours initialiser les pointeurs à NULL ,et de leur affecter cette valeur après libération de la mémoire.
Ce qui nous amène à l'erreur la plus commune : 0xc0000005 Access Violation
Un exemple simple :
Dans la fonction OnInitialUpdate d'une CFormView je fais la chose suivante :
Code c++ : | Sélectionner tout |
1 2 3 | CDialog *pdlg; pdlg->ShowWindow(1); |
En debug si je regarde la valeur de this dans ShowWindow il a la valeur reconnaissable 0xcccccccc et le debugger met des ??? Dans le handle de fenêtre.
Pour résumer lorsque vous tracez un programme en debug et que vous tombez sur ce message,
Pensez à regarder la valeur affectée à this ,
Ou remonter d'un cran dans la pile des appels pour regarder la valeur du pointeur.
Il est tout à fait possible de mélanger des sources en C extension .c et des sources en C++ au sein du même projet.
Détail de la procédure :
Il faudra veiller à enlever au niveau des options de compilation des fichiers en C l'option entêtes pré compilés :
Avec Visual C++6.0 :
Dans la gestion de projet à gauche sélectionner le(s) fichier(s) en C Puis faire clic droit settings .
Dans l'onglet onglet C++ sur la catégorieprecompiled header cocher not using precompiled headers.
Astuce : Pour la multi sélection des fichiers utiliser la touche CTRL+clic gauche.
Enfin tous les prototypes des fonctions en C devront être déclarés avec extern "C" :
Exemple de .h pour déclarer les fonctions en C pour utilisations en C et C++
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | #ifdef __cplusplus extern "C" { #endif void myfct(int c); #ifdef __cplusplus } #endif |
Microsoft avec un outil gratuit dénommé HTML Help Workshop permet de fabriquer un système d'aide à partir de fichier HTML, remplaçant ainsi avantageusement l'ancien système à base de fichier RTF.
Ceux-ci pouvant être composés avec FrontPage par exemple.
Le fichier d'aide généré aura une extension .chm et peut être entièrement autonome, c'est-à-dire pas forcément rattaché à un programme.
Pour une prise en main du kit voir le tutoriel Créer un fichier d’aide de type .chm (format html) de Thierry AIM.
Cliquer ici pour le téléchargement du kit sur le site de Microsoft
Maintenant nous allons voir comment établir le lien entre un projet développé avec le kit de Microsoft et un programme MFC .
A terme l'appui de la touche F1 sur une fenêtre particulière (FormView) invoquera l'aide correspondante dans le fichier d'aide.
Génération du fichier de liens entre les formes et l'aide :
Pour établir un lien entre le fichier d'aide et le programme il faut faire un fichier de correspondance qui peut être généré automatiquement par visual .
Sur les propriétés du projet : (menu projects /settings )
Onglet custom build :
Dans la partie gauche des fichiers du projet, sélectionner le fichier resource.h
Dans l'édit descriptions: on renseigne le libellé par exemple : Making HTML Help Include File...
Et dans l'édit Commands:
Code : | Sélectionner tout |
1 2 3 4 5 6 7 | makehm ID_,IDH_,0x10000 IDM_,IDH_,0x10000 resource.h >>"hlp\$(TargetName).hm" echo. >>"hlp\$(TargetName).hm" makehm IDP_,IDH_,0x30000 resource.h >>"hlp\$(TargetName).hm" makehm IDR_,IDH_,0x20000 resource.h >>"hlp\$(TargetName).hm" makehm IDD_,IDH_,0x20000 resource.h >>"hlp\$(TargetName).hm" makehm IDW_,IDH_,0x50000 resource.h >>"hlp\$(TargetName).hm" MakeIDH "hlp\$(TargetName).hm" |
le fichier généré portera le nom du projet suivi de l'extension .hm.
À indiquer dans l'édit Outputs
Code C++ : | Sélectionner tout |
hlp\$(TargetName).hm
la première compilation des ressources le fichier .hm va être généré .
Note : le programme MakeIDH doit être présent à la racine du projet.
Compilation automatique du projet d'aide à partir de visual 6: (falcutatif)
Le fichier .hpp doit être inclu dans le projet.
Sur les propriétés du projet : (menu projects /settings )
Onglet custom build :
Dans la partie gauche des fichiers du projet, sélectionner le fichier .hpp
Dans l'édit descriptions : on renseigne le libellé par exemple : Making HTML Help Include File...
Et dans l'édit Commands :
Code : | Sélectionner tout |
1 2 3 | hhc.exe html\$(InputName).hhp echo. copy html\$(InputName).chm $(OutDir)\$(InputName).chm |
Code : | Sélectionner tout |
html\$(InputName).chm
Code : | Sélectionner tout |
html\$(TargetName).hm
Object/library modules, rajouter : htmlhelp.lib
Lien du fichier d'aide .chm avec le programme :
Dans la fonction InitInstance de la classe d'application on va modifier le lien de l'aide qui est par défaut sur une extension .HLP
Code C++ : | Sélectionner tout |
1 2 3 4 5 | // Change the extension for the default help file CString strHelpFile = m_pszHelpFilePath; strHelpFile.Replace(".HLP", ".chm"); free((void*)m_pszHelpFilePath); m_pszHelpFilePath = _tcsdup(strHelpFile); |
Le fichier .chm devra être au même emplacement que le programme.
Appel de l'aide par la touche F1 :
Dans la classe CMainFrame rajouter manuellement le message
Code C++ : | Sélectionner tout |
1 2 | ON COMMAND(IDHELP_CMDIFrameWnd ::OnHelp) END_MESSAGE_MAP() |
dans le cas d'un projet SDI :
Code C++ : | Sélectionner tout |
ON_COMMAND(ID_HELP, CFrameWnd::OnHelp)
Au niveau de la classe CMainFrame rajouter avec l'aide classwizard la fonction virtuelle :WinHelp .
Code C++ : | Sélectionner tout |
virtual void WinHelp(DWORD dwData, UINT nCmd = HELP_CONTEXT);
Et remplacer le contenu par :
Code C++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | void CMainFrame::WinHelp(DWORD dwData, UINT nCmd) { CMyApp *pApp=static_cast<CMyApp *>(AfxGetApp()); #if _MFC_VER >= 0x0700 ::HtmlHelp((pApp->m_bHelpModal?m_hWnd:NULL), AfxGetApp()->m_pszHelpFilePath, HH_HELP_CONTEXT,nCmd==HELP_CONTEXT?dwData:0); #else HtmlHelp((pApp->m_bHelpModal?m_hWnd:NULL), AfxGetApp()->m_pszHelpFilePath, HH_HELP_CONTEXT,nCmd==HELP_CONTEXT?dwData:0); #endif } |
la variable est un booleen permettant de choisir si l'appel de l'aide est bloquante ou non .
pour finir dans le hhp on doit avoir les sections suivantes :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 | [MAP] #include MyApp.hm [TEXT POPUPS] MyApp.hm [MERGE FILES] MyApp.chm |
c'est au niveau de la section alias dans le programme help workshop que l' on va définir les liens :
on va associer un numéro d'id IDH à une page web.
en faisant double clic sur la section alias on renseignera le nom de l'include :
sur l'onglet map avec l'aide du bouton header file on indiquera le chemin du fichier .hm.
dans l'onglet alias on établira directement le lien entre l'id IDH_ et la page web.
Note :
à la racine du projet on doit avoir les répertoires : html et hlp.
Le détail de l'erreur avec Visual .net :
Dans la plupart des cas Visual génère un projet avec l'option en-tête précompilé.
Les en-têtes précompilés représentent des instantanés de compilation pris à un certain emplacement du code source.
Il est pris juste après l'inclusion de la ligne #include "stdafx .h" qui contient les fichiers d'en-têtes des MFC ou template.
Les en-têtes précompilés font donc gagner du temps à la compilation.
À partir du moment où cette option est réglée pour le projet tous les fichiers .CPP du projet doivent avoir en tout début du source la ligne :
Code C++ : | Sélectionner tout |
#include "stdafx .h"
Note : il est possible de désactiver spécifiquement par fichier cette option ,c'est le cas par exemple si on veut mixer des fichiers .c et .cpp dans un même projet Voir Comment intégrer des sources C dans un projet C++ ?
En utilisant l'API OutputDebugString et un petit logiciel qui intercepte ces informations de débugage Une fois ce programme lancé, un simple
Code c++ : | Sélectionner tout |
OutputDebugString("Informations à voir affichées");
Lors de l'utilisation de grosses variables dans un programme, on se demande souvent comment sont allouées les différentes variables.
On peut résumer rapidement en disant que :
Une déclaration de type
Code c++ : | Sélectionner tout |
int monTableau[20000];
Une allocation dynamique de type
Code c++ : | Sélectionner tout |
int * monTableau = new int[20000]
Les variables globales sont définies dans le segment de données.
La taille par défaut du tas est de 1 Mo. Cette taille par défaut peut être augmentée en réglant une option du linker
Code c++ : | Sélectionner tout |
/HEAP:reserve[,commit]
C'est le système d'exploitation qui gère cela tout seul (de la même facon, il préfèrera déléger à VirtualAlloc lorsque l'allocation dépasse un certain seuil).
Cette option est rarement utilisée, sauf pour des questions de performances.
La pile dispose elle aussi d'une taille par défaut d'1 Mo. Cependant, celle-ci est fixe et ne peut augmenter seule. Ainsi, lorsque l'on déclare beaucoup de grosses variables, il peut se produire une erreur de dépassement de mémoire : stack overflow.
On peut de même facilement augmenter la mémoire de la pile par défaut en réglant une option du linker.
Code c++ : | Sélectionner tout |
/STACK:reserve[,commit]
Le linker utilise par défaut une valeur de 0x100000 (1 Mo) et 0x1000 (4Ko) pour la mémoire commit
donc pour réserver 2 Mo par exemple, il suffit de réserver cette mémoire dans le linker par :
Code c++ : | Sélectionner tout |
/STACK:0x200000, 0x1000
Remarque : C'est en général une mauvaise idée d'avoir une grosse pile. Il est plus judicieux d'utiliser des allocations dynamiques pour les gros objets.
cette erreur peut apparaitre à l'exécution du programme en mode debug,
Il faudra régler la valeur de génération du manifest à oui dans les options d'édition des liens du projet.
Menu Project / Properties /Configuration Properties /Linker/ Manifest File mettre "Generate Manifest" à Yes. Voir ce lien MSDN pour en savoir plus.
Vous avez certainement remarqué qu'il est très pratique de visualiser directement le contenu de certains objets dans la fenêtre de Visual ou encore en passant la souris sur un objet pour inspecter le contenu d'une CString par exemple ?
Et bien cette fonctionnalité de visualiser les données membres importantes d'un objet qu'il soit MFC ou autre est personnalisable.
Ce paramétrage est à mettre en place dans un fichier nommé AUTOEXP.DAT
Il est situé pour Visual 6.0 à l'emplacement :
C:\Program Files\Microsoft Visual Studio\Common\MSDev98\Bin
Sous Visual 2005 :
C:\Program Files\Microsoft Visual Studio 8\Common7\Packages\Debugger
Voici un aperçu de son contenu:
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | ; from afxwin.h CDC =hDC=<m_hDC> attrib=<m_hAttribDC> CPaintDC =<,t> hWnd=<m_hWnd> CPoint =x=<x> y=<y> CRect =top=<top> bottom=<bottom> left=<left> right=<right> CSize =cx=<cx> cy=<cy> CWnd =<,t> hWnd=<m_hWnd> CWinApp =<,t> <m_pszAppName,s> CWinThread =<,t> h=<m_hThread> proc=<m_pfnThreadProc> ; from afx.h CArchiveException =cause=<m_cause> CFile =hFile=<m_hFile> name=<m_strFileName.m_pchData,s> CFileException =cause=<m_cause> OS Error=m_lOsError CMemFile =pos=<m_nPosition> size=<m_nFileSize> CObject =<,t> CRuntimeClass =<m_lpszClassName,s> CStdioFile =FILE*=<m_pStream> name=<m_strFilename.m_pchData,s> CTimeSpan =time=<m_time> CTime =time=<m_time> |
Une description des formats d'affichages est disponible dans ce même fichier :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | Letter Description Sample Display ; ------ -------------------------- ------------ ------------- ; d,i Signed decimal integer 0xF000F065,d -268373915 ; u Unsigned decimal integer 0x0065,u 101 ; o Unsigned octal integer 0xF065,o 0170145 ; x,X Hexadecimal integer 61541,X 0X0000F065 ; l,h long or short prefix for 00406042,hx 0x0c22 ; d, i, u, o, x, X ; f Signed floating-point 3./2.,f 1.500000 ; e Signed scientific-notation 3./2.,e 1.500000e+000 ; g Shorter of e and f 3./2.,g 1.5 ; c Single character 0x0065,c 'e' ; s Zero-terminated string pVar,s "Hello world" ; su Unicode string pVar,su "Hello world" |
Pour rajouter vos propres classes il suffit d'éditer ce fichier avec notepad et de rajouter une section pour vos différents objets.
La syntaxe générale est la suivante :
L'objet =leLibelledonnéemembre<donnée membre>
Plusieurs variables membre peuvent être indiquées à la suite.
Les types simples entiers double etc sont correctement interprétés.
Pour une variable membre de type CString il faudra donner le chemin de la chaîne interne :
name=<m_strFilename.m_pchData,s> avec le type s voulant dire chaîne se terminant par un zéro.
Pour inscrire un nouveau type de fichier procéder comme suit:
avec le programme regedit (faire démarrer exécuter, saisir regedit) ouvrir la clef :
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\Languages\File Extensions
suivant les versions de compilateurs on aura à la place de 8.0 pour VC 2005
7.0 pour .net2002 et 7.1 pour .net 2003
Si on désire que l'extension de fichier .pc soit reconnue comme un .C on créera le répertoire .PC en dessous de la clef File Extensions
Ensuite dans la clef .c copier le contenu de la zone défaut dans le presse papiers
Coller la valeur dans la zone défaut de la clef ajoutée (ici .pc)
Pour intégrer IntelliSense modifier la clef suivante :
HKEY_CURRENT_USER\Software\Microsoft\VisualStudio\8.0\Languages\Language Services\C/C++\NCB Default C/C++ Extensions
il suffit de rajouter aux extensions existantes la nouvelle extension (ici .pc)
Lorsqu'on migre petit à petit son projet en C++.Net ou simplement lorsqu'on veut faire cohabiter l'Api Win32 (ou les MFC) et le framework.Net, on peut rencontrer des problèmes de compilation du style :
Code c++ : | Sélectionner tout |
1 2 3 | error C2039: 'GetObjectA' : is not a member of 'System::Resources::ResourceManager' error C2653: 'MessageBoxA' : is not a class or namespace name |
Le problème vient du fait que lorsqu'on souhaite utiliser la fonction MessageBox par exemple, le compilateur ne sait pas s'il doit utiliser la fonction MessageBox définie dans Windows.h ou bien dans le namespace System::Windows::Forms.
Il faut donc à ce moment dire au compilateur quelle fonction on veut utiliser.
On enlève alors la définition du MessageBox comme ci-dessous
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | #pragma push_macro("MessageBox") #undef MessageBox ... MessageBox::Show(S"hello"); ... #pragma pop_macro("MessageBox") |
- Soit en l'ajoutant dans les options du projet, pour VS2005 : propriétés du projet, configuration properties, linker, input, additional dependencies et indiquer ici le .lib à lier.
- Soit par pragma
Code c++ : | Sélectionner tout |
#pragma comment(lib,"malib.lib")
Lorsque la bibliothèque de runtime (CRT) est utilisée en mode statique /MT (multi-thread statique)
(en opposition au mode dynamique /MD (dll)), il peut apparaitre ce type d'erreurs lorsqu'on utilise plusieurs bibliothèques :
Code c++ : | Sélectionner tout |
1 2 3 4 5 | nafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new(unsigned int)" (??2@YAPAXI@Z) already defined in LIBCMTD.lib(new.obj) nafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPAX@Z) already defined in LIBCMTD.lib(dbgdel.obj) nafxcwd.lib(afxmem.obj) : error LNK2005: "void * __cdecl operator new[](unsigned int)" (??_U@YAPAXI@Z) already defined in libcpmtd.lib(newaop.obj) nafxcwd.lib(afxmem.obj) : error LNK2005: "void __cdecl operator delete[](void *)" (??_V@YAXPAX@Z) already defined in LIBCMTD.lib(delete2.obj) |
Voici comment procéder :
Avec Visual 2005 dans les propriétés du projet, chapitre éditeur de liens.
Rubrique Entrée, zone : "bibliothèque spécifique ignorée", mettre : Nafxcwd.lib Libcmtd.lib (pour le mode debug).
Dans la zone : "dépendances supplémentaires" mettre en dernier à la suite des autres bibliothèques utilisées par le projet: Nafxcwd.lib Libcmtd.lib
Sous Windows Vista avec l'UAC lorsqu'une tâche nécessite un privilège administrateur il faut elever les droits de l'application
Comment gérer la situation ?
Soit on change les droits d'accès sur le programme par clic droit onglet compatibilité : exécuter en tant que administrateur, un peu pénible...
ou bien on rajoutera le fichier manifest suivant à notre application :
Code xml : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity name="monapplication" version="1.0.0.0" processorArchitecture="X86" type="win32"/> <description>Mon Application</description> <!-- Identify the app's security requirements. --> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="requireAdministrator" /> </requestedPrivileges> </security> </trustInfo> </assembly> |
MyAppName.exe.manifest ,MYAppName correspondant au nom de l'application.
Avec Visual 2005 il est préférable d'inclure le manifest à notre programme en respectant la procédure indiquée dans la faq: Comment rajouter le fichier manifeste du style XP au manifeste existant dans Visual 2005 ?
Pour plus de détails sur les options du manifest consultez MSDN.
Voir aussi Comment fonctionne UAC ?
Le dernier SDK valide pour Visual 6.0 date de février 2003.
Il ne faut pas installer le SDK proposé en téléchargement sur MSDN il est prévu pour les versions Visual .Net et Visual 2005.
On peut encore télécharger ce SDK à l'emplacement : http://www.microsoft.com/en-us/downl...d/default.aspx
Au fil des ans et des différentes versions de Windows, nombre d'applications ont été conçues pour avoir un accès direct à des emplacements systèmes ou tout simplement à la base de registres.
Sous Vista ces emplacements sont maintenant verrouillés pour réduire la surface d'attaque du système.
Pour garder une compatibilité avec les anciens programmes Vista implémente un service de virtualisation de l'UAC qui redirige de manière transparente les accès aux parties protégées du système (fichier ,base de registres) vers un emplacement virtualisé et non protégé faisant croire ainsi à l'application qu'elle écrit à l'emplacement d'origine.
Exemple :
Un programme exécuté avec des droits utilisateurs s'installe dans le répertoire :
C:\Program Files\<application>\
L'accès sera automatiquement virtualisé à l'emplacement :
C:\Users\<votre_compte>\AppData\Local\VirtualStore\Program Files\<application>
Si le programme doit impérativement écrire à l'emplacement d'origine il faudra élever les droits de l'application en administrateur.
On procédera comme suit :
Soit on change les droits d'accès sur le programme par clic droit onglet compatibilité : exécuter en tant que administrateur, un peu pénible...
ou bien on rajoutera avec les versions de Visual antérieure à Visual 2008 le fichier manifest suivant à notre application : Comment rajouter le fichier manifeste du style XP au manifeste existant dans Visual 2005 ?
Code xml : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity name="monapplication" version="1.0.0.0" processorArchitecture="X86" type="win32"/> <description>Mon Application</description> <!-- Identify the app's security requirements. --> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="requireAdministrator" /> </requestedPrivileges> </security> </trustInfo> </assembly> |
Au moment d'exécuter le programme Vista demandera l'acceptation de l'utilisateur pour l'élévation des droits sauf évidemment si vous avez désactivé l'UAC.
Pour conclure :
Il faut bien comprendre que ce mécanisme de virtualisation a été implémenté par Microsoft pour permettre une transition en « douceur » des applications.
Mais il ne faut pas trop compter sur la solution de la virtualisation, car rien ne garantit que ce mécanisme soit encore présent dans les futures versions de Windows…
Visual 2008 permet la gestion du manifest UAC au niveau du projet.
Le réglage de cette option permet de spécifier le niveau d'exécution demandé pour l'application.
Trois options sont disponibles :
AsInvoker : l'application devra s'exécuter avec le jeton actuel de l'appelant sans demander une élévation de privilèges.
highestAvailable : l'application s'exécutera avec le privilège le plus haut que le compte utilisateur possède.
RequireAdministrator: l'application doit s'exécuter avec un jeton complet d'administration.
L'UAC demandera à l'utilisateur l'autorisation de donner l'accès au jeton complet si le compte administrateur fonctionne avec le jeton restreint.
Les classes MFC utilisent l'héritage simple. Dans le cas où l'on voudrait se passer de l'héritage multiple pour enrichir des classes existantes il est possible d'utiliser les classes templates.
Démonstration :
L'exemple ci-dessus montre l'implémentation d'une classe template pour la gestion des couleurs,
Celle-ci est associée à trois classes dialogues.
Technique d'implémentation :
On déclare la classe avec un paramètre qui est aussi utilisé pour spécifier le parent.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | template <class GENERIC_DLGCOLOR /*= CWnd*/> class CTplDlgColor : public GENERIC_DLGCOLOR { public: CTplDlgColor(UINT nID=0,CWnd* pParent=NULL) :GENERIC_DLGCOLOR(nID,pParent) { /* HBRUSH */ m_HbrClrCtlBk=NULL ; /* COLORREF */ m_ClrCtlText= RGB(0, 0, 0) ; } ~CTplDlgColor() { if(m_HbrClrCtlBk) ::DeleteObject(m_HbrClrCtlBk); } virtual BOOL OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult) { if(message==WM_CTLCOLOR) { //… return TRUE; } return GENERIC_DLGCOLOR::OnWndMsg(message, wParam, lParam, pResult); } //………………… }; |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | class CTabInformation : public CTplDlgColor<CDialog> { // Construction public: CTabInformation(CWnd* pParent = NULL); // standard constructor //….. } ; |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | CTabInformation::CTabInformation(CWnd* pParent /*=NULL*/) : CTplDlgColor<CDialog>(CTabInformation::IDD, pParent) { //{{AFX_DATA_INIT(CTabInformation) // NOTE: the ClassWizard will add member initialization here //}}AFX_DATA_INIT m_pTab = NULL; } void CTabInformation::DoDataExchange(CDataExchange* pDX) { CTplDlgColor<CDialog>::DoDataExchange(pDX); //{{AFX_DATA_MAP(CTabInformation) // NOTE: the ClassWizard will add DDX and DDV calls here //}}AFX_DATA_MAP } |
Les problèmes liés à des méthodes membres qui sont présentes plusieurs fois dans les ancêtres d'une classe peuvent être résolus simplement si ces méthodes sont virtuelles comme dans l'exemple ci-dessus avec la méthode OnWndMsg.
Utilisation native des méthodes sans cast sur la classe de base ici CWnd
Souplesse d'intégration dans une classe existante.
Etc..
Les Inconvénients :
Le fait de devoir mettre le code dans le .h, ce qui nuit à la lisibilité, même en séparant dans un deuxième fichier la partie implémentation, cela peut avoir un impact sur le temps de compilation si cette classe est souvent utilisée ou si le code est conséquent.
Alors les classes patrons solution idéale ?
Presque, j'en viens maintenant au problème qui est apparu avec Visual 2005,
Un bug du ClassView empêche pour des classes dialogues (CFormView, CDialog, CDialogBar) construites à partir de classe patron la reconnaissance de la classe.
Dans ce contexte la demande de génération d'une variable contrôle à partir des ressources échoue, l'option contrôle variable étant grisée.
Pour résumer si vous utilisez une classe patron sur une classe dialogue il faudra vous passer de l'assistant pour associer une variable à son contrôle.
J'ai signalé ce problème au support Microsoft, la réponse est qu'effectivement le problème est constaté mais ce n'est pas une priorité et ne sera pas résolu…
Donc en conclusion tout était parfait avec Visual 6.0 , avec Visual 2005 et 2008 beaucoup moins..
Si l'assistant est indispensable Il faudra donc recourir à l'héritage multiple et se passer des classes patrons.
Dans les propriétés du projet sur l'onglet link/system on trouve l'option /TSAWARE.
Cette option permet de spécifier que l'application est compatible Terminal Serveur et n'apporte pas de modification au chargement du programme.
Quelles sont les conséquences de cette option ?
Dans le cas d'une application non compatible (on parle d'application héritée ou legacy)
Terminal Serveur apporte des modifications pour faire fonctionner correctement l'application dans un environnement multi-utilisateur.
Ainsi il va créer un dossier Windows virtuel pour que chaque utilisateur puisse accéder à ses propres fichiers .ini ou à son paramétrage dans la base de registres dans la clef HKEY_CURRENT_USER
Donc par défaut Visual 2008 considère que l'application est compatible avec Terminal Serveur et que vous n'utilisez pas les .ini ou la clef HKEY_CURRENT_USER
Ce qui signifie que par exemple que l'api32 GetWindowsDirectory renvoie toujours C:\windows quelque soit l'utilisateur, j'en ai fait les frais il n'y pas longtemps…
Ce qui n'était pas le cas sous Visual 6.0…
Une application compatible Terminal serveur ne gère plus un emplacement virtualisé pour la gestion des fichiers ini.
Voir ce post de la faq pour plus de renseignements Comment rendre une application compatible terminal serveur ?
Dans ces conditions comment récupérer ce chemin virtualisé ?
On utilisera la fonction SHGetFolderPath avec l'option CSIDL_PROFILE.
L'exemple ci-dessous récupère le chemin du profil utilisateur et crée le répertoire Windows s'il n'existe pas.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | TCHAR szPath[MAX_PATH]; if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PROFILE |CSIDL_FLAG_CREATE, NULL, 0,szPath))) { PathAppend(szPath, TEXT("Windows")); if(!PathIsDirectory(szPath)) CreateDirectory(szPath,NULL); } |
Le passage de Visual 2005 à Visual 2008 n'est pas sans surprises …
Vous avez dû certainement vous rendre compte que pour de vieux projets issus de Visual6 ou autres Visual définissait par défaut la plateforme système à Windows Vista ?
Laisser ce choix par défaut n'est pas sans danger, en effet certaines API comme SystemParametersInfo ne fonctionneront plus sous windows XP, la fonction renverra NULL!.
Je me sers de cette API pour récupérer la fonte système par défaut, donc par chance je me suis rendu assez vite compte du problème avec le code suivant :
Code c++ : | Sélectionner tout |
1 2 3 4 5 | NONCLIENTMETRICS ncm; ncm.cbSize = sizeof(NONCLIENTMETRICS); VERIFY(SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0)); SetFont(&(ncm.lfMessageFont)); |
Eh bien la structure utilisée a changée de taille sous Vista, en laissant le paramétrage par défaut sur Vista la taille de la structure évaluée par sizeof correspond donc à la version de l'API sous Vista, et l'appel échouera sous Windows XP.
En cherchant un peu dans les sources de Visual 2008 on trouve une référence à l'ancienne structure que vous pouvez comparer à la nouvelle..
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | struct AFX_OLDNONCLIENTMETRICS { UINT cbSize; int iBorderWidth; int iScrollWidth; int iScrollHeight; int iCaptionWidth; int iCaptionHeight; LOGFONT lfCaptionFont; int iSmCaptionWidth; int iSmCaptionHeight; LOGFONT lfSmCaptionFont; int iMenuWidth; int iMenuHeight; LOGFONT lfMenuFont; LOGFONT lfStatusFont; LOGFONT lfMessageFont; }; typedef struct tagNONCLIENTMETRICS { UINT cbSize; int iBorderWidth; int iScrollWidth; int iScrollHeight; int iCaptionWidth; int iCaptionHeight; LOGFONT lfCaptionFont; int iSmCaptionWidth; int iSmCaptionHeight; LOGFONT lfSmCaptionFont; int iMenuWidth; int iMenuHeight; LOGFONT lfMenuFont; LOGFONT lfStatusFont; LOGFONT lfMessageFont; int iPaddedBorderWidth; } NONCLIENTMETRICS, |
Dans les sources de Visual 2008, le problème est résolu pour cette API comme suit :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | struct AFX_OLDNONCLIENTMETRICS { UINT cbSize; int iBorderWidth; int iScrollWidth; int iScrollHeight; int iCaptionWidth; int iCaptionHeight; LOGFONT lfCaptionFont; int iSmCaptionWidth; int iSmCaptionHeight; LOGFONT lfSmCaptionFont; int iMenuWidth; int iMenuHeight; LOGFONT lfMenuFont; LOGFONT lfStatusFont; LOGFONT lfMessageFont; }; const UINT cbProperSize = (_AfxGetComCtlVersion() < MAKELONG(1, 6)) ? sizeof(AFX_OLDNONCLIENTMETRICS) : sizeof(NONCLIENTMETRICS); NONCLIENTMETRICS NonClientMetrics; NonClientMetrics.cbSize = cbProperSize; ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, cbProperSize, &NonClientMetrics, 0); m_MenuFont.CreateFontIndirect(&NonClientMetrics.lfMenuFont); |
Dans mon cas avec un poste de production sous Windows XP je l'ai défini dans stdafx.h (au sommet)
Code c++ : | Sélectionner tout |
1 2 3 4 5 | #if _MSC_VER > 1200 #define WINVER 0x0500 // windows 2000 #define _WIN32_WINNT 0x0501 // xp #endif |
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.