|
[Win API] Un carnet d'adresses
|
| Auteur |
Message |
Toam
Junior Member
 
Messages : 36
Groupe : Membres
Inscription : Nov 2006
Statut :
Hors ligne
Réputation : 0
|
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.
bool creerfonctiontemoins(hwnd hwnd,hwnd *hwndfille, temoins *temoins, wparam wparam);
ou
bool creerfonctiontemoins(hwnd hwnd,hwnd *hwndfille, temoins &temoins, wparam wparam);
en effet ta structure temoins est :
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).
struct temoins
{
bool couv;
bool liste;
bool fiche;
};
Je chipote pour pas grand chose, mais c'est une bonne habitude à prendre
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^^ ...
|
|
| 15/12/2006 10:34 |
|
 |
acryline
Member
  
Messages : 68
Groupe : Membres
Inscription : Dec 2006
Statut :
Hors ligne
Réputation : 2
|
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 !
...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

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.
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.
|
|
| 16/12/2006 06:51 |
|
 |
acryline
Member
  
Messages : 68
Groupe : Membres
Inscription : Dec 2006
Statut :
Hors ligne
Réputation : 2
|
Bonjour !
Voici la fonction du jour creerFenetreFille

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.lpszmenuname = null;
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.
|
|
| 17/12/2006 07:21 |
|
 |
acryline
Member
  
Messages : 68
Groupe : Membres
Inscription : Dec 2006
Statut :
Hors ligne
Réputation : 2
|
Bonjour,
il n'y avait pas foule ce week end ! 
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 :
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 !  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:
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.
|
|
| 18/12/2006 07:09 |
|
 |
Toam
Junior Member
 
Messages : 36
Groupe : Membres
Inscription : Nov 2006
Statut :
Hors ligne
Réputation : 0
|
:o heu trop de chose là ... je vais juste revenir sur ces points
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
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
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.
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.
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:
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;
}
}
}
|
|
| 18/12/2006 23:47 |
|
 |
acryline
Member
  
Messages : 68
Groupe : Membres
Inscription : Dec 2006
Statut :
Hors ligne
Réputation : 2
|
Bonjour et merci pour tes explications Toam 
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 au passage j'ai aussi ajouté l'icône utilisée pour créer la fenêtre parent).
#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 :
//ressource
#define ide_edit1 101
#define id_ok 102
on doit ã©crire la procã©dure de cette fenãªtre:
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..).
|
|
| 19/12/2006 07:19 |
|
 |
Toam
Junior Member
 
Messages : 36
Groupe : Membres
Inscription : Nov 2006
Statut :
Hors ligne
Réputation : 0
|
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).
|
|
| 19/12/2006 09:24 |
|
 |
acryline
Member
  
Messages : 68
Groupe : Membres
Inscription : Dec 2006
Statut :
Hors ligne
Réputation : 2
|
Je suis toute confusionnée 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. 
Allez, pour aujourd'hui une petite fonction, (celle de la procédure de la boite de dialogue vue ci-dessus) : nouveauFichierAdresse

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);
}
}
|
|
| 20/12/2006 07:37 |
|
 |
acryline
Member
  
Messages : 68
Groupe : Membres
Inscription : Dec 2006
Statut :
Hors ligne
Réputation : 2
|
Bonjour,
aujourd'hui je vous présente deux petites fonctions. testChaine et testLongueur

bool testchaine(char*nom)
{
long mini=0, maxi=256;
if(! testlongueur(nom,mini,maxi)) return false;
if(! testcaracteres(nom)) return false;
return true;
}
bool testlongueur(char*nom,long mini, long maxi)
{
if (strlen(nom)==0 || strlen(nom)>256)
{
return false;
}
else
return true;
}
|
|
| 21/12/2006 05:24 |
|
 |
Toam
Junior Member
 
Messages : 36
Groupe : Membres
Inscription : Nov 2006
Statut :
Hors ligne
Réputation : 0
|
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 .
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:
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.
|
|
| 21/12/2006 09:20 |
|
 |
|
|