API Windows en C

Les boîtes de dialogue communes (Ouverture de fichier)

CGI

?

- Titre : API Windows en C

- Auteur : CGI

- Parution : 5 juin 2005

- Licence : Copyright 2002-2020 CGi - Tous droits réservés CGi. Toutes reproduction, utilisation ou diffusion de ce document par quelque moyen que ce soit autre que pour un usage personnel doit faire l'objet d'une autorisation écrite de la part de l'auteur, propriétaire des droits intellectuels. Les codes sources de ce document sont fournis en l'état. L'utilisateur les utilise à ses risques et périls, sans garantie d'aucune sorte de la part de l'auteur. L'auteur n'est responsable d'aucun dommage subi par l'utilisateur pouvant résulter de l'utilisation ou de la distribution des codes sources de ce document. De la même façon, l'auteur n'est en aucun cas responsable d'une quelconque perte de revenus ou de profits, ou de données, ou de tous dommages directs ou indirects, susceptibles de survenir du fait de l'utilisation des codes sources de ce document, quand bien même l'auteur aurait été averti de la possibilité de tels dommages. L'utilisation des codes sources de ce document vaut acceptation par l'utilisateur des termes de la licence ci-dessus.

Sommaire

- I. Introduction

- II. Préparation de l'exemple

- III. La boîte de dialogue commune

- IV. Code complet

- V. Remarques

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\0Fichier 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\nEtes 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\0Fichier 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.

CGi

Retour au sommaire.