Création de classe fenêtre et d'instances
Création d'une fenêtre
Le code de création d'une fenêtre se fait en deux phases:
- Tout d'abord, il faut créer une classe de fenêtre.
- Ensuite il faut créer une instance de la classe de fenêtre.
Remarque:
L'affichage de la fenêtre ne se fera pas ici. Il faudra encore faire quelques petites choses qui sont expliquées dans le chapitre suivant.
Pourquoi classe et instance ?
Il est probable que vous n'ayez pas qu'une fenêtre à afficher dans votre application, mais bien plusieurs.
Sous windows en version 16 bits, plutôt que de définir sans arrêt la même chose, on pouvait se diriger vers une programmation orientée objet. La première instance du programme allait donc déclarer la classe de sa fenêtre à Windows, puis les instances suivantes de du programme utilisaient directement la classe de cette fenêtre sans pour autant la redéfinir de nouveau.
Pour savoir si votre programme était la première instance ou non, il suffisait de tester le paramètre hPrevInstance de la fonction WinMain. Si ce paramètre était différent de zéro, c'est qu'il existait au moins une instance du programme (l'instance de ce programme étant alors hPrevInstance).
Depuis les windows 32 bits, ce paramètre hPrevInstance est toujours transmis avec la valeur NULL. Cela signifie que chaque instance d'une application fonctionne comme si elle était la seule instance en cours d'exécution. Chaque programme va donc définir une classe de sa fenêtre...
Cependant, ce principe de classe est gardé, car il est en effet possible d'avoir plusieurs instances de la même classe fenêtre dans une même instance du programme !
Evidemment, il est toujours possible de faire le contrôle sur hPrevInstance même s'il ne sert plus (compatibilité oblige !). Vos anciens programmes fonctionneront donc toujours. Pour les nouveaux, il faudra passer par un sémaphore.
Création d'une classe de fenêtre
Pour définir une classe de fenêtre, vous utiliserez
Soit type de structure WNDCLASS
Où de préférence WNDCLASSEX qui est WNDCLASS étendue.
Je commence par WNDCLASS, sachant que la différence entre WNDCLASS et WNDCLASSEX est vraiment minime:Il n'y a que quelques champs supplémentaires...
Exemple de définition d'une classe pour une fenêtre dans la structure
wndclass
wndclass.lpszClassName = szAppName;
wndclass.lpfnWndProc = WndProc;
wndclass.hInstance = hInstance;
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
Sous Windows 3.1,
on tapait plutôt wndclass.hbrBackground = GetStockObject
(WHITE_BRUSH); qui me semble plus propre...
wndclass.lpszMenuName = NULL;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
Cela peut faire peur, mais en fait, les champs les plus importants sont:
wndclass.lpszClassName;
wndclass.lpfnWndProc;
szAppName va contenir le nom de votre classequi sera stocké dans wndclass.lpszClassName. C'est ce nom que vous utiliserez ensuite dans vos instances de vos applications pour instancier vos fenêtres.
WndProc est une procédure qui traitera les messages (événements) concernant les fenêtres. A propos de cette procédure, comme elle peut être appelée de n'importe où par n'importe quelle instance de l'application, la définition de cette procédure sera particulière:
long FAR PASCAL _export WndProc (HWND, UINT,UINT,LONG); (Valable uniquement
sous Windows 3.1 et donc, obsolète !)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); (De nos jours !)
Le premier paramètre que peut recevoir cette fonction étant hWnd qui est le handle de la fenêtre sur laquelle un message sera à traiter. Ensuite, c'est le message et les paramètres de ce message. Tout ceci étant expliqué dans le détail dans le chapitre sur la procédure de gestions des messages.
Les champs qui suivent sont enfin:
wndclass.hInstance : On indique ici l'instance du programme ayant déclaré la classe
wndclass.style : On indique ici les événements que la procédure WndProc doit traiter. Dans cette exemple, on veut connaître toutes modifications de la taille de la fenêtre horizontalement et verticalement.
wndclass.hIcon
wndclass.hCursor : L'icône et le curseur (pointeur de la souris) par défaut
de la fenêtre.
wndclass.hbrBackground : Couleur de fond de la fenêtre.
wndclass.lpszMenuName : Si un menu est associé à la fenêtre, on peut définir ici les paramètres de ce menu
wndclass.cbClsExtra
wndclass.cbWndExtra : pour se réserver de la place. Jamais utilisé
personnellement...
Enfin, vous enregistrez votre classe de fenêtre dans Windows en utilisant
la fonction RegisterClass et en lui passant la structure en paramètre
ainsi définie:
RegisterClass (&wndclass);
comme indiqué
au début, wndclass est étendue:
Vous auriez pu faire:
pour définir la classe:WNDCLASSEX wndclassex;
Ce qui change alors:
wndclassex.cbSize=sizeof(WNDCLASSEX);
Définition de l'icône small:
wndclassex.hIconSm =LoadIcon (NULL, IDI_APPLICATION);
Sinon, le reste de la structure est identique à wndclass.
Enfin, pour enregistrer la classe : RegisterClassEx (&wndclassex);
Création d'une instance de la nouvelle classe de fenêtre
Pour une fenêtre utilisant la structure wndclass:
HWND CreateWindow(
LPCTSTR lpClassName, // nom de la classe de fenêtre
LPCTSTR lpWindowName, // titre de votre fenêtre
DWORD dwStyle, // style de la fenêtre
int x, // position horizontal de la fenêtre
int y, // position vertical de la fenêtre
int nWidth, // largeur de la fenêtre
int nHeight, // hauteur de la fenêtre
HWND hWndParent, // handle de la fenêtre parent
HMENU hMenu, // handle de menu
HANDLE hInstance, // handle de linstance de lapplication
LPVOID lpParam // Pointe sur une valeur passée à la fenêtre à travers une structure CREATESTRUCT.
);
Pour une fenêtre utilisant la structure wndclassex:
Il faut utiliser la fonction CreateWindowEx().
HWND CreateWindowEx(
DWORD dwExStyle, // style étendue de la fenêtre
LPCTSTR lpClassName, // nom de la classe de fenêtre
LPCTSTR lpWindowName, // titre de votre fenêtre
DWORD dwStyle, // style de la fenêtre
int x, // position horizontal de la fenêtre
int y, // position vertical de la fenêtre
int nWidth, // largeur de la fenêtre
int nHeight, // hauteur de la fenêtre
HWND hWndParent, // handle de la fenêtre mère
HMENU hMenu, // handle de menu
HINSTANCE hInstance, // handle de linstance de lapplication
LPVOID lpParam // Pointe sur une valeur passée à la fenêtre à travers une structure CREATESTRUCT.
);
Concernant x, y, nWidth et nHeight, on pourra utiliser la constante CW_USEDEFAULT,
laissant Windows utiliser les valeurs par défaut.
Dans lpClassName, vous ne mettrez ici pour le moment que le nom de la classe
que vous venez de définir, mais il faut savoir qu'il existe des classes
prédéfinies...
Exemple :
Avec CreateWindow:
HWND hWnd;
hWnd=CreateWindow(
"nom_de_notre_classe",
"Titre de notre fenêtre",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
Avec CreateWindowEx:
HWND hWnd;
hWnd=CreateWindowEx(
WS_EX_CLIENTEDGE,
"nom_de_notre_classe",
"Titre de notre fenêtre",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
NULL,
NULL,
hInstance,
NULL
);
CreateWindow/CreateWindowEx:
ces deux fonctions vont créer la fenêtre puis envoyer un message
WM_CREATE à la procédure
de gestion des messages de la nouvelle fenêtre. Evidemment, la variable
hWnd de "hWnd=CreateWindow" ou "hWnd=CreateWindowEx()"
qui doit reçevoir le handle de votre nouvelle fenêtre via ces fonctions
n'est alors toujours pas renseignée ! En effet, la fonction CreateWindow/CreateWindowEx
n'a toujours pas rendue la main à votre application. La variable
ne sera donc pas encore utilisable ! A priori, vous n'avez pas à l'utiliserez
dans la procédure de gestion des messages car
celle-ci reçoit le handle en paramètre.
Annexes
Gestion du titre de la fenêtre et autres après création .