Forum de la C-P-F

Version complète: [Win API] Un carnet d'adresses
Vous regardez actuellement la version basse qualité d'un document. Voir la version complète avec le bon formatage.
Oui le prototype de ta fonction devrait retourner quelque chose ... mais le compilateur aurait du au minimum te sortir un avertissement non?

je lirai de plus près la suite mais le mieux quand tu passes une structure à une fonction est de la passer par référence. Cela évite la recopie.
Code CPP :
bool creerfonctiontemoins(hwnd hwnd,hwnd *hwndfille, temoins *temoins, wparam wparam);

ou
Code CPP :
bool creerfonctiontemoins(hwnd hwnd,hwnd *hwndfille, temoins &temoins, wparam wparam);

en effet ta structure temoins est :
Code CPP :
struct temoins
{
    bool couv;
    bool liste;
    bool fiche;
};

or le type bool est en fait un int, donc 4 octets. ta structure fait donc 12 octets. un pointeur ã©tant sur 4 octets ã©galement (sur un pc 32bits), ã§a fait 8 octets de moins ã  allouer lors du passage de paramã¨tres.
en revanche si tu avais utilisã© le type bool, alors ta structure ne ferait plus que 3 octets, donc le passage par rã©fã©rence ne se justifie plus. (le type bool n'ã©tant disponible qu'ã  partir de vc++5, je ne sais pas sur les autres compilateurs).
Code CPP :
struct temoins
{
    bool couv;
    bool liste;
    bool fiche;
};


Je chipote pour pas grand chose, mais c'est une bonne habitude à prendre Wink

edit: J'en profite pour signaler un bug.
si tu consultes une fiche (petit bouton 'V'), la fenêtre qui s'ouvre n'est pas modale par rapport à la fenêtre mère. Si on laisse donc cette fenêtre ouverte et qu'on clique à nouveau sur le bouton 'V', on se retrouve avec 2 fois la même fiche ouverte. Le problème c'est que le bouton 'Fermer' de la première fiche ouverte ne fonctionne plus^^ ...
A la fin de la présentation de mon code je pourrai le corriger et le présenter ici ...mais ça va demander du temps ! Smile

Toam a écrit :
...quand tu passes une structure à une fonction est de la passer par référence. Cela évite la recopie. ...

Je n'ai pas bien compris "par référence" :- (j'ai de graves lacunes je sais... j'ai appris sur internet, alors j'ai dû zapper des choses et je n'ai pas tout le vocabulaire).

Aujourd'hui je vais présenter deux fonction parce qu'elles sont toute petites :
creerAfficherFenetreFille et detruireFenetresFilles


Code C :

void creerafficherfenetrefille (hwnd hwnd,hwnd * hwndfille,long numero,char * classe, wndproc proc,long onglet)
{
    detruirefenetresfilles( hwndfille);
    creerfenetrefille(hwnd,hwndfille,numero,classe,proc,onglet);
}
 

lã  c'est vraiment pour dã©composer... j'aurais pu mettre les deux fonctions dans une seule ;d
en gros en premier je fais le mã©nage, et aprã¨s je rã©amã©nage.



Code C :

void detruirefenetresfilles( hwnd *hwndfille)
{
    long i=0;
    for (i=0;i<3;i++)
    {
        sendmessage(hwndfille[i],wm_destroy,0,0);
    }
}

 


... d'où l'utilité du tableau de handle de fenêtre. Question : est ce qu'on peut détruire une fenêtre qui n'existe pas sans problème ?

Demain au programme : création des fenêtres filles.
Bonjour !
Voici la fonction du jour creerFenetreFille

Code C :

void creerfenetrefille(hwnd hwnd,hwnd *hwndfille,long numero,char* classe, wndproc proc, long onglet)
{

    static wndclass wc;
    static long largeur=0,hauteur=0;
    dimfenetre(&largeur,&hauteur);

   // structure de la classe de la fenetre principale
    wc.style=0;
    wc.lpfnwndproc = proc;
    wc.cbclsextra = 0;
    wc.cbwndextra = 0;
    wc.hinstance = hinstance;
    wc.hicon = null;
    wc.hcursor = loadcursor(null,idc_arrow);
    wc.hbrbackground = createsolidbrush(rgb(192,192,192));
    wc.lpszmenunamenull;
    wc.lpszclassname = (lptstr )classe;
   //enregistrer la classe de fenetre
    registerclass(&wc);

    //creer la fenetre
      hwndfille[numero] = createwindow((lptstr )classe,(lptstr ) "",ws_child ,0,0,largeur ,hauteur, hwnd,null, hinstance, onglet);

    if ( ! hwndfille[numero] ) return false;
    showwindow ( hwndfille[numero] , sw_show);
}

 


Cette fonction ne retourne rien contrairement à ce que j'avais prévu dans l'organigramme... :- (même erreur)
Il faut prévoir une procédure pour chaque fenêtre fille où seront traité les messages envoyés.

Enfin une petite précision pour les néophytes, il y aura une procédure pour chaque boite de dialogue.
Bonjour,
il n'y avait pas foule ce week end ! Smile
Aujourd'hui je vais vous présenter un gros morceau :messagesMenu la fonction qui gère les messages envoyés au menu de la fenêtre principale.

Voici son énorme organigramme (on ne dit pas ordinogramme pour un programme ?)


le code qui est étonnamment beaucoup plus succinct :
Code C :

void messagesmenu (hwnd hwnd,hwnd hwndcouv,wparam wparam,lparam lparam)
{
    hwnd hwndfille=null;

     switch (loword(wparam))
    {
            case idm_nouveau :
            dialogbox(hinstance, "dialog1" , hwnd, (dlgproc)dialog1proc);
            break;

            case idm_ouvrir :
             chargercarnet(hwnd);
             invalidaterect(hwndcouv,null,true);
            break;

            case idm_quitter :
            fermeapplication (hwnd);
            break;

            case idm_nouvellefiche :
            ouvrirnouvellefiche (hwnd);
            break;

            case idm_retour :
            ouvrircouverture(hwnd);
            break;

            case idm_aide :
             shellexecute(hwnd, text("open"),
                            "d:\programmation\carnet\carnet.chm",
                             null, null, sw_shownormal);
            break;

            case idm_apropos :
            dialogbox(hinstance, "dialog2" , hwnd, (dlgproc)dialog2proc);
            break;

            case idm_options :
            dialogbox(hinstance, "dialog3" , hwnd, (dlgproc)dialog3proc);
            sendmessage(findwindow ("dialog","dialog3"),wm_handle,hwndcouv,0);
            break;

            default:
            break;
    }
}

 


on peut remarquer qu'on traite chaque identifiant du menu que l'on avait dã©fini dans le fichier header du programme. demain je vous montre comment j'ai traitã© idm_nouveau, le message envoyã© quand on choisit l'option fichier/nouveau du menu.

je viens de remarquer que l'aide ne doit pas fonctionner chez vous ! Rolleyes il faut que j'arrange ã§a ! le programme doit certainement planter ã  ce niveau.

voilã  c'est fait:
je dã©clare la variable dossier : char dossier [max_path]={0};
et le code devient pour idm_aide:
Code C :

            case idm_aide :
            getcurrentdirectory(max_path,(lptstr)dossier);
            strcat(dossier,"\carnet.chm");
             shellexecute(hwnd, text("open"),dossier,
                             null, null, sw_shownormal);
            break;
 


Je mettrai la nouvelle compilation en ligne plus tard elle sera téléchargeable depuis le lien du premier message du topic. Smile
:o heu trop de chose là ... je vais juste revenir sur ces points

acryline a écrit :
Je n'ai pas bien compris "par référence" :- (j'ai de graves lacunes je sais... j'ai appris sur internet, alors j'ai dû zapper des choses et je n'ai pas tout le vocabulaire).

En fait ce n'est pas très compliqué si on ne cherche pas à compliquer les choses.
Les paramètres d'une fonction peuvent être passé de deux manières différentes : par valeur, par adresse (ou référence).
par valeur :
exemple de code

Code CPP :
void mafonction(int a){...};
void main()
{
    int var=5;
    mafonction(var);
}
 

lorsque tu appelles la fonction mafonction, le contenu de la variable var est "recopiã©" dans la variable a. donc quand tu modifies a tu ne modifies pas var. on a donc bien passã© une valeur ã  la fonction.
par valeur :
exemple de code
Code CPP :
void mafonction2(int &b){...};
void mafonction3(int *c){...};
void main()
{
    int var=5;
    mafonction2(var);
    mafonction3(&var);
}
 

lorsque tu appelles mafonction2 et mafonction3, tu ne passes pas la valeur de la variable var, mais l'adresse de la variable. ainsi, dans mafonction2, &b (adresse de b) est l'adresse de la variable vra, et dans mafonction3, c est l'adresse de la variable var ... . ainsi dans ces deux fonctions, si l'on modifie la valeur de b, ou la valeur de *c, on modifie bien var. c'est clair?

arf, tu vas me dire, "mais pourquoi deux ã©critures?" "et c'est quoi la diffã©rence entre passage par adresse et par rã©fã©rence?"
... lã  je te laisse te ballader sur les nombreux dã©bats sans fin ...
d'un point de vu pratique j'aime bien utiliser le passage par rã©fã©rence pour un confort d'ã©criture
ex. de 2 fonctions faisant la mãªme chose.
Code CPP :

typedef struct _st1 {int a;int b;} st1;
void mafonction1 (st1 &mastructure)
{
    mastructure.a=5;
    mastructure.b=7;
}
void mafonction2 (st1 *mastructure)
{
    mastructure->a=5;
    mastructure->b=7;
    //ou encore
    (*mastructure).a=5;
    (*mastructure).b=7;
}
void main()
{
    st1 mast;
    mafonction1(mast);
    mafonction2(&mast);
}
 

moi perso, je prã©fã¨re l'utilisation de mafonction2 (elles font bien strictement la mãªme chose!!!) et l'ã©criture du code comme celã , mais il faut prendre garde qu'on ne sait plus forcement si la fonction va modifier ou non les paramã¨tres au bout d'un moment ... c'est un risque que je prends.

acryline a écrit :
Code C :

void detruirefenetresfilles( hwnd *hwndfille)
{
    long i=0;
    for (i=0;i<3;i++)
    {
        sendmessage(hwndfille<em>,wm_destroy,0,0);
    }
}
... d'où l'utilitã© du tableau de handle de fenãªtre. question : est ce qu'on peut dã©truire une fenãªtre qui n'existe pas sans problã¨me ?

heu ... oui, pas grand risque, aucun gestionnaire d'ã©vã¨nement ne pourra intercepter cet appel. cependant, ã§a ne sert ã  rien et il vaut mieux donc l'ã©viter. pour cela il y a une rã¨gle ã  toujours appliquer. lorsque que l'on manipule un objet, pointeur ou autre, et qu'on ne l'utilise plus, [i]"on le met ã  0".kesako?
hwndfille est un tableau de handle. or un handle invalide vaut null.
donc lorsque tu dã©truis ta fenãªtre, tu mets ce handle ã  0 (aprã¨s la destruction rã©el de ta fenãªtre).
ainsi, avant d'envoyer un message il te suffit de faire un test sur le handle (ou hwnd) pour savoir s'il est valide ou non.
ta fonction devient donc:

Code C :
void detruirefenetresfilles( hwnd *hwndfille)
{
    long i=0;
    for (i=0;i<3;i++)
    {
        if(hwndfille[i]!=null){
            sendmessage(hwndfille[i],wm_destroy,0,0);
            //si tu n'utilises pas ce hwnd autre part, alors il faut faire ici
            hwndfille[i]=null;
        }
    }
}
Bonjour et merci pour tes explications Toam Smile

Comme je l'ai dit hier je vais vous montrer comment j'ai procédé pour traiter le cas où on choisit l'option
Fichier/Nouveau dans le menu.

J'ai déjà créé une boite de dialogue dans un fichier ressource (ressource.rc) ainsi Sadau passage j'ai aussi ajouté l'icône utilisée pour créer la fenêtre parent).

Code C :

#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include "fonctions.h"

icone1 icon "bonnet.ico"

dialog1 dialog
   10, 10, 160, 120
          style ws_popup | ws_visible | ws_caption | ws_sysmenu
                                        caption "crã©er un nouveau carnet d'adresses "
begin
    defpushbutton "ok", id_ok, 56, 55, 42, 12
    ltext "nom du nouveau carnet :", -1, 10, 15, 90, 10
    edittext ide_edit1, 45, 30, 60, 12,es_left | ws_border | ws_tabstop
    ltext "caractã¨res permis :", -1, 10, 70, 90, 10
    ltext "abcdefghijklmnopqrstuvwxyz", -1, 10, 80, 150, 10
    ltext "abcdefghijklmnopqrstuvwxyz", -1, 10, 90, 150, 10
    ltext "0123456789_", -1, 10, 100, 150, 10
end
 


cette boite de dialogue est une fenãªtre windows "prã©-fabriquã©e" ã  laquelle on ajoute les contrã´les nã©cessaires.
ces contrã´les ici sont, un bouton ok des zones texte ltext et un contrã´le d'ã©dition edittext.
id_ok et ide_edit doivent ãªtre dã©finit dans le header :
Code C :

//ressource
#define ide_edit1   101
#define id_ok       102
 


on doit ã©crire la procã©dure de cette fenãªtre:


Code C :

bool apientry dialog1proc(hwnd hdlg,uint umsg,wparam wparam,lparam lparam)
{

    switch (umsg)
    {
      case wm_initdialog:
      //c'est ici qu'il est possible d'entrer du code pour modifier l'apparence de la boite de dialogue.
      return true;

      case wm_command:
      if (loword(wparam) == id_ok)
                {
                   nouveaufichieradresse ( hdlg);
                   return true;
                }
      return true;

     case wm_close:
      enddialog(hdlg,0);
     return true;

        default:
        return false;
    }
}

 


J'ai aussi envisagé le message WM_INITDIALOG bien que cela soit inutile ici. En effet j'aimerais modifier
l'apparence de la boite d dialogue (couleurdu texte, du fond, la police..).
Franchement (si si réellement), je suis très impressionné par ce que tu fais. Je ne me suis jamais amusé à tout coder à la main (sauf pour de vieilles applications que je maintiens) et surtout à décrire la gestion des évènements comme tu le fais dans tes diagrammes.
Moi j'utilise VC++6 et VS.NET et j'utilise les wizard à fond ( bon faut dire aussi que lorsque tu as des dizaines et des dizaines de fenêtres, ce les faire à la main c'est pénible à force).
Je suis toute confusionnée Smile Mais franchement, quand tu me parles de "VC++6 et VS.NET " je n'ai qu'une très vague idée de l'usage qu'on peut en faire. Big Grin

Allez, pour aujourd'hui une petite fonction, (celle de la procédure de la boite de dialogue vue ci-dessus) : nouveauFichierAdresse



Code C :

void nouveaufichieradresse (hwnd hdlg)
{
   char nom[max_path]={0};
    //trouver le texte du controle d'edition
    getdlgitemtext(hdlg, ide_edit1,nom,max_path);
    //tester la chaine
     if (!testchaine(nom))
     {
        if(messagebox(hdlg,"le nom entre n'est pas autorise", "attention",mb_okcancel|mb_iconwarning)==idcancel)
        {
            enddialog(hdlg,0);
        }
    }
    else
    {
    creernouveaucarnet(hdlg,nom);
    }

}
 
Bonjour,
aujourd'hui je vous présente deux petites fonctions. testChaine et testLongueur



Code C :

bool testchaine(char*nom)
{
    long mini=0, maxi=256;
    if(! testlongueur(nom,mini,maxi)) return false;
    if(! testcaracteres(nom)) return false;
    return true;
}
 


Code C :

bool testlongueur(char*nom,long mini, long maxi)
{
if (strlen(nom)==0 || strlen(nom)>256)
    {

      return false;
    }
    else
    return true;
}
 
Bien matinale acryline ...

ben pas grand chose à dire mais comme je ne veux pas te laisser seul à poster sur ce forum je vais faire quelques commentaires Wink.
D'abord sur la fonction testLongueur.
Je pense que tu l'as fais exprès car tu pensais ajouter plus de fonctionnalités mais les variables long mini et long max ne servent à rien ici puisque tu ne les utilises pas.
Mais si tu veux utiliser ces paramètres, alors il faut tenir compte des types de valeurs que tu vas comparer.
strlen renvoie un unsigned int et non un long, donc ta fonction doit prendre les mêmes types de paramètres.
Ta fonction deviendrait alors:
Code CPP :
bool testlongueur(char*nom,unsigned int mini,unsigned int  maxi)
{
    bool bresultat=false;
    unsigned int itaille=strlen(nom);
    if (itaille>=mini && itaille<=maxi) resultat=true;

    return bresultat;
}


Tu noteras que j'ai modifier légèrement la fonction, ce sont des choix purement personnels discutables.
List:
·Je prends toujours l'habitude de bien faire attention à ce que ma fonction n'ai qu'un seul return

·Lorsque c'est une fonction qui renvoie un booléen, j'ai l'habitude de l'initialiser à la valeur défavorable, puis de tester les cas favorables. Ainsi si j'oublie un test, la fonction renverra FALSE ce qui en général est moins grave que de renvoyer TRUE et ainsi de continuer un traitement sur un cas qui devrait être faux.




L'autre commentaire est sur la fonction testChaine.
Apparemment tu effectues deux vérification sur le nom du fichier :
List:
·sa taille

·les lettres autorisés.



Ce qui est dommage c'est que tu ne renvoies finalement à l'utilisateur qu'un type de message dans les 2 cas. Il serait intéressant que tu renvoies deux messages différents, un pour la taille, et un pour les caractères autorisés.
Pages: 1 2 3 4 5 6 7
URLs de référence