I. Introduction▲
Les boîtes de dialogues communes sont celles qui font partie de Windows, comme les boîtes de dialogue d'ouverture ou de sauvegarde de fichier, de choix de polices de caractères, de choix de couleurs… Dans ce premier exemple nous mettrons en œuvre la boîte de dialogue d'ouverture de fichier. Le principe est semblable pour les autres boîtes de dialogue.
II. Préparation de l'exemple▲
Nous continuons sur la base de l'exemple du chapitre précédent. Nous ajoutons une option avec le texte « Ouvrir… » plus un séparateur au sous-menu. L'option « Ouvrir… » sera identifiée par la constante IDM_OPEN :
#define IDM_QUIT 1
#define IDM_OPEN 2
hSousMenu =
CreateMenu
(
);
AppendMenu
(
hSousMenu, MF_STRING, IDM_OPEN, "
Ouvrir...
"
);
AppendMenu
(
hSousMenu, MF_SEPARATOR, 0
, NULL
);
AppendMenu
(
hSousMenu, MF_STRING, IDM_QUIT, "
Quitter
"
);
Comme vu au chapitre précédent, son action sera interceptée dans la procédure de fenêtre avec un message WM_COMMAND :
switch
(
uMsg)
{
case
WM_COMMAND:
if
(
LOWORD
(
wParam) ==
IDM_OPEN)
{
/* Appel de la boîte de dialogue ici. */
III. La boîte de dialogue commune▲
Avant d'appeler une boîte de dialogue commune, nous devons remplir une structure de données la concernant. Elle sera passée comme paramètre à la fonction d'appel. Dans le cas de la boîte de dialogue destinée à l'ouverture de fichier la structure se nomme OPENFILENAME :
OPENFILENAME ofn;
CHAR szFile[MAX_PATH]={
0
}
;
ZeroMemory
(&
ofn, sizeof
(
OPENFILENAME));
ofn.lStructSize =
sizeof
(
OPENFILENAME);
ofn.hwndOwner =
hwnd;
ofn.lpstrFile =
szFile;
ofn.nMaxFile =
MAX_PATH;
ofn.lpstrFilter =
"
Fichier source C
\0
*.c
\0
Fichier source CPP
\0
*.cpp
\0
"
;
ofn.nFilterIndex =
1
;
ofn.Flags =
OFN_PATHMUSTEXIST |
OFN_FILEMUSTEXIST |
OFN_HIDEREADONLY;
Nous déclarons donc en premier lieu une variable du type de cette structure : ofn. Comme nous allons remplir seulement les champs qui nous intéressent, nous metterons la totalité de la structure à zéro. Ceci étant effectué en une seule opération par l'appel de la fonction ZeroMemory.
Ensuite, nous affectons son champ lpstrFile avec l'adresse d'un tableau de caractères que nous avons préalablement défini, pour y récupérer le nom et le chemin du fichier. Attention, il doit contenir une chaine valide avant l'appel de la fonction, car elle l'utilise aussi pour initialiser le champ d'édition : « Nom du fichier » de la boîte de dialogue.
Le champ lStructSize doit recevoir la taille de la structure.
Le champ hwndOwner le handle de la fenêtre parent.
Le champ nMaxFile la taille du tableau de caractère recevant le nom du fichier.
Le champ lpstrFilter avec l'adresse d'un tableau de caractères contenant des filtres de fichiers par leur extension. Les textes des filtres et les filtres étant séparés par des caractères null et le tableau ce terminant par deux caractères null (ce type de tableau de caractères est assez fréquent dans Windows).
Le champ nFilterIndex avec l'index du filtre que l'on désire afficher dans le champ d'édition « Fichier de type » à l'ouverture de la boîte de dialogue.
Le champ Flags avec différentes constantes qui influence sur son aspect ou ses fonctionnalités (voir l'aide API Win32).
Une fois les champs de la structure correctement rempli, nous appelons la fonction GetOpenFileName avec l'adresse de la structure précédement rempli comme paramètre. Si elle nous retourne la valeur TRUE, ce qui indique que l'utilisateur à fermé la boîte de dialogue avec le bouton « Ouvrir », nous pourons récupérer le nom et chemin du fichier sélectionné dans le buffer szFile afin de charger le fichier.
if
(
GetOpenFileName
(&
ofn)==
TRUE)
{
HANDLE hf;
DWORD FileSize,nbcharRead ;
CHAR *
buffer;
hf =
CreateFile
(
szFile, GENERIC_READ, 0
,NULL
,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL
);
FileSize =
GetFileSize
(
hf, NULL
);
buffer =
(
PCHAR)LocalAlloc
(
LMEM_FIXED, FileSize+
1
);
ReadFile
(
hf, buffer, FileSize, &
nbcharRead, NULL
) ;
buffer[FileSize] =
0
;
SendMessage
(
hEdit, WM_SETTEXT, 0
, (
LPARAM)buffer);
LocalFree
(
buffer);
CloseHandle
(
hf);
}
Connaissant maintenant le nom et le chemin du fichier, nous allons l'ouvrir avec la fonction de Windows CreateFile qui retourne un handle de fichier. Je vous invite à consulter l'aide API Win32 pour plus de détails sur cette fonction qui offre de nombreuses possibilités. Nous récupérons la taille du fichier avec la fonction GetFileSize. Nous créons un buffer de la taille du fichier + 1 octet avec la fonction de Windows LocalAlloc. Nous remplissons ce buffer avec le contenu du fichier à l'aide de la fonction ReadFile. Comme c'est un fichier texte nous ajoutons le zéro terminal au dernier élément du buffer pour en faire une chaîne de caractères.
Nous la copions dans le champ d'édition multiligne, en lui envoyant un message WM_SETTEXT avec l'adresse du buffer comme dernier paramètre de SendMessage. Pour terminer, nous libérons la mémoire avec la fonction LocalFree et fermons le fichier avec la fonction CloseHandle.
IV. Code complet▲
#include <windows.h>
#define IDM_QUIT 1
#define IDM_OPEN 2
HINSTANCE hinst;
LRESULT CALLBACK MainWndProc
(
HWND, UINT, WPARAM, LPARAM);
int
WINAPI WinMain
(
HINSTANCE hinstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int
nCmdShow)
{
HWND hwnd;
MSG msg;
WNDCLASS wc;
HMENU hMenu, hSousMenu;
hinst =
hinstance;
wc.style =
0
;
wc.lpfnWndProc =
MainWndProc;
wc.cbClsExtra =
0
;
wc.cbWndExtra =
0
;
wc.hInstance =
hinstance;
wc.hIcon =
NULL
;
wc.hCursor =
LoadCursor
(
NULL
, IDC_ARROW);
wc.hbrBackground =
NULL
;
wc.lpszMenuName =
NULL
;
wc.lpszClassName =
"
MaWinClass
"
;
if
(!
RegisterClass
(&
wc)) return
FALSE;
hSousMenu =
CreateMenu
(
);
AppendMenu
(
hSousMenu, MF_STRING, IDM_OPEN, "
Ouvrir...
"
);
AppendMenu
(
hSousMenu, MF_SEPARATOR, 0
, NULL
);
AppendMenu
(
hSousMenu, MF_STRING, IDM_QUIT, "
Quitter
"
);
hMenu =
CreateMenu
(
);
AppendMenu
(
hMenu,MF_POPUP,(
UINT)hSousMenu,"
Fichier
"
);
hwnd =
CreateWindow
(
"
MaWinClass
"
, "
Titre
"
, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 640
, 300
,
NULL
, hMenu, hinstance, NULL
);
if
(!
hwnd) return
FALSE;
ShowWindow
(
hwnd, nCmdShow);
while
(
GetMessage
(&
msg, NULL
, 0
, 0
))
{
TranslateMessage
(&
msg);
DispatchMessage
(&
msg);
}
return
msg.wParam;
}
/**
***************************************************************************
*/
LRESULT CALLBACK MainWndProc
(
HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
static
HWND hEdit;
static
BOOL EditNotChg =
TRUE;
switch
(
uMsg)
{
case
WM_CREATE:
{
HFONT hFont;
hEdit =
CreateWindow
(
"
edit
"
, "
Texte
"
, WS_CHILD |
WS_VISIBLE |
ES_MULTILINE |
ES_WANTRETURN |
WS_VSCROLL,
0
, 0
, 0
, 0
, hwnd, NULL
, hinst, NULL
);
hFont =
(
HFONT)GetStockObject
(
ANSI_FIXED_FONT);
SendMessage
(
hEdit,WM_SETFONT,(
UINT)hFont,TRUE);
SendMessage
(
hEdit, EM_SETMARGINS, EC_LEFTMARGIN |
EC_RIGHTMARGIN,
MAKELONG
(
5
, 5
));
return
0
;
}
case
WM_CLOSE:
if
(
EditNotChg ||
MessageBox
(
hwnd,"
Le texte a été modifié.
\r\n
Etes vous sûr de \
vouloir fermer l'application ?
"
,"
Question ?
"
,MB_YESNO |
MB_ICONQUESTION ) ==
IDYES)
DestroyWindow
(
hwnd);
return
0
;
case
WM_COMMAND:
if
(
LOWORD
(
wParam) ==
IDM_OPEN)
{
OPENFILENAME ofn;
CHAR szFile[MAX_PATH]={
0
}
;
ZeroMemory
(&
ofn, sizeof
(
OPENFILENAME));
ofn.lStructSize =
sizeof
(
OPENFILENAME);
ofn.hwndOwner =
hwnd;
ofn.lpstrFile =
szFile;
ofn.nMaxFile =
MAX_PATH;
ofn.lpstrFilter =
"
Fichier source C
\0
*.c
\0
Fichier source CPP
\0
*.cpp
\0
"
;
ofn.nFilterIndex =
1
;
ofn.Flags =
OFN_PATHMUSTEXIST |
OFN_FILEMUSTEXIST |
OFN_HIDEREADONLY;
if
(
GetOpenFileName
(&
ofn)==
TRUE)
{
HANDLE hf;
DWORD FileSize,nbcharRead ;
CHAR *
buffer;
hf =
CreateFile
(
szFile, GENERIC_READ, 0
,NULL
,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
);
FileSize =
GetFileSize
(
hf, NULL
);
buffer =
(
PCHAR)LocalAlloc
(
LMEM_FIXED, FileSize+
1
);
ReadFile
(
hf, buffer, FileSize, &
nbcharRead, NULL
) ;
buffer[FileSize] =
0
;
SendMessage
(
hEdit, WM_SETTEXT, 0
, (
LPARAM)buffer);
LocalFree
(
buffer);
CloseHandle
(
hf);
}
}
if
(
LOWORD
(
wParam) ==
IDM_QUIT) PostMessage
(
hwnd, WM_CLOSE,0
,0
);
if
(
HIWORD
(
wParam) ==
EN_CHANGE) EditNotChg =
FALSE;
return
0
;
case
WM_SIZE:
MoveWindow
(
hEdit, 0
, 0
, LOWORD
(
lParam), HIWORD
(
lParam), TRUE);
return
0
;
case
WM_DESTROY:
PostQuitMessage
(
0
);
return
0
;
default
:
return
DefWindowProc
(
hwnd, uMsg, wParam, lParam);
}
}
En annexe voici trois autres boîtes de dialogue communes :
- la boîte de dialogue de sauvegarde ;
- la boîte de dialogue de choix de police de caractères ;
- la boîte de dialogue de choix de couleur.
J'ai testé les compilations avec C++ Builder 4 (Win 95), C++ Builder 6 (Win XP), et DevC++ (Win XP).
V. Remarques▲
Compilé avec BCB6, la boîte de dialogue ne s'ouvrait pas si le programme était exécuté sous une plateforme Win95. Une erreur CDERR_STRUCTSIZE était retourné. Aprés une recherche sur le Web, j'ai trouvé que les compilations faite pour Win 2000 pouvait causer ce problème si les fichier header de Windows était recent. Je n'ai pas eu ce problème avec DevC++ avec une compilation sous Win XP.
La solution pour pallier ce problème est d'intitaliser le champs lStructSize de la
structure OPENFILENAME avec la constante : OPENFILENAME_SIZE_VERSION_400.
ofn.lStructSize =
OPENFILENAME_SIZE_VERSION_400;
Voire une compilation conditionnelle :
#ifdef OPENFILENAME_SIZE_VERSION_400
ofn.lStructSize =
OPENFILENAME_SIZE_VERSION_400;
#else
ofn.lStructSize =
sizeof
(
OPENFILENAME);
#endif
À tester selon vos configurations.
À vos PC.
Avec la contribution de nicopyright(c) pour la relecture.