Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Non-Officiels > Programmation > Interface Graphique > Apprentissage de l'API Windows > Annexes > Divers > Lecture du tutoriel

Divers

Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Avatar
Auteur : Mg++
Visualisations : 4 943


Plus d'informations Plus d'informations
Sommaire du chapitre :
Icône du chapitre
Chapitre précédent Sommaire Chapitre suivant

Utiliser le style du Windows courant

Vous allez ici apprendre à donner le style de votre Windows actuel à votre application.

Téléchargement préalable



Pour cela, vous aurez besoin d'un fichier manifeste, se présentant sous la forme d'un fichier xml.

Commencez donc par télécharger celui-ci.
(Clic droit + Enregistrer sous...)

Si le téléchargement échoue, sachez qu'il s'agit seulement d'un fichier codé en ASCII (donc avec NotePad, par exemple) et renommé en .xml, contenant le code suivant :
Code : XML
1
2
3
4
5
6
7
8
9
<assembly manifestVersion="1.0">
    <assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="CompanyName.ProductName.YourApplication" type="win32"/>
    <description>Description de votre application ici.</description>
    <dependency>
        <dependentAssembly>
          <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*"/>
        </dependentAssembly>
    </dependency>
</assembly>


Association



Pour associer le manifeste à votre application, vous disposez de deux méthodes :

Dynamiquement



En utilisant cette méthode, l'exécutable utilisera le manifeste situé dans le même dossier que celui-ci.
Commencez par renommer votre manifeste avec le nom de votre exécutable comme radical, et avec ".manifest" comme extension. Par exemple, si votre exécutable s'appelle "Programme.exe", alors renommez "manifest.xml" en "Programme.exe.manifest".

Vous n'avez plus qu'à placer le manifeste renommé dans le répertoire contenant votre exécutable.

En utilisant les ressources



Vous avez peut-être envie de vous débarrasser de quelques fichiers entourant l'exécutable, en les intégrant aux ressources ? Comme le manifeste, par exemple ? :p
Bien. Alors tout se passe à la compilation. Rajoutez à votre script ressource (*.rc) le code d'intégration suivant :
Code : C
1
1 24 "manifest.xml"

Dans ce cas-ci, 1 est l'ID du manifeste, 24 son type, et "manifest.xml" son chemin.
Modifiez-le en conséquence s'il ne se trouve pas dans le répertoire des codes sources.

Attention, dans certains cas (je n'en connais pas la raison à ce jour), vous obtiendrez une fenêtre vide. Alors, la procédure à observer est la suivante :
  • Inclure <commctrl.h>.
  • Appeler InitCommonControls() au début du programme.
  • Linker le projet avec comctl32.lib ou libcomctl32.a (selon le compilateur utilisé).
Pour les feignants utilisant Code::Blocks, vous pouvez passer outre ces étapes, simplement en accédant à l'entrée Windows XP look'n'feel via le menu Plugin.


Vous n'avez plus qu'à contempler votre application disposer du style du Windows courant. ;)
Rédacteur : Mg++

La notation hongroise

La notation hongroise est une notation largement employée par Microsoft, qui consiste à faire apparaître, dans le nom de chaque variable, le type de celle-ci.

Les avis sont partagés à son sujet : à savoir si elle est utile ou non, productive ou non, mais toujours est-il qu'elle reste employée, et qu'on a tout intérêt à la comprendre.

Principe



Nous allons donc ici décrire les sigles qui reviennent le plus souvent.
En fait, elle consiste à préfixer en minuscules le nom de la variable (qui commence alors par une majuscule) par le sigle de son type.

Exemple:
Code : C
1
2
3
HANDLE hWnd;
INT iX;
CHAR cCode;


Les préfixes



Les préfixes utilisés sont:


Les modificateurs



Il existe aussi quelque modificateurs:


Exemples



Ainsi, en mélangeant les deux, on peut obtenir ceci par exemple :
Code : C
1
2
3
4
5
6
7
8
LPSTR g_szName;// variable globale qui est une chaîne de caractères terminée par zéro.

struct Truc
{
   PPOINT m_lpptPosition;// variable membre d'une structure, pointeur sur une structure de type point.
};

INT cbExtra;// nombre(le 'c') d'octets(le 'b') supplémentaires (à allouer par exemple)


Évidemment, il ne faut pas en abuser. Dans l'exemple précédent, cela alourdit inutilement l'expression ; on pourra omettre le "pt".

Rédacteur : Pamaury

Quelques codes...

Depuis la parution de mon tutoriel, j'ai répondu à diverses questions, parfois en fournissant un code fonctionnel. J'ai donc décidé de répertorier ici-même ces différents codes afin que vous puissiez en profiter ;)

Gestion du presse-papier



Le presse-papier revient souvent sur les forums, en particulier en ce qui concerne les futurs traitements de texte en WinAPI. C'est pour cela que j'ai créé deux petites fonctions toutes simples permettant sa gestion, GetClipboardText() et SetClipboardText(), qui conviendront aux moins exigeants, tout en inspirant les plus exigeants :) :

Code : C
 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include <windows.h>

/* goto peut être utilisé si la redirection est dans le sens direct de la lecture du code (on évite le code spaguetti), et que son utilisation est claire... Pour moi, ça l'est. */
#define REDIRECT_IF(condition, label) if(condition) goto label
 
#define EXIT_LABEL local_exit
#define EXIT_IF(condition) REDIRECT_IF(condition, EXIT_LABEL)
 
/* Quand "on colle" : */
char *GetClipboardText()
{
    char *text = NULL;
 
    EXIT_IF(!IsClipboardFormatAvailable(CF_TEXT));
 
    EXIT_IF(!OpenClipboard(NULL));
 
    {
        char *textMem = (char*)GetClipboardData(CF_TEXT);
 
        /* free devra être utilisé une fois le texte utilisé */
        text = malloc((lstrlen(textMem)+1) * sizeof *text);
        lstrcpy(text, textMem);
    }
 
    CloseClipboard();
 
    EXIT_LABEL:
        return text;
}

/* Quand "on copie" : */
int SetClipboardText(char const *text)
{
    int ret = 0;
 
    HANDLE hGlobalMem = GlobalAlloc(GHND, lstrlen(text)+1);
    char *lpGlobalMem = NULL;
 
    EXIT_IF(hGlobalMem == NULL);
 
    lpGlobalMem = (char*)GlobalLock(hGlobalMem);
 
    EXIT_IF(lpGlobalMem == NULL);
 
    lstrcpy(lpGlobalMem, text);
 
    GlobalUnlock(hGlobalMem);
 
    EXIT_IF(!OpenClipboard(NULL));
 
    EmptyClipboard();
 
    if(SetClipboardData(CF_TEXT, hGlobalMem) != NULL)
        ret = 1;
 
    CloseClipboard();
 
    EXIT_LABEL:
        return ret; /* 1 : Succeed, 0 : Failure */
}

En ce qui concerne la détection du copier-coller, mieux vaut se fier à des fonctions dédiées à ces opérations, en spécifiant sa fenêtre listener des opérations de copier-coller, grâce à SetClipboardViewer(). Ainsi, toute modification du presse papier se traduira par la réception du message WM_DRAWCLIPBOARD, qu'il faudra traiter.


Gestion du drag and drop



La question du drag-and-drop est aussi quelque peu récalcitrante, puisque l'API Windows ne fournit pas de système direct pour s'en charger. J'en ai jadis proposé un, qui se résume à une unique fonction GetDroppedFilesPaths() :

Citation : Moi
Un petit exemple censé écrire les chemins des fichiers lâchés sur la fenêtre dans un fichier (situé dans le même répertoire que l'exécutable) nommé "Result.txt" (pas compilable tel quel).

Code : C
 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
/* Callback procedure : Soit hwnd (HWND) et wParam (WPARAM), passés en paramètre à la procédure. */ 
case WM_CREATE:
    /* Ne pas oublier d'accepter les drag-and-drop ;) */
    DragAcceptFiles(hwnd, TRUE);
    return 0;

case WM_DROPFILES:
{
    CHAR **paths = NULL;

    UINT count = GetDroppedFilesPaths((HDROP)wParam, paths);
    
    WritePathsToFile(paths, count, "Result.txt");


    /* On n'oublie pas de libérer la mémoire allouée */
    {
        UINT i = 0;
        while(i < count)
            free(paths[i++]);

        free(paths);
    }

    /* Ne pas oublier de finir la procédure de drag */
    DragFinish((HDROP)wParam);

    return 0;
}

Avec :
Code : C
 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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/* Il aurait mieux fallu utiliser le système de gestion des fichiers de l'API, mais bon... */
#include <stdio.h>
#include <windows.h>

VOID WritePathsToFile(
                          CONST CHAR ** CONST paths, 
                          CONST UINT count, 
                          LPCSTR resPath
                      )
{
    FILE *file = NULL;
    file = fopen(resPath, "a");

    if(file != NULL)
    {
        for(UINT i = 0 ; i < count ; i++)
            fputs(paths[i], file);
        
        fclose(file);
    }
}

static UINT GetDroppedFilesCount(HDROP hDrop)
{
    return DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0);
}

static UINT GetDroppedFileLen(HDROP hDrop, CONST UINT index)
{
    return DragQueryFile(hDrop, index, NULL, 0);
}

UINT GetDroppedFilesPaths(HDROP hDrop, CHAR **paths)
{
    const UINT count = GetDroppedFilesCount(hDrop);

    paths = malloc(count * sizeof *paths);
    if(paths == NULL)
        return 0;

    for(UINT i = 0 ; i < count ; i++)
    {
        const UINT droppedFilePathLen = GetDroppedFileLen(hDrop, i);

        paths[i] = malloc(droppedFileLen * sizeof *paths[i]);
        if(paths[i] == NULL)
            return i;

        DragQueryFile(hDrop, i, paths[i], droppedFileLen);
    }
    return count;
}

Il faut bien évidemment autoriser la fenêtre au drag-and-drop via les styles de la fenêtre ou DragAcceptFiles()...

N'oubliez pas de libérer la mémoire allouée par les chemins des fichiers récupérés ! Gare aux fuites de mémoire dans le cas contraire...


Gestion d'une icône dans la barre des tâches



Voici deux fonctions toutes simples permettant de minimiser et de restaurer une application :
Code : C
 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
/* Cache ton application et place une icône dans la barre de tâches,
dont le handle est hIcon, szTip le message affiché dans une petite bulle (due au
survol prolongé de la souris), et msg le message callback, permettant de traiter
les événements */
NOTIFYICONDATA *Minimize(HWND handler, HICON hIcon, LPCTSTR szTip, UINT msg)
{
    NOTIFYICONDATA *nim = malloc(sizeof *nim);

    nid->cbSize = sizeof(NOTIFYICONDATA);
    nid->hWnd = handler;
    nid->uID = 0;
    nid->hIcon = hIcon;
    nid->uCallbackMessage = msg;
    nid->uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;

    strcpy(nid->szTip, szTip);

    Shell_NotifyIcon(NIM_ADD, nid);
    ShowWindow(handler, SW_HIDE);

    return nid;
}

void Restore(NOTIFYICONDATA *data)
{
    ShowWindow(data->hWnd, SW_SHOW);
    Shell_NotifyIcon(NIM_DELETE, data);
}

Et voilà un exemple d'utilisation :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/* Par exemple, la zone utilisateur étant située après WM_USER */
#define TI WM_USER+1

/* On minimise la fenêtre en récupérant une structure qui servira à la restaurer */
NOTIFYICONDATA *data = Minimize(hwnd, LoadIcon(hInstance, "Icon"), "Je suis une TrayIcon", TI);

/* ... */

/* On restaure la fenêtre */
Restore(data);
/* Et on n'oublie pas de libérer la mémoire... */
free(data);

/* ... */

/* Quant au switch du low-word du message : */
case TI:
    /* Traitement, par exemple avec TrackPopupMenuEx(), voir msdn */


Encore une fois, n'oubliez pas de libérer la mémoire occupée par l'instance de NOTIFYICONDATA nouvellement créée !


Une idée à fouiller lors de l'encapsulation



Un jour on m'a demandé si c'était possible de spécifier une méthode comme callback, et voici ce que je lui ai répondu :
Citation : Moi
Le fait est que... C'est impossible ^^ La callback doit être statique. Edit: Question de signature... Ca paraît logique avec la structure WNDCLASSEX

Tu dois donc contourner le problème. Voici ce que je te propose : Utilise SetWindowLongPtr() après création de ta fenêtre pour passer l'objet courant (obtenu à travers la structure CREATESTRUCT passée par le LPARAM de la callback statique) en tant que pointeur récupérable grâce à GetWindowLong() (par exemple, à l'emplacement GWLP_USERDATA) dans la callback statique qui se chargera alors de rediriger les événements perçus vers les callbacks virtuelles correspondantes.

Fouille cette idée, si tu as du courage :
Code : C++
 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
38
39
40
41
42
43
class Wnd
{
    HWND m_handle;
 
    //...
 
    static LRESULT StaticProc(HWND, UINT, WPARAM, LPARAM);
    static LRESULT DefProc(HWND, UINT, WPARAM, LPARAM);
 
    //...
 
    virtual LRESULT virtualProc(UINT, WPARAM, LPARAM); //Pure ou pas
 
    //...
};
 
class MyWnd : public Wnd
{
    //...
 
    virtual LRESULT virtualProc(UINT, WPARAM, LPARAM); //Override
};
 
LRESULT Wnd::StaticProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{    
    LRESULT ret = 0;    
    
    //Si la fenetre est en creation, le pointeur n'a pas ete encore initialise
    if(uMsg == WM_NCCREATE)
    {
        /* SetWindowLongPtr() de 'this' contenu dans lParam grâce au dernier paramètre
           de CreateWindowEx() -> Voir CREATESTRUCT structure */
        SetObjectFromCreateStruct(hWnd, lParam); //A toi de la créer...
    }
 
    Wnd *pObj = reinterpret_cast<Wnd*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));   
    if(pObj != NULL)
        ret = pObj->virtualProc(uMsg, wParam, lParam); 
    else       
        ret = DefProc( hWnd, uMsg, wParam, lParam);
 
    return ret;
}

Cela reste primitif, mais c'est une solution à envisager lorsqu'on est nous-même Orienté Objet ( ^^ ) et que l'on est réticent à utiliser MFC. Cela permet en outre de bien contrôler les rouages d'une API ainsi simplifiée.


Il me semblait en avoir d'autres en réserve, mais je vous avoue que j'ai un peu la flegme de me plonger dans mon courrier dans une chasse aux codes :-°
D'autres surgiront peut-être ?

Rédacteur : Mg++

Chapitre précédent Sommaire Chapitre suivant
Retour en haut Retour en haut


Créé : le 16/08/2006 à 11:53:13
Modifié : le 22/08/2008 à 15:53:16
Avancement : 0%
Licence : Copie non autorisée

2 commentaires

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | RSS tutoriels | RSS news
Édité par Simple IT SARL : Nous contacter | Notre blog | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 490 Zéros connectés | Requêtes SQL 8 requêtes | Temps de génération de la page : Total (SQL) 0.0275s (0.0133s)