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)
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 : XML1
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, donc pendant l'exécution
- en utilisant les ressources, ceci pour l'intégrer à l'exécutable.
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 ?
Bien. Alors tout se passe à la compilation. Rajoutez à votre script ressource (*.rc) le code d'intégration suivant :
Code : C
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 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 : C1
2
3 | HANDLE hWnd;
INT iX;
CHAR cCode;
|
Les préfixes
Les préfixes utilisés sont:
- i : entier (int ou INT en général)
- n : entier court (short int ou SHORT)
- l : entier long (long int ou LONG)
- f : nombre à virgule flottante simple précision (float ou FLOAT)
- d : nombre à virgule flottante double précision (double ou DOUBLE)
- c : caractère (char ou CHAR) ou un dénombrement
- ch : caractère (char ou CHAR)
- b : booléen (bool ou BOOL)
- s : chaîne de caractères (CHAR *, PCHAR, LPSTR, LPCSTR, ...)
- sz : chaîne de caractères terminée par un zéro (CHAR *, PCHAR, LPSTR, LPCSTR, ...)
- w : mot ou "word" en anglais (WORD)
- dw : double-mot ou "double word" en anglais (DWORD)
- h : handle (HANDLE, HWND, HFILE, HBITMAP, ...)
- pt : point (POINT)
- rgb : couleur (RGB) [rarement utilisée]
- str : encore une autre façon de préciser une chaîne de caractères.
Les modificateurs
Il existe aussi quelque modificateurs:
- u : non-signé. Exemple : ul pour ULONG
- lp, p ou p_ : pointeur sur quelque chose. Exemple : lpstr ou psz...
- a ou ar : tableau (ou "array" en anglais) [rarement utilisé]
- m_ : membre d'une classe (C++) ou d'une structure
- g_ : variable globale
- s_ : variable statique (correspondant au mot-clé static en C/C++).
Exemples
Ainsi, en mélangeant les deux, on peut obtenir ceci par exemple :
Code : C1
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
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 */
}
|
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 : MoiUn 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 : MoiLe 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++