Win32 API
Faire face aux fenêtres
Recherche…
Créer une fenêtre
#define UNICODE
#define _UNICODE
#include <windows.h>
#include <tchar.h>
const TCHAR CLSNAME[] = TEXT("helloworldWClass");
LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp);
int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, PTSTR cmdline,
int cmdshow)
{
WNDCLASSEX wc = { };
MSG msg;
HWND hwnd;
wc.cbSize = sizeof (wc);
wc.style = 0;
wc.lpfnWndProc = winproc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;
wc.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor (NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = CLSNAME;
wc.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
if (!RegisterClassEx(&wc)) {
MessageBox(NULL, TEXT("Could not register window class"),
NULL, MB_ICONERROR);
return 0;
}
hwnd = CreateWindowEx(WS_EX_LEFT,
CLSNAME,
NULL,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInst,
NULL);
if (!hwnd) {
MessageBox(NULL, TEXT("Could not create window"), NULL, MB_ICONERROR);
return 0;
}
ShowWindow(hwnd, cmdshow);
UpdateWindow(hwnd);
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK winproc(HWND hwnd, UINT wm, WPARAM wp, LPARAM lp)
{
return DefWindowProc(hwnd, wm, wp, lp);
}
La première chose que l'on voit sont les deux définitions de macro, UNICODE
et _UNICODE
. Ces macros font que notre programme comprend des chaînes de caractères larges ( wchar_t[n]
), pas des chaînes étroites ( char[n]
). Par conséquent, tous les littéraux de chaîne doivent être encapsulés dans une macro TEXT(
type de caractère générique pour les chaînes Win32 est TCHAR
, dont la définition dépend du fait que UNICODE
soit défini ou non. Un nouvel en-tête est inclus: <tchar.h>
contient le déclaration de TCHAR
.
Une fenêtre se compose de ce qu'on appelle une classe de fenêtre . Cela décrit des informations sur une fenêtre à partager entre ses instances, comme l'icône, le curseur et d'autres. Une classe de fenêtre est identifiée par un nom de classe de fenêtre, qui est donné dans la variable globale CLSNAME
dans cet exemple. Le premier acte de WinMain
est de remplir la structure de classe de fenêtre WNDCLASSEX wc
. Les membres sont:
- cbSize: La taille, en octets, de la structure
- style: les styles de classe de fenêtre. Ceci est 0 pour le moment.
- lpfnWndProc: C'est l'un des champs les plus importants. Il stocke l'adresse de la procédure de fenêtre . La procédure de fenêtre est une fonction qui gère les événements pour toutes les fenêtres qui sont des instances de cette classe de fenêtre.
- cbClsExtra: Le nombre d'octets supplémentaires à allouer pour la classe de fenêtre. Pour la plupart des situations, ce membre est 0.
- cbWndExtra: Le nombre d'octets supplémentaires à allouer pour chaque fenêtre individuelle. Ne confondez pas cela avec
cbClsExtra
, qui est commun à toutes les instances. C'est souvent 0. - hInstance: Le handle d'instance. Attribuez simplement l'argument
hInst
dansWinMain
à ce champ. - hIcon: Le descripteur d'icône pour la classe de fenêtre.
LoadIcon(NULL, IDI_APPLICATION)
charge l'icône de l'application par défaut. - hCursor: Le handle de curseur pour la classe de fenêtre.
LoadCursor(NULL, IDC_ARROW)
charge le curseur par défaut. - hbrBackground: Un handle pour le pinceau d'arrière-plan.
GetStockObject (WHITE_BRUSH)
donne un handle à un pinceau blanc. La valeur de retour doit êtreGetStockObject
carGetStockObject
renvoie un objet générique. - lpszMenuName: Le nom de ressource de la barre de menus à utiliser. Si aucune barre de menus n'est nécessaire, ce champ peut être NULL.
- lpszClassName: Le nom de classe qui identifie cette structure de classe de fenêtre. Dans cet exemple, la variable globale
CLSNAME
stocke le nom de la classe de fenêtre. - hIconSm: Un handle pour l'icône de petite classe.
Une fois cette structure initialisée, la fonction RegisterClassEx
est appelée. Cela provoque l'enregistrement de la classe de fenêtre avec Windows, ce qui la fait connaître à l'application. Il retourne 0 en cas d'échec.
Maintenant que la classe de fenêtre a été enregistrée, nous pouvons afficher la fenêtre en utilisant CreateWindowEx
. Les arguments sont:
- stylesex: les styles de fenêtres étendus. La valeur par défaut est WS_EX_LEFT.
- clsname: le nom de la classe
- cap: le titre de la fenêtre ou la légende. Dans ce cas, c'est la légende qui est affichée dans la barre de titre d'une fenêtre.
- styles: les styles de fenêtre. Si vous souhaitez créer une fenêtre de niveau supérieur (parent) comme celle-ci, l'indicateur à transmettre est WS_OVERLAPPEDWINDOW.
- x: Coordonnée x du coin supérieur gauche de la fenêtre.
- y: coordonnée y du coin supérieur gauche de la fenêtre
- cx: La largeur de la fenêtre
- cy: La hauteur de la fenêtre
- hwndParent: le handle de la fenêtre parente. Comme cette fenêtre est en soi une fenêtre parente, cet argument est NULL.
- hMenuOrID: Si la fenêtre en cours de création est une fenêtre parente, cet argument est un handle vers le menu de la fenêtre. Ne confondez pas cela avec le menu de la classe, qui est
WNDCLASSEX::lpszClassName
. Le menu de classe est commun à toutes les instances de fenêtres portant le même nom de classe. Cet argument, cependant, est spécifique à cette instance uniquement. Si la fenêtre en cours de création est une fenêtre enfant, il s'agit de l'ID de la fenêtre enfant. Dans ce cas, nous créons une fenêtre parente sans menu, donc NULL est passé. - hInst: le handle de l'instance de l'application.
- etc: les informations supplémentaires transmises à la fenêtre de la fenêtre. Si aucune information supplémentaire ne doit être transmise, transmettez NULL.
Si x
ou y
ou cx
ou cy
est CW_USEDEFAULT
, alors la valeur de cet argument sera déterminée par Windows. C'est ce qui se fait dans cet exemple.
CreateWindowEx
renvoie le descripteur à la fenêtre nouvellement créée. Si la création de la fenêtre échouait, il NULL
.
Nous montrons ensuite la fenêtre en appelant ShowWindow
. Le premier argument de cette fonction est le handle de la fenêtre. Le second argument est le style d'affichage, qui indique comment la fenêtre doit être affichée. La plupart des applications transmettent simplement l'argument cmdshow
transmis dans WinMain
. Une fois la fenêtre affichée, elle doit être mise à jour par un appel à UpdateWindow
. Un message de mise à jour est envoyé à la fenêtre. Nous allons apprendre ce que cela signifie dans un autre tutoriel.
Vient maintenant le coeur de l'application: la pompe à messages. Il pompe les messages envoyés à cette application par le système d'exploitation et envoie les messages à la procédure de fenêtre. L'appel GetMessage
renvoie une valeur différente de zéro jusqu'à ce que l'application reçoive des messages provoquant sa fermeture, auquel cas elle renvoie 0. Le seul argument qui nous concerne est le pointeur vers une structure MSG
contenant des informations sur le message. Les autres arguments sont tous 0.
Dans la boucle de message, TranslateMessage
traduit les messages de clé virtuelle en messages de caractère. La signification de ceci, encore une fois, est sans importance pour nous. Il faut un pointeur sur une structure MSG
. L'appel qui le suit directement, DispatchMessage
, distribue le message pointé par son argument à la procédure de fenêtre de la fenêtre. La dernière chose que WinMain
doit faire est de renvoyer un code d'état. Le membre wParam
de la structure MSG
contient cette valeur de retour, il est donc retourné.
Mais c'est juste pour la fonction WinMain
. L'autre fonction est winproc
, la procédure de fenêtre. Il traitera les messages de la fenêtre qui lui sont envoyés par Windows. La signature de winproc
est la suivante:
- hwnd: descripteur de la fenêtre dont les messages sont en cours de traitement.
- wm: l'identifiant du message de la fenêtre
- wp: un des arguments d'information sur le message. Cela dépend de l'argument
wm
- lp: un des arguments d'information de message. Cela dépend de l'argument
wm
. Cet argument est généralement utilisé pour transmettre des pointeurs ou des poignées
Dans ce programme simple, nous ne traitons aucun message nous-mêmes. Mais cela ne signifie pas que Windows non plus. C'est pourquoi il faut appeler DefWindowProc
, qui contient le code de gestion des fenêtres par défaut. Cette fonction doit être appelée à la fin de chaque procédure de fenêtre.
Qu'est-ce qu'une poignée?
Un handle est un type de données qui représente un objet unique. Ce sont des pointeurs, mais des structures de données secrètes gérées par le système d'exploitation. Les détails de ces structures ne doivent pas nous concerner. Tout ce que l'utilisateur doit faire, c'est simplement créer / récupérer un descripteur en utilisant un appel d'API, et le transmettre à d'autres appels d'API en utilisant ce type de descripteur. Le seul type de handle que nous avons utilisé était leHWND
renvoyé par CreateWindowEx
. Les constantes
Dans cet exemple, nous rencontrons une poignée de constantes, qui sont en majuscules et commencent par un préfixe de 2 ou 3 lettres. (Les types Windows sont également en majuscules)- IDI_APPLICATION: Nom de la ressource contenant l'icône de l'application par défaut. Ceci est utilisé avec
LoadIcon
ouLoadImage
(LoadIcon dans cet exemple). - IDC_ARROW: le nom de la ressource qui contient le curseur de l'application par défaut. Ceci est utilisé avec
LoadIcon
ouLoadImage
(LoadIcon dans cet exemple). - WHITE_BRUSH: Le nom d'un objet stock. Cet objet de stock est le pinceau blanc.
- MB_ICONERROR: Un indicateur utilisé avec
MessageBox
pour afficher une icône d'erreur. - WS_EX_LEFT: le style de fenêtre étendu par défaut. Cela entraîne la fenêtre à avoir des propriétés alignées à gauche.
- WS_OVERLAPPEDWINDOW: style de fenêtre indiquant que la fenêtre doit être une fenêtre parente avec une barre de titre, une zone de taille et d'autres éléments typiques des fenêtres de niveau supérieur.
- CW_USEDEFAULT: Utilisé avec les arguments
x
,y
,cx
oucy
CreateWindowEx
. Windows choisit une valeur valide pour l'argument pour lequelCW_USEDEFAULT
été transmis.
Types de Windows
Lors de la programmation pour Windows, vous devrez vous familiariser avec les types Win32, qui sont des alias pour les types intégrés. Ces types sont en majuscules. Les types d'alias utilisés dans ce programme sont:- TCHAR: Le type de caractère générique. Si
UNICODE
est défini, il s'agit d'unwchar_t
. Otheriwse, c'est unchar
. - UINT: Un entier non signé. Utilisé pour représenter l'identificateur de message dans les procédures de fenêtre et à d'autres fins.
- WPARAM: Dans Win16, il s'agissait d'un argument WORD (d'où le préfixe
W
). Avec l'introduction de Win32, il s'agit maintenant d'unUINT_PTR
. Cela illustre le point de ces alias Windows; ils sont là pour protéger les programmes du changement. - LPARAM: Ceci est un argument
LONG
(LONG_PTR
dans Win64). - PTSTR: Le
P
signifie un pointeur. LeT
signifie un caractère générique et leSTR
signifie une chaîne. Il s’agit donc d’un pointeur sur une chaîneTCHAR
. Les autres types de chaînes incluent:- LPTSTR: Identique à
PTSTR
- LPCTSTR: signifie
const TCHAR *
- PCTSTR: Identique à
LPCTSTR
- LPWSTR: Chaîne large (
wchar_t *
) - LPCWSTR: signifie
const wchar_t *
- PWSTR: Identique à
LPWSTR
- Et beaucoup plus Comme vous pouvez le voir, les types Win32 peuvent être un problème à comprendre, en particulier avec autant de types synonymes, qui sont un artefact de Win16.
- LPTSTR: Identique à
- LRESULT: Ce type est utilisé pour représenter la valeur de retour des procédures de fenêtre. C'est généralement un long (d'où le
L
).