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.
Bonsoir, voilà je vous propose le carnet d'adresses que j'ai fait avec la bibliothèque Win API, en C.
Vous pouvez télécharger le logiciel ici puis le tester. Vos critiques constructives seront les bienvenues, ainsi que les idées sympas.
En ce moment je travaille sur un autre projet, alors je ne sais pas si je passerai encore bcp de temps sur ce carnet d'adresses, mais pourquoi pas qq petites modifications.
Pour ouvrir le carnet d'adresse il faut décompresser le fichier, cliquer sur carnet.exe puis ouvrir le carnet d'adresses carnet.adr (ou pas).

Je mettrai en ligne(si j'ai le temps) le code mais aussi mon organigramme pour montrer é ceux qui ne connaissent pas win Api comment j'ai procédé. Il faudra rester critique parce que je ne fais éa qu'en amateur et je ne programme pas tjrs dans les régles de l'art...exemple pour l'allocation de mémoire, entre autre ! Smile

Voici déjé le plan de mon programme avec les fonctions que j'ai créées : (les fonctions en rouges sont traitées ci-dessous)

A.Fonction principale
A1 Charger les dimensions de la fenétre
A1a. Charger des paramétres de configuration
A1a1. Enregistrer un fichier carnet.config par défaut
A2 MENU

B. PROCEDURE PRINCIPALE
B1. Création de fenétre fille selon les états des témoins
B1a Création de la couverture
B1a1. DESTRUCTION DES FENéTRES FILLES EXISTANTES
B1a2. CRéER UNE FENéTRE FILLE
B2. GESTION DES NOTIFICATIONS DU MENU
B2b. Charger un nouveau répertoire
B2b1 Ouvrir un fichier
B2c. Ouvrir une nouvelle Fiche
B2c1. Modifier l'état des témoins
B2c2. Mise é jour du menu
B2d. Sortir de la Fiche et revenir é la couverture
B3. Définir l'état des paramétres de la Structure TEMOINS
B4. Fermer l'application
B4a Supprimer le fichier temporaire

C.PROCEDURE DE LA FENETRE COUVERTURE
C1. CRéER LES ONGLETS
C2. Réponse au notifications des onglets depuis la couverture
C3 Dessiner la couverture
C3a Dessiner /reliure/plat/sous boutons
C3b Dessiner l'étiquette

D.PROCEDURE DE LA FENETRE FICHE
D1 . Création des boutons de la fiche
D2 . Création des contréles d'édition et des combo-box de le fiche
D2a Céer les Comb-Box de la fiche
D3. Dessiner la fiche
D3a Dessiner le contenu de la fiche
D3a1. Dessiner la photo de la fiche

D3a11 Lire le fichier temporaire photo
D3a12 Dessiner une image
D4. Titre de la fiche
D5 Réponses aux notifications des boutons de la fiche
D5a.Remplacer la photo de la fiche
D5a1 Charger la photo
D5a2. Ecrire l'adresse de l'image dans un fichier temporaire
D5b Enregistrer la nouvelle fiche
D5b1 Remplir la structure FICHEPERSONNE
D5b2 Enregistrer la structure dans le fichier du carnet
D5b21 Ouvrir le carnet d'adresses et compter les entrées
D5b3 Vider la fiche

E.PROCEDURE DE LA FENETRE LISTE
E1 Créer les boutons de la fenétre liste
E2 Dessiner le fenétre Liste
E2a Dessiner le fond de la liste (rayures)
E2b Ecrire l'intiale de la lettre en haut de la fenétre Liste
E2b1 Remplir le tableau des lettres
E2c Charger les fiches selon l'initiale et les compter.
E2d Classer les fiches dans l'ordre alphabétique
E3. Réponse au notifications des onglets depuis la fenétre Liste
E4. Réponses aux notifications des boutons de la fenétre Liste
E4b Supprimer la fiche

F. Procédure de boéte de dialogue (création d'un nouveau carnet)
F1. Créer un nouveau fichier d'adresses
F1a. Tester le nom du nouveau carnet
F1a1. Tester la longueur d'une chaéne
F1a2. Tester les lettres permises
F1b. Créer le nouveau carnet et enregistrer dans la config
F1b1. Créer le fichier
F1b2 Remplacer le nom du carnet dans carnet.config
G. Procédure de boéte de dialogue é propos
H.Procédure de Property Sheet de la fnétre de configuration
H1 Placer les données de configuration nécessaires dans une structure
H2. GESTION DES MESSAGES de la procédure Property Sheet
H2a Enregistrer un fichier carnet.config par défaut

I et J Procédures des fenétres des Onglets 1 et 2
I1 Messages de la fenétre Onglet 1
I1a Changer la couleur
I2 Messages de la fenétre Onglet 2
I3 Dessiner la fenétre de l'onglet 1
I3a Dessiner un rectangle simple
I3b Ecrire un texte
I4 Dessiner la fenétre de l'onglet 2


K. Procédure de la fiche é visualiser
Voici l'organigramme de ma fonction principale suivit du code pour cette fonction :
- les cases rouges sont les points d'entrée et sortie de la fonction
- les cases noires signifient qu'une ou plusieurs actions simples sont effectuées dans la fonction(un calcul, un appel de fonction Win Api, etc..)
- les cases jaunes sont des conditions
- les cases verte sont des appels de fonctions propres au programme.




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"

 hinstance hinstance;

int winapi winmain(hinstance hinstance, hinstance hprevinstance,lpstr lpcmdline, int ncmdshow)
{
static long largeur=0,hauteur=0;
static hmenu hmenu[4]={0};
static msg msg;
static wndclassex wc;
static hwnd hwnd;


dimfenetre(&largeur,&hauteur);
lemenu (hmenu);

//creer la fenetre principale
           // structure de la classe de la fenãªtre principale
        wc.cbsize =sizeof(wndclassex);
        wc.style =0;
    wc.lpfnwndproc = procedure;
    wc.cbclsextra = 0;
    wc.cbwndextra = 0;
    wc.hinstance = hinstance;
    wc.hicon = loadicon(hinstance,"icone1");
    wc.hcursor = loadcursor(null,idc_arrow);
    wc.hbrbackground = createsolidbrush(rgb(255,255,255));
    wc.lpszmenunamenull;
    wc.lpszclassname = (lptstr )"class";
    wc.hiconsm=null;

   //enregistrer la classe de fenãªtre
    if(!registerclassex(&wc)) return false;

   //creer la fenãªtre
    hwnd = createwindowex(0,(lptstr )"class", (lptstr )"carnet d'adresses "
            ,ws_border |ws_caption|ws_minimizebox |ws_sysmenu |ws_clipchildren ,
                              300,100,largeur,hauteur+50,null,hmenu[3], hinstance, null);
    if (!hwnd)  return false;

    showwindow (hwnd, sw_show);
    updatewindow( hwnd );

        //boucle de message
    while (getmessage(&msg, null, 0, 0))
    {
        translatemessage(&msg);
        dispatchmessage(&msg);
    }
    return (int) msg.wparam;

}


 


Avec le langage C, on créer une fenêtre et tout que qui se passe autour de la fenêtre envoie des messages à l'application.
La boucle des messages intercepte les messages, les traduit et les envoie aux procédures des fenêtres concernées (il peut y avoir plusieurs fenêtres dans une application). La procédure de la fenêtre est la fonction dans laquelle on va capter les messages envoyés à l'application et surtout les traiter, c'est à dire, donner des directives à l'application pour répondre à tel ou tel message.

Exemple de message : WM_CREATE --> message envoyé à la procédure quand la fenêtre est créée. On pourra demander à l'application de créer un "champs d'édition " = contrôle d'édition lorsqu'elle recevrace message .

Nous verrons cela plus tard dans l'application. Pour l'instant ce qu'il faut retenir c'est que dans la fonction principale WinMain:
- on remplit la structure de la classe de la fenêtre :WNDCLASS ou WNDCLASSEX
- on enregistre la classe de la fenêtre avec RegisterClassEx ou RegisterClass (certaines classes de fenêtre sont déjà toute prètes comme les boutons, les list-box etc...)
- on créer la fenêtre avec CreatWindowEx ou CreatWindow
- on définit la boucle des messages comme ci-dessus.
Allez une petite fonction pour ce week-end, comme précédamment je mets l'organigramme puis le code:



Code C :

/******************************************************************************** ****************************/
void dimfenetre (long *largeur,long* hauteur)
{
    static char larg[5]={0},haut[5]={0};

    chargerconfig ((lpvoid)&larg,0,sizeof(char)*5);
    chargerconfig ((lpvoid)&haut,sizeof(char)*5,sizeof(char)*5);
    *largeur=strtol(larg,null,10);
    *hauteur=strtol(haut,null,10);
}

 


Voilà, c'est très court, mais je vais m'en servir souvant dans la suite du programme Smile

Je passe les pointeurs des deux dimensions comme ça je récupère les valeurs dans main.c. Si on passe les variables
pour récupérer la valeurs il faut faire un return (valeur), et on ne peut renvoyer qu'une seule valeur. Donc en C
on utilise bcp les pointeurs pour passer les nouvells valeurs des variables d'une fonction à une autre fonction.

ChargerConfig est une fonction qui va chercher les infos nécessaires dans un fichier config pour
configurer l'application. C'est la prochaine fonction que je mettrai ici.

La suite au prochain numéro !
... eh ben.
Je suis impressionné par ton organisation. C'est mon métier de faire des logiciels et je dois bien avouer que je prends rarement le temps d'écrire ce genre de description.

J'ai un peu essayé ton programme et c'est plutôt pas mal du tout. D'ailleurs je n'ai pas grand chose à dire^^. Je l'ai fais planter une fois, je ne serais pas te dire comment. (je crois en créant une fiche sans ouvrir de fichier avant et en ne remplissant pas tout les champs).

Comme tu postes une fonction je voulais juste te faire un petit commentaire.
Aussi bien ta fonction DimFenetre, que ChargerConfig ne renvoient rien. Ton argument de modifier les valeurs passées en paramètres est très bien, cependant il est préférable de renvoyer un code erreur.
Car là si ta fonction ChargerConfig échoue, non seulement tu fais quand même le traitement suivant, mais la fonction DimFenetre n'est pas capable d'indiquer le problème à la fonction appellante. Au final c'est ce genre de chose qui font que ton programme peut planter à un endroit et qu tu vas chercher pendant des jours et des jours le problème alors que l'erreur c'est situé complètement ailleurs ;D

Heu tiens si une autre question, pourquoi déclares tu les variables larg et haut static?
:o
pour quelqu'un dont l'informatique n'est pas le boulot, je trouve que tu es vachement organisée Wink
je manquerai pas de tester
Bonjour !

Alors merci pour les compliments (smiley timide )... Smile

Déjà pour l'organisation.. j'ai bien été obligée de m'organiser parce que je n'arrivais jamais à rien. Rolleyes
Avec mon organigramme maison, j'ai enfin dissocié les problèmes. Quand j'écris l'organigramme je ne pense qu'à la
succession des taches à accomplir et pas comment le faire. Ensuite quand j'ai l'organigramme, c'est bcp plus facile et
économique pour coder ! Mais je ne pense pas tjrs à tout non plus ! ;D

Ensuite pour le renvoie du code erreur... je sais, qq fois je le fais. ça c'est un truc que je ne capte pas trop bien, ça doit être dû au côté amateur... Wink Si on renvoie un code d'erreur ça n'empêche pas le programme de planter. On doit seulement le recevoir pour dire à l'application de faire autre chose c'est ça ?? Si je ne trouve pas de fichier config... alors l'application plante de toute façon si je ne le prévois pas ? On peut le prévoir sans envoyer de code d'erreur alors... non ? C'est une question que je me suis tjrs posée, c'est pas très clair pour moi.

Sinon pour le static.. c'est un tic . Comme dans Win Api le Static résoud presque tous les problèmes Wink quelque fois j'en abuse, effectivement. Encore une approximation béotienne qui ne demande qu'à être corrigée et clarifiée.

Bon allez je vais mettre ma petite fonction ChargerConfig : (organigramme + code)
Ce qui est hachuré n'a pas été fait, je me suis rendu compte que c'était inutile en codant.

Code C :

void chargerconfig (lpvoid buffer, long place, dword taille)
{
static handle hfichier=null;
static dword nbcharread ;
static char dossier[max_path]={0};

//crã©er l'adresse du fichier de configuration.
getcurrentdirectory(max_path,(lptstr)dossier); //trouver le rã©pertoire courant
strncat(dossier,"\carnet.config",max_path);        //ajouter le nom du fichier de configuration

// ouvrir le fichier carnet.config
hfichier =createfile(dossier, generic_write|generic_read, 0,null,
                                            open_existing, file_attribute_normal, null);
if (hfichier == invalid_handle_value)
{
    //enregistrer le fichier avec les paramã¨tres par dã©faut
     creerfichierconfig (&hfichier);
}

  setfilepointer(hfichier,place,0,file_begin);
  readfile(hfichier, buffer,taille, &nbcharread, null) ;

  closehandle(hfichier);

}

 

Citation :
Sinon pour le static.. c'est un tic . Comme dans Win Api le Static résoud presque tous les problèmes

Etrange ...
Le code principal de ton programme est la fonction Procedure, c'est elle qui reçoit tout les évènements de ton programme (clic, rafraichissement, agrandissement, ...).
Cette fonction est donc appelée à chaque évènement. La plupart du temps, les variables déclarées dans cette fonction le sont en static. En effet, comme pour chaque évènement cette fonction est appelé puis se termine, la meilleur façon de conserver la contenu de la variable entre chaque appel est de la déclaré static (on pourrait également déclaré une variable globale, mais cette solution est à éviter au maximum afin d'éviter d'avoir 250 variables globales en début de fichier.)
La variable static a donc l'avantage de continuer à exister après la fin de la fonction.

En revanche, dans une fonction classique dont les variables n'ont pas à être retenues entre chaque appel, les déclarer en static ne sert à pas grand chose ... sauf des erreurs du style

Code CPP :

int sertarien(char mot1[10], char mot2[10])
{
    static int i;//au premier appel, i sera initialise a 0
    int j;//initialise a 0 à chaque appel puisque local à la fonction et non static
    for(j=0;j<10;j++){
        if(mot1[j]!=mot2[j]) i++;
    }
    return i;
}


cette fonction ne sert à rien Smile, elle renvoie simplement le nombre de lettre différente sur 2 mots (je ne fais aucun contrôle c'est simplement un exemple).
mais regardons ce qu'il se passe lorsque l'on utilise cette fonction.

Code CPP :

int main()
{
        int res;
        char mot1[10]="qsdfghjjk";
        char mot2[10]="azertyuio";
        res=sertarien(mot1,mot2);
        char mot3[10]="mot1de10c";
        char mot4[10]="mot2de10c";
        res=sertarien(mot3,mot4);
        return 0;
}
 


mot1 et mot2 sont complètement différent. La fonction renvoie donc 9.
mot3 et mot4 n'ont qu'une seule lettre de différente, elle devrait renvoyer 1 ... mais elle renvoie 10, car i étant static, au deuxième passage elle vaut déjà 9.

Je sais c'est un exemple stupide mais souvent des problèmes subtiles de ce genre peuvent intervenir et c'est souvent un casse tête pour débuguer derrière.

Pour les codes erreurs des fonctions, il faut que tout le long de ton programme tu détermines toi même les erreurs qui seront dites critiques, et qui devront terminer ton programme en avertissant l'utilisateur, et les erreurs qui pourront être traités par ton programme.
Imaginons que ton programme laisse la liberté à l'utilisateur de créer un nouveau fichier dans le répertoire qu'il souhaite. Si jamais il choisit un répertoire dans lequel il n'a pas les droits suffisants pour créer ce fichier, ta fonction CreateFile va échouer. Que fais tu alors? Pour le moment ton programme va planter (tôt ou tard).
Tu pourrais donc renvoyer un message à l'utilisateur pour qu'ils choisissent un autre répertoire.
Si l'on prend ta fonction par exemple, que se passe t-il si le fichier existe mais qu'il est bloqué par un autre processus? Et bien toutes tes fonctions de lecture du fichier vont planter, et taille ne vaudra pas grand chose d'intéressant.
Donc ici tu pourrais tester le retour de ta fonction creerFichierConfig. Si la fonction a échoué, soit tu définis que tant pis, ta fenêtre aura une taille par défaut (ce qui ne t'empêche pas de prévenir l'utilisateur que le fichier config est inaccessible et que ces préférences n'ont pas été chargés), soit que les paramètres sont critiques et dans ce cas tu avertis l'utilisateur que le programme va être fermé car le fichier n'est pas accessible.


Désolé d'avoir fait si long ...
J'ai conscience que le programme peut planter, mais les erreurs possibles sont tellement nombreuses que quelques fois je me dis, ben ça plantera. Et il y a aussi des erreurs que je ne suis pas capable de detecter ou de corriger. Par exemple je ne suis pas sûre de savoir définir si un fichier est déjà ouvert.. en y pensant là tout de suite j'aurais bien une petite idée, mais je ne l'ai jamais fait. Quel boulot ! :-

O peut faire un listing des zones d'ombre.
De toute façon, si un jour j'ai l'idée du siècle pour faire un programme, il me faudra les compétences d'un vrai programmeur pour terminer et parfaire mon travail !

C'est sympa d'apprendre comme ça. Et je crois que j'ai un peu mieux pris conscience qu'il faut mieux fermer les portes dans mes programmes.

Ton intervention n'est pas trop longue, c'est même super interessant ! Merci Smile Si ça ne te dérange pas, j'aimerais que tu me donnes encore des conseils. Je ne promets pas de corriger mon programme mais ça me sera toujours utile pour mes autres projets. En plus tu complètes bien ma petite présentation.

C'est un peu comme ce que tu expliquais dans un autre topic. Si on est là c'est qu'on aime programmer, on prend et donne ce qu'on veut quand on veut et quand on peut... Smile
Bon, je vais un peu partir en HS, mais pas tellement.
Il est vrai que la définition des variables, et surtout de leur portée, est très important lors de l'élaboration d'un script/programme.
Malheureusement, chaque langage a sa propre définition, et ça peut aussi varier d'un langage à l'autre. Le tout est surtout de bien intégrer le principe et de le comprendre de manière à pouvoir s'adapter et surtout éviter les effets de bords.

Combien de fois ai-je vu des soucis parce qu'une variable était déclarée en global et qu'une fonction utilisait une variable du même nom?
Oui tu as raison, c'est pour ça que j'évite les globales au maximum, Je n'en ai qu'une dans tout le programme, c'est
hinstance.

Allez vite vite une petite fonction : creerFichierConfig....vous me dites stop quand vous voulez .



Code C :

void creerfichierconfig (handle *hfichier)
{
static char dossier[max_path]={0};
static dword nbcharwrite ;
  // valeurs par dã©faut des paramã¨tres de configuration
static char largeur[5]="0450", hauteur[5]="0500";
static colorref  couleurreliure = rgb(38,97,221), couleurcouverture = rgb(123,152,225);
static colorref  couleurpapier1 = rgb(255,255,255), couleurpapier2 = rgb(236,233,216);
static colorref  couleurtexte1 = rgb(0,0,0), couleurtexte2 = rgb(0,0,0);
static char carnet[max_path]={0};
getcurrentdirectory(max_path,(lptstr)carnet);
strncat(carnet,"\carnets\carnet.adr",max_path);

getcurrentdirectory(max_path,(lptstr)dossier);
strncat(dossier,"\carnet.config",max_path);
*hfichier = createfile( dossier, generic_write|generic_read, 0,null,
                                           create_always, file_attribute_normal, null);

setfilepointer(hfichier,0,0,file_begin);
writefile(*hfichier, (lptstr)& largeur,sizeof(char)*5, &nbcharwrite, null);
writefile(*hfichier, (lptstr)& hauteur,sizeof(char)*5, &nbcharwrite, null);
writefile(*hfichier, (lptstr)& couleurreliure,sizeof(colorref), &nbcharwrite, null);
writefile(*hfichier, (lptstr)& couleurcouverture,sizeof(colorref), &nbcharwrite, null);
writefile(*hfichier, (lptstr)& couleurpapier1,sizeof(colorref), &nbcharwrite, null);
writefile(*hfichier, (lptstr)& couleurpapier2,sizeof(colorref), &nbcharwrite, null);
writefile(*hfichier, (lptstr)& couleurtexte1,sizeof(colorref), &nbcharwrite, null);
writefile(*hfichier, (lptstr)& couleurtexte2,sizeof(colorref), &nbcharwrite, null);
writefile(*hfichier, (lptstr)& carnet,sizeof(char)*(max_path), &nbcharwrite, null);
}

 


Voilà . Commentaires plus tard si necessaire. Là je n'ai pas le temps ! Smile
Pages: 1 2 3 4 5 6 7
URLs de référence