- Programmation windows : chapitre 6 - Le sous-classement et le debuggage

1. Introduction.

Nous voici arrivé au début du cinquième chapitre de cette série de tutoriaux sur la programmation sous Windows. Nous avancons petit à petit vers des sujets un peu plus avancés et vers des notions un peu plus complexes. Nous allons aujourd'hui aborder deux sujets particuliers de la programmation sous Windows : le sous-classement et quelques petits problemes liés au log d'informations.



2. La spécialisation du comportement d'un controle.

Nous avons vu que les controles possèdent un comportement par défaut. Ainsi, un controle de type EditBox par exemple est prevu pour accepter tous les types de caracteres, etc ... Ce comportement va se caracteriser en pratique comme nous l'avons deja vu par une DialogProc qui va repondre de maniere "standard" aux differents messages adresses a ce controle. Quand on y pense un peu, chaque controle repond a un besoin specifique d'organisation dans l'espace (votre ecran ou votre GUI) d'un certain type d'information. A ce controle est associe un ensemble d'action permettant d'interagir avec ce dialogue. Pour prendre un exemple plus concret et intuitif, une maison est une idee generale a laquelle sont associes une occupation spatiale particuliere (sa forme) et un certain nombre d'actions de base qui sont la par defaut (y entrer, en sortir, ... ). Une maison par defaut vient avec des portes encastrees dans ses murs externes permettant d'y entrer et d'en sortir. Bon imaginons maintenant que vous vouliez condamner les portes de votre maison et juste permettre l'entree par la cheminee. Bon, nous sommes en presence d'une sorte de maison speciale. D'une maison dont les caracteristiques different des maisons dites traditionnelles. Vous allez donc devoir modifier le comportement (les plans) par defaut de votre maison pur l'adapter au besoin specifique que vous avez ... et bien voila en gros le sous-classement !

Le sous classement est donc, en somme, une technique permettant de specialiser le comportement par defaut de n'importe quel controle. Quand on y pense cette notion correspond a peu pres a de l'heritage (cf. C++ ou n'importe quel autre langage objet). En effet, en C++, par exemple, l'heritage correspond entre autre a une specialisation de la classe mere. Or, nous sommes ici justement confronte a un desir de specialisation d'un controle. Nous voulons lui faire effectuer une operation particuliere qui sort du type d'operation qu'il a l'habitude de traiter.

Les techniques : nous avons 2 techniques qui nous permettent d'obtenir facilement le resultat de specialisation voulu : le sous-classement et le super-classement.

2.1. Le sous classement.

Le sous classement est une technique simple à utiliser et je pense simple à comprendre. Nous avons vu dans les chapitres precedents que à chaque boite de dialogue était associé une "Dialog Procedure" (DlgProc). Cette procédure est appelée par Windows à chaque fois qu'un message concernant notre boite de dialogue a été recu et doit etre traité. Pour chaque boite de dialogue que nous créons nous devons lui spécifier une DlgProc afin de lui associer un comportement utile. Nous avons déjà vu qu'une boite de dialogue n'est en fait qu'une fenetre un peu particulière composée de sous-fenetres filles (les controles). Si tel est le cas, ces sous-fenetres filles doivent disposer d'une procedure de type DlgProc par défaut qui va décrire son comportement "standard". Etant donne que nous voulons outrepasser ce comportement standard, on peut imaginer que le fait de substituer notre DlgProc à la DlgProc par défaut pour répondre aux messages qui nous intéressent pourrait résoudre efficacement notre probleme.

Et bien, c'est exactement à quoi correspond la technique de sous-classement : remplacer la dlgProc par défaut par la notre dans laquelle nous filtrerons les messages recu par notre controle et nous traiterons les messages pour lesquels nous voulons avoir un comportement spécifique et nous laisserons l'ancienne DlgProc traiter les autres messages avec un comportement par défaut.
Voici un diagramme mettant un peu de clareté dans tout ca (j'espère) :




A gauche nous avons un controle avec un comportement par défaut auquel est associé une DlgProc. A droite nous avons ce meme controle sous-classé par une DlgProc de l'utilisateur. Il faut bien voir qu'il n'est pas raisonnable de COMPLETEMENT sous classer un controle : un edit box reste un edit box meme si on le spécialise. Il faut rester dans l'esprit d'héritage et de spécialisation de comportement. C'est pourquoi meme si la nouvelle dlgProc intercepte tous les messages destinés au controle, elle doit tout de meme faire appel à l'ancienne DlgProc pour gérer les comportements qu'elle n'a pas jugée bon de spécialiser.

D'un point de vue pratique, une fonction va nous permettre de réaliser ce sous-classement : SetWindowLong().
Elle nous permet, entre autres, de substituer une DlgProc quelconque par une autre. Nous lui passons l'adresse de cette DlgProc et elle nous retourne l'adresse de la vieille DlgProc. Le dernier point auquel nous devons bien penser est d'appeler cette ancienne DlgProc pour que les autres comportement par defaut soient concerves. Cela va se faire depuis la nouvelle DlgProc grace a la fonction CallWindowProc() qui prend comme premier parametre l'adresse de la vieille DlgProc et ensuite les parametres classiques associes.
Donc si nous resumons un peu le tout, pour sous classer un controle quelconque, il nous faut :

  • Une variable de type DLGPROC (ou bien WNDPROC) qui sera chargee de pointer sur l'ancienne dlgProc du controle sous-classer, ce qui va nous permettre de ne pas la perdre,
  • Une DlgProc valide qui va prendre en charge les messages voulus et laisser a l'ancienne DlgProc le soin de gerer les autres. On oubliera pas d'utiliser CallWindowProc afin de faire appel a l'ancienne DlgProc.

  • Ensuite, il nous faut faire un petit appel a SetWindowLong() pour effectivement sous-classer notre controle.

    Cela donne dons au niveau code source quelquechose comme:

    DLGPROC	g_oldDlgProc;
    
    BOOL CALLBACK NewDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
    {
    	[...]
    	return CallWindowProc( g_oldDlgProc, hwndDlg, uMsg, wParam, lParam );
    }
    
    BOOL CALLBACK MainGUIDlgProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
    {
       switch( uMsg ) 
       {
       	[...]
    	case WM_INITDIALOG:
    	{	
    		// on specialise le controle qui nous interesse
    		g_oldDlgProc = (WNDPROC) SetWindowLong( MY_CONTROL_ID, GWL_WNDPROC, (long)NewDlgProc);
    		[...]
    	}
    	[...]
       }
       return FALSE;
    }
    
    int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
    {
    	[...]
    	DialogBox( hInstance, MAKEINTRESOURCE(NOTRE_ID), NULL, MainGUIDlgProc );
    	[...]
    }
    
    Voila en gros pour ce qui est du sous-classement. C'est une technique qu'il est bon de connaitre car elle est extremement utile et utilisee sous Windows mais aussi parce la notion de sous-classement est aussi utilise dans d'autres cadre que celui de Windows et on retrouve frequement le terme. Un exemple simple de ce type d'utilisation est celui d'un controle de type Edit dont on veut qu'il ne recoive que des entrees de type numeriques et non des caracteres de l'alphabet. Ce type de comportement n'est pas prevu par avance pour ce controle nous devons donc specialiser son comportement pour lui faire accepter que des chiffres. Voila une situation ou le sous-classement est typiquement une technique ideale. Voici le source :

    ProjetWindows_chap6_sous_class1.zip

    Voici un autre exemple ou je sous-classe un edit-control pour lui faire afficher une chose differente quand la souris passe par dessus :

    ProjetWindows_chap6_sous_class2.zip


    2.2. La technique de super-classement.

    Je passerai rapidement sur cette technique car une fois que le sous-classement a été compris, il n'y a plus grand chose à expliquer. Contrairement au sous-classement, elle permet de specialiser une classe et non pas seulement une procedure de callback. Les possibilités sont plus grandes qu'avec le sous-classement puisqu'il nous est possible de modifier plusieurs caractéristiques du controle en agissant directement au niveau de sa WNDCLASS. Nous allons donc , dans ce cas, recuperer la WNDCLASS de notre controle, changer le nom de cette WNDCLASS et modifier les champs qui nous interessent pour finir par ré-enregistrer cette classe. Ce que nous faisons effectivement c'est créer une nouvelle classe qui va avoir les memes caractéristiques que la classe du controle que nous voulons spécialiser, remplacer les caracteristiques de cette classe par celles que nous voulons, et enregistrer cette classe. Mais attention, TOUTES les fenetres que nous pourrons creer avec le nom de cette classe auront ainsi le meme comportement que le controle visé mais avec une specialisation voulue. Il faut bien comprendre que le sous-classement ne s'attaque qu'a un controle en particulier lui en lui associant une nouvelle WNDPROC.



    3. Les problemes de log et debuggage sous Windows.


    Je vais maintenant aborder le sujet du debuggage et du log d'information sous Windows. Loin d'etre une presentation exhaustive, je vais plutot voir quelques points qui sont plus apparentés à des trucs intéressant à connaitre qu'à des éléments essentiels dans la programmation sous Windows. Quelques une de ces informations seront spécifiques a VC++, je le signalerai dans ce cas de figure.

    3.1. Les macros d'environnement.

    Sous windows, il existe un certain nombre de macros specifiques à cet environnement qui nous permettent d'avoir acces a une foule d'informations utiles. Par exemple, la macro _WIN32 nous permet de savoir si nous sommes dans un programme win32 ou pas. La macro __cplusplus nous permet de savoir que nous sommes dans un source c++ et _MSC_VER (specifique a VC++) permet d'avoir acces a un element d'information concernant la version du compilateur utilise, Voici une table resumant les valeurs possibles de _MSC_VER :

    Version du compilateurValeur de _MSC_VER
    C Compiler version 6.0600
    C/C++ compiler version 7.0700
    Visual C++, Windows, version 1.0800
    Visual C++, 32-bit, version 1.0800
    Visual C++, Windows, version 2.0900
    Visual C++, 32-bit, version 2.x900
    Visual C++, 32-bit, version 4.01000
    Visual C++, 32-bit, version 5.01100
    Visual C++, 32-bit, version 6.01200

    la macro _DEBUG (specifique a VC++) permet de savoir si on est en debug ou release mode.

    3.2. Des outils de log et de debug.

    Dans un projet logiciel, que ce soit en phase de debuggage pure ou en phase de test, il est souvent utile d'avoir acces a des informations de type texte pouvant, par exemple, renseigner sur l'etat de l'application en cours ou bien fournir des informations specifiques sur une erreur qui vient de survenir. Pour un projet Win32, alors que logger des informations dans un fichier ne pose aucun probleme, il est moins evident de logger simplement des informations a l'ecran comme nous pouvons avoir l'habitude de le faire avec des printf (ou cout) sous DOS, Linux, etc ... Et bien, nous allons etudier a travers quelques exemples des solutions simples permettant d'afficher du texte dans une boite de dialogue, dans la fenetre de debug, et pour finir dans une console DOS !

    NOTE: j'utilise le terme 'log' comme un equivalent de "isoler des informations pertinentes au qu'un programme tourne" et logger comme un equivalent de "mettre de cote ces informations pertinentes".

    3.2.1. Un meilleur assert().

    La macro assert() est une macro que tout programmeur devrait utiliser. Pour ceux pour qui elle n'évoque rien, elle permet de vérifier la validité d'une expression booléenne passée en paramètre. Si cette expression est fausse, assert provoque la fin du programme et affiche un message d'erreur. Chose importante a savoir c'est que cette macro n'agit qu'en mode Debug et ne fait rien quand un programme est compile en version Release. Nous pouvons donc en truffer notre source sans aucune penalite pour la version finale de notre application mais avec une verification maximum en version Debug.

    NOTE: pour ceux qui ne sont pas a l'aise avec ces notions de Release et de Debug, voici quelques explications supplementaires. Un compilateur propose souvent une option Debug ou Release. Pour une application compilee en Debug, le compilateur va rajouter une foule d'information a notre executable devant par la suite faciliter le tracage du code pas a pas, etc ... Parmis ces informations, on a la table des symboles qui va contenir la liste des symboles de votre application (variables, ...) ainsi qu'un ensemble d'information les concernant, etc ... En mode de compilation Debug, le compilateur ne va pas chercher a optimiser votre code et va ainsi fournir un code machine brute "de base". En mode release, tout ceci est oublie bien entendu. Il est bon dans un projet de rester en mode Debug jusqu'au dernier moment mais attention cette regle peut parfois reserver des surprises. Il n'est pas rare qu'un programme semblant fonctionner en Debug crash violement en Release. La raison est souvent un probleme de violation de memoire qui n'avait pas ete detecte. En mode Debug, la gestion memoire est, en effet, plus "assistee" et peut masquer des erreurs qui resortent en mode Release ou aucune verification n'est faite. C'est a vous de regarder avec le compilateur que vous utilisez la maniere de voir comment passer du mode Release au mode Debug.

    Je vous propose deux possibilites simple pour notre version de assert(). L'une va utiliser un debugger et l'autre va utiliser une boite de dialogue.

    3.2.1.a. Le debugger.

    Un debugger de maniere generale est une application qui va assurer un certain nombre de services minimum vous permettant de verifier l'etat de fonctionnement de votre application. Parmi ces services, on trouve des possibilites d'execution pas a pas, de mettre en place des breakpoint ( ou "point d'arret", qui va vous permettre de lancer votre programme et de le laisser tourner jusqu'a ce qu'il atteigne une certaine ligne de votre code source, ligne a laquelle il s'arretera vous permettant d'inspecter l'etat de vos variables, etc ...), etc ... Ce debugger peut prendre plusieurs forme, sous VC++ il est integre a l'EDI et vous pouvez y avoir acces directement dans le menu "Build>Start debug", sous gcc le debugger prend la forme d'une application a part : gdb, etc ...



    Il est possible de demander au debugger d'afficher des informations. La maniere dont ces informations vont etre affichees va dependre du type du debugger. Sous VC++ par exemple, elles apparaissent dans une petite fenetre horizontale au bas de la fenetre texte principale.



    Une fonction simple va nous permettre de le faire : OutputDebugString(). Tout simplement. Chose importante a comprendre, meme si votre programme est compile en mode Debug, il ne vas pas forcement systematiquement tourner dans un debugger, il peut etre tres bien etre lance en dehors de toute application de debug. Il est ainsi preferable de prevoir le cas ou votre application ne tourne pas en mode debug. Une fonction est la pour nous aider dans cette demarche : IsDebuggerPresent(). Nous allons donc pouvoir avoir un comportement de notre macro assert qui va varier en fonction de la presence ou non d'un debugger. Notre macro commence ainsi a ressemble au niveau du fonctionnement a celui de la macro assert() classique. Il nous manque une chose : mettre fin au programme. Nous pourrions mettre brutalement fin a l'application avec un exit() mais ce comportement ne serait pas satisfaisant dans un environnement de type debugger. En effet, quand un assert() avec une condition fausse survient alors que l'application tourne avec le debugger, il y automatiquement un breakpoint qui est insere et qui va nous permettre d'arriver rapidement au point d'assertion et de constater l'etat de notre application a ce moment du programme. Il nous faut donc creer artificiellement un breakpoint. Facile :). Un moyen de se rendre compte d'une maniere dont peut se faire un breakpoint avec assert est de suivre la methode suivante :

  • Creer un nouveau projet console bidon quelconque, y taper le code suivant :

  • 
    #include <stdio.h>
    #include <assert.h>
    
    int main(void)
    {
       assert(0);
       	
       return 0;
    } 
    

  • Compiler l'application en mode debug, et la lancer avec le debugger (menu Build>Start debug>go ou bien F5),
  • Lorsque la boite de dialogue indiquant une assertion apparait, cliquer sur Retry puis Cancel pour la boite de dialogue suivante,
  • Le debugger va alors vous dire qu'un breakpoint vient d'arriver en vous donnant une adresse. Cliquer sur OK, et la un listing de code assembleur apparait. si tout va bien la premiere ligne de ce source doit etre pointee par une fleche jaune qui indique ou on en est au niveau de l'execution de votre programme. Et elle pointe sur un "int 3". Voila. Une assertion vient de survenir et votre pointeur de programme pointe sur une instruction "int 3". Et bien voila, cette instruction assembleur va nous permettre de provoquer a n'importe quel moment un breakpoint. Il existe aussi un autre moyen d'obtenir un breakpoint a tout moment : utiliser la fonction DebugBreak(). Elle va adapter son comportement en fonction de la presence ou non d'un debugger : si oui elle va agir comme un "int 3" sinon elle va simplement provoquer la fin de l'application incriminee avec une exception appropriee. Meme si les deux methodes ont un comportement identique DebugBreak() a un comportement plus propre car "int 3" fait crasher le programme quand il n'y a pas de debugger. Nous avons maintenant un comportement correct pour notre macro assert.



    Voila une maniere possible de voir la chose en terme de code C :

    #define MAX_MESSAGE		256
    
    #if defined(_DEBUG)
    
    #define MY_ASSERT(condition) {							\
    			char szMessage[MAX_MESSAGE];					\
    			_snprintf(szMessage, sizeof(szMessage), "ERREUR D'ASSERTION : fichier %s, ligne %d, condition : %s.\n", __FILE__, __LINE__, #condition);	\
    			if( !(condition) ) {						\
    					 if( IsDebuggerPresent() )			\
    						OutputDebugString(szMessage);		\
    			}									\
    			}						
    #else
    
    #define MY_ASSERT(condition)
    
    #endif
    
    

    Cette solution est tres loin d'etre la meilleure mais c'est une possibilite.

    3.2.1.b. La boite de dialogue.

    Une autre solution pouvant nous venir a l'esprit est d'utiliser un boite de dialogue pour obtenir une sortie d'assertion formatte comme on le desire. Cette boite de dialogue peut etre une simple message box ou bien une boite un peu plus developpee avec des commentaires eventuels sur le type de problemes rencontres, etc ...
    Je ne vais pas m'ettendre la dessus etant donne que vous avez deja toutes les clefs en main pour vous permettre de le faire vous meme.

    3.2.2. Logger des informations.

    Au cours du developpement d'une application, il est parfois necessaire d'avoir une sortie de texte permettant d'afficher des informations pertinente en cours d'execution. Afin de parvenir a ce comportement plusieurs solutions s'offrent a nous :

  • Utiliser un EditBox,
  • Afficher a chaque fois des MsgBox (!!!),
  • Utiliser la sortie du debugger (OutputDebugString),

  • Chaque solution a ses aspects negatifs et ses aspects positifs plus ou moins remarquables. Je vous propose cependant d'utiliser un autre moyen : logger les informations dans une console. Meme si il ne s'agit pas, a priori, de la de la meilleure solution, ca va me donner l'occasion de vous parler des consoles et de la maniere de les inclure dans vos applications windows.

    Une console sous Windows est un moyen physique pour une application d'interagir avec le monde exterieur (usage, ecran ...) a travers des flots entrant ou sortant de caracteres. Elle va ainsi servir d'interface permettant de transmettre un flot de donnees de type caracteres de l'utilisateur vers l'application (grace au clavier) et de l'application vers l'utilisateur grace a l'ecran.





    Il est possible d'avoir un controle tres fin sur le comportement d'une console et de lui faire faire des choses tres interessantes. Nous allons juste effleurer le sujet pour mettre en evidence les fonctionnements de base d'une console et les manieres de l'utiliser. Il va nous etre possible, a terme d'ecrire dans cette console en utilisant simplement des printf() du C, par exemple.

    Une premiere chose a savoir est qu'il ne peut y avoir qu'une seule console par processus (process en anglais). En gros, pour nous ca veut dire qu'il ne peut y avoir qu'une seule console par application. L'utilisation d'une console va pouvoir se faire en suivant un certain nombre d'etapes tres simple :
    	1. Associer une console a notre application,
    	2. Recuperer les handles associes a cette console devant nous permettre d'y ecrire et
    d'y lire,
    	3. (optionnel) Formatter l'apparence de la console a notre bon gre,
    	4. Utiliser la console.
    
    D'un point de vue pratique, il va nous falloir d'abord attacher une console au process de notre application. La fonction AllocConsole() est la pour nous aider dans cette demarche.
    Ensuite, il nous faut trouver un moyen d'ecrire et de lire dans cette console. Les fonctions classiques du C nous permettent d'ecrire dans un fichier ou sur la sortie standard grace a un mecanisme de handles de fichiers. On ne sait pas trop a priori a quoi ils correspondent en realite et on s'en moque puisque c'est un mecanisme qui est dependant de l'OS et qui ne nous concerne pas. En gros, il faut savoir que chaque handle de ce type est associe a une structure de donnee qui va directement referer a un objet de sortie ou d'entree. Ainsi, un printf() par exemple va aller ecrire une chaine de caractere formattee dans la sortie standard referencee par le handle stdout. Cette sortie standard est par defaut dirigee vers l'ecran. Dans notre cas de figure, pour pouvoir ecrire et lire de la console, il nous faut trouver quels sont les handles associes a celle-ci en sortie et en entree. La encore l'API win32 va nous fournir une fonction pour ca : GetStdHandle(). Il est alors possible de recuperer des handles correspondant directement a un flux sortant de caracteres vers le console, a un flux entrant de caractere provennant de la console, et a un flux sortant de caractere dirige vers la sortie de messages d'erreur associes a la console.
    HANDLE hConsoleStdout = GetStdHandle( STD_OUTPUT_HANDLE );
    HANDLE hConsoleStdin = GetStdHandle( STD_INPUT_HANDLE );
    HANDLE hConsoleStderr = GetStdHandle( STD_ERROR_HANDLE );
    
    Il nous est alors possible d'ecrire dans la console avec quelques fonctions specifiques du type WriteConsole(), ReadConsole(), etc ... On peut change la couleur, la taille des caracteres et de la console elle meme, ce qui pourrait nous permettre de faire une console qui affiche les messages de log en vert, les warning en bleu et les messages d'erreur en rouge par exemple ! ...
    Encore mieux, il est possible d'utiliser des simples printf pour afficher dans la console ou encore des scanf pour recuperer des infos ... Pour cela, il nous faut bien comprendre que les fonctions du type printf() vont utiliser les entrees/sorties standards (clavier/ecran). Or nous voulons que ces entrees/sorties standards soient redirigees vers notre console. La fonction standards du C freopen nous permet de reassigner un handle de fichier deja ouvert a un autre fichier. Dans notre cas, les handles de fichiers a rediriger seraient stdin/stdout respectivement vers l'entree et la sortie de la console symbolises par les noms de fichiers "CONIN$"/"CONOUT$". Ca donnerait donc :
    freopen("CONIN$", "rb", stdin);
    freopen("CONOUT$", "rb", stdout);
    freopen("CONOUT$", "rb", stderr);
    
    Facile hein ? :) On peut imaginer enormement d'applications a ce types de solutions. Voici un exemple simple de ce type de console en action.

    Voici un exemple resumant l'utilisation d'une console :

    ProjetWindows_chap6_console.zip


    3.3. Cloture du sujet et memory leaks.

    L'API win32 de Windows offre un grand nombre de fonctions facilitant le debuggage des applications. Nous n'avons qu'effleure les possibilites offertes et il faudrait au moins 1 ou 2 tutorial en plus pour en explorer les possibilites (peut etre que je m'y mettrais si je recois des demandes). Mais il faut bien que je me force a m'arreter un jour parce que deja la j'ai tellement rajoute de choses non prevues initialialement que si je continue non seulement ce tut ne sortira jamais mais en plus il fera 10 pages de plus. Je vais quand meme me faire un petit plaisir :) en rajoutant une fois de plus une choses non prevue. Je voudrais en effet dire 2 mots sur les problemes de memory leaks et sur la maniere dont Windows peut nous aider a y trouver une solution. Ces problemes ne sont pas si eloignes que ca du sujet de debug puisqu'ils sont symptomatiques de bugs existant dans l'application.

    Tout d'abord, qu'est ce qu'un memory leak ?
    Et bien comme son nom l'indique, il s'agit d'une fuite de memoire incontrolee dans votre application. La petite experience que je peux avoir dans le monde professionel en tant qu'ingenieur en informatique montre que les risques de memory leaks augmentent exponentiellement au fur et a mesure que la taille du projet augmente et que le nombre de personnes concernees augmente aussi. Il s'agit vraiment la d'une plaie dont il est preferable d'etre conscient afin de pouvoir des le debut d'un projet prendre des bonnes habitudes. Un memory leak intervient lorsque vous avez allouer dynamiquement de la memoire dans votre code et que vous n'avez pas de liberation associee a cett eportion de memoire allouee. Etant donne qu'en C ou C++ il n'y pas de garbage collector comme en Java ou en Smalltalk par exemple c'est a vous de prendre en charge la gestion de memoire. Les memory leaks peuvent provoquer des maux bien ennuyeux a l'environnement dans lequel tourne votre application et donnent une impression de non professionel et de non-fini a votre application. Elle peuvent ainsi etre responsable de nombreux symptomes bien ennuyeux comme une augmentation dramatique du swap sur disque et donc un ralentissemnt de votre environnement, etc ...

    Voici un exemple bien bete de memory leak :

    
    class CMemoryLeakSource 
    {
      public:
    	CMemoryLeakSource();
    	~CMemoryLeakSource();
    
      private:
    	char 		* m_lpDummy;	
    }
    
    CMemoryLeakSource::CMemoryLeakSource()
    {
    	m_lpDummy = new char[10];
    }
    
    CMemoryLeakSource::~CMemoryLeakSource()
    {
    	// la memoire allouee devrait etre liberee
    }
    
    
    int main(void)
    {
    	CMemoryLeakSource  test;
    	return 0;
    }
    
    
    Cet exemple peut paraitre stupide (et il l'est) mais ce genre d'erreur est frequente dans un gros projet quand on en vient, presse par les deadlines, a patcher violement le code pour faire en sorte que ca marche a grand coup de cut-and-paste, etc ...
    Windows met a votre disposition un tres grand nombre d'outils permettant de detecter ces memory leaks et d'avoir une analyse de la memoire utilisee par votre application tres fine et precise. Il existe aussi un tres grand nombre de fonctions permettant d'avoir un rapport en mode texte precis resumant ces resultats. Ces fonctions proviennent de la bibliotheque CRT (C RunTime). Les fonctions en question commencent par un "_Crt".
    Pour pouvoir utiliser ces fonctions il convient d'inclure le bon header comme suis :

    #define _CRTDBG_MAP_ALLOC 
    #include <stdlib.h> 
    #include <crtdbg.h>
    
    En utilisant ces fonctions de diagnostic memoire, il vous est possible de maniere tres simple d'avoir des rapports relativements detailles sur les memory leaks que peut creer votre application.

    Pour aller plus loin.

    Voici une serie d'utilitaires commerciaux ou non parmi les plus connu permettant une gestion/detection des memory leaks travaillant au niveau du binaire directement ou du code source :

    BoundsCheckerhttp://www.compuware.com/products/numega/bounds/
    Purify de Rationalhttp://www.parasoft.com/products/insure/
    Electric Fence http://perens.com/FreeSoftware/
    ZeroFault http://www.tkg.com
    Fortifyhttp://www.geocities.com/SiliconValley/Horizon/8596/fortify.html




    Conclusion :

    Nous voila enfin au terme de ce tutorial qui aura finalement balaye plusieurs sujets plus generaux concernant la programmation sous Windows. J'espere que vous commencez a etre un peu a l'aise avec la programmation sous Windows. Si ce n'est pas le cas n'hesitez pas a m'ecrire, je reponds a tous les emails que je recois plus ou moins rapidement en fonction de mon temps libre. N'hesitez pas aussi a me faire parvenir des critiques, remarques, et des idees de sujets que vous voudriez voir aborde. Pour vous donner une idee sur ce qui va venir dans les mois prochains, voici ce que je prevois d'aborder (dans l'ordre) :

    	- Les problemes d'ajustement de taille de fenetre,
    	- Les precompiled headers,
    	- Le format PE,
    	- Le multi-thread en profondeur (3 tuts),
    	- Les "common controls" (? tuts),
    	- Les DLLs et autres libs (3 tuts),
    
    Voila ca en fait des choses pour cette annee prochaine, on verra la ou j'en serais en debut d'annee prochaine. Je suis ouvert au fait d'aborder des sujets autres biensur.



    Quelques liens interessants :

  • Memory Leaks :
  • 	http://www.cs.colorado.edu/homes/zorn/public_html/MallocDebug.html
    	http://www.codeguru.com/cpp_mfc/MemoryTracking.shtml
    	http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvc60/html/memleaks.asp
    	http://tehran.stanford.edu/Iran_Lib/doreh/Events/1995/april.2.1995.html
    	http://www.rational.com/products/whitepapers/319.jsp
    	http://personal.cfw.com/~tkprit/page/leak.html
    	http://www.edm2.com/0508/membug.html
    
    --
    Document ecrit par ABREU Alexandre : wiss1976@yahoo.fr
    Libre reproduction et diffusion autorisée - modifications interdites sans autorisation de l'auteur.

    Précédent