Fenêtre Charger/Sauver.

Afficher la fenêtre standard Windows qui propose de charger ou sauver un fichier.

GetOpenFileName,GetSaveFileName

Voir version :

Pas de dépendances

Télécharger :

#include <windows.h>
#include <tchar.h>
#include <stdio.h>

void convert_multiple(TCHAR* buf)
{
    int i;
    for(i=0;;i++)
    {
        if (buf[i]=='\0')
        {
            buf[i] = '\n';
            if (buf[i+1]=='\0')
                break;
        }
    }
}

int main()
{
    int res;
    OPENFILENAME ofn;
    TCHAR tmp[1024] ;
    tmp[0]= '\0' ;
    ZeroMemory ( &ofn , sizeof ( OPENFILENAMEW ) );
    ofn.lStructSize = sizeof ( OPENFILENAMEW );
    ofn.lpstrFile = tmp;
    ofn.nMaxFile = 1024;
    ofn.lpstrTitle = _T("Le titre");
    ofn.lpstrFilter = _T("Tous (*.*)\0*.*\0Textes (*.txt)\0*.TXT\0");
    ofn.Flags = OFN_LONGNAMES | OFN_EXPLORER; // | OFN_ALLOWMULTISELECT  ;
    res = GetOpenFileName(&ofn); 
    //int res = GetSaveFileName(&ofn); 
    printf("Code de sortie : %d\n",res);
    //convert_multiple(ofn.lpstrFile);
    _tprintf(_T("Buffer de sortie : %s\n"),ofn.lpstrFile);
    return 0;
}



Commentaires

  Pour code::blocks, vous aurez besoin de linker Comdlg32.a
  Pour visual C++, la lib Comdlg32.lib est nécessaire, mais visual la link automatiquement visiblement.

  Une seule fonction est appelée dans ce programme : c'est GetOpenFileName.
  Tout ce qu'il y avant, c'est un remplissage de structure.
  En effet, il faut remplir une structure OPENFILENAME avant d'appeler GetOpenFileName

  doc plus explicite sur cette structure OPENFILENAME 
  
  http://msdn2.microsoft.com/en-us/library/ms646839.aspx

  doc sur GetOpenFileName

  http://msdn2.microsoft.com/en-us/library/ms646927.aspx

  Les options sont très très nombreuses.

  Si vous lancez le programme directement, vous pouvez choisir un fichier texte, ou un fichier *.*
  En sortant, on vous affiche le code d'erreur de la fonction GetOpenFileName, et le nom du fichier choisi :)

VALEURS DE RETOUR :
*******************

  La fonction renvoie 1 si vous avez choisi un fichier 0 si vous avez annulé :)

LES CHAINES MULTIPLES :
***********************

  Vous pouvez remarquer, dans la ligne suivante :
  ofn.lpstrFilter = "Tous (*.*)\0*.*\0Textes (*.txt)\0*.TXT\0" ;
  une drole de façon de coder les chaines...

  Comme vous le savez surement, en C, une chaine est "NULL TERMINATED", 
  c'est a dire que l'on défini que un 0 termine la chaine.

  la fonction GetOpenFileName va pouvoir gérer des chaines que j'appellerais "chaines multiples"

  Une chaine multiple, c'est un codage qui fait que si l'on trouve un 0, on a un séparateur, 
  et on admet qu'on terminera la chaine quand on trouvera 0 0 (2 zéros consécutifs)
  Cela va nous permettre de rentrer plusieurs chaines dans une seule.
  Ici en l'occurrence, on entre 4 chaines :

  Tous (*.*)
  *.*
  Textes (*.txt)
  *.TXT

  Chaque \0 permet de mettre EXPLICITEMENT dans la chaine un code 0 (un NULL TERMINATED)
  Il est important d'en mettre un a la fin de la chaine !

  En effet, le langage C (ou C++), lorsqu'on lui rentre une chaine en dur, comme ici, 
  rajoute tout seul le \0 final a la fin.
  Par exemple, quand vous faites :

  char* plouf = "test";
  Le compilo rajoute un \0, ainsi, en mémoire, la chaine est  test\0 -> ce qui donnera une chaine standard.

  Dans la chaine 
  "Tous (*.*)\0*.*\0Textes (*.txt)\0*.TXT\0" ;

  Vous n'échappez pas a la regle, et donc un deuxieme \0 se greffe tout seul apres le .TXT, ce qui, 
  comme vu plus haut, correspondra a la fin   de la chaine multiple.

  note : si vous lancez un strlen sur une chaine multiple, vous obtiendrez la longueur de la premiere chaine : 
  en effet, strlen, strcpy, et toutes les autres, partent du principe qu'on leur donne une chaine normale, 
  donc qui se termine par \0. Si vous passez une chaine multiple, au premier \0 trouvé, les fonctions "penseront"
  que la chaine est terminée, et ne vous prendront donc que la premiere partie....


GetOpenFileName VS GetSaveFileName
**********************************

  Je dois dire que je n'ai pas vu de différences fondamentales entre les deux fenetres : 
  l'une a pour but de charger un fichier,  l'autre de le sauver.
  GetSaveFileName réagira sur la flag OFN_OVERWRITEPROMPT : puisque le but est de sauver un fichier, 
  si on en choisis un qui existe déja, ça demandera (ou pas selon l'activation du flag)
  si on écrase ou pas. Ce flag n'a aucun sens pour GetOpenFileName.
  Bon, je ne vais pas disserter la dessus, il y a tellement d'options à essayer :)

  * Je rappelle que ces fonctions permettent de choisir un fichier, mais elles ne le modifient pas : 
  c'est a vous, derriere, de bosser a coup de fopen ou de ifstream/ofstream bien sur !
    
	
FLAGS
*****

  le champ ofn.Flags permet de renseigner des flags. Vous en verrez la liste et les explications sur cette page :
  http://msdn2.microsoft.com/en-us/library/ms646839.aspx

  Entre autre, diverses options de confirmations : 
  (par exemple, est ce que je peux selectionner un fichier qui n'existe pas, est ce que je veux des noms longs, etc...)
  (si vous utilisez GetSaveFileName, et que vous selectionnez un fichier, 
  ça vous demande confirmation si vous activez OFN_OVERWRITEPROMPT)

  Une option intéressante est : OFN_ALLOWMULTISELECT, qui va vous permettre de choisir plusieurs fichiers !
  En effet, si par exemple vous voulez faire un convertisseur d'images par lot, vous avez envie de rentrer 
  plusieurs noms de fichiers, vous pouvez le faire grace a ce flag.
  Voir dans "récuperer la réponse" pour savoir comment récupérer cela.

  La gestion des flags se fait avec es | . Vous avez certainement vu cette utilisation, par exemple dans SDL, ou ailleurs.
  

RECUPERER LA REPONSE :
**********************

  Le but est bien entendu de récupérer le nom des fichiers selectionnés !
  - Si le flag OFN_ALLOWMULTISELECT est débranché, vous récupérez votre réponse dans ofn.lpstrFile en tant que char*
  Comme vous pouvez le voir en testant le fichier, vous récupérez tout le chemin avec.
  - Si le flag OFN_ALLOWMULTISELECT est branché, alors vous récupérez, dans ofn.lpstrFile, une chaine multiple, 
  comme décrite plus haut.
  C'est a vous d'analyser cette chaine multiple.

  Dans l'exemple, si vous activez ce flag, activez aussi la ligne convert_multiple(ofn.lpstrFile);
  Vous pouvez constater que cette fonction que j'ai faite remplace les séparateurs \0 par des retour chariot \n
  Ainsi, le programme affiche tous les noms de fichier selectionnés :)

REMPLISSAGE DE LA STRUCTURE AVANCE :
************************************

  Bon, je suis allé droit au but plus haut, détaillons maintenant quelques options pour le remplissage de la structure OPENFILENAME.
  Pour éviter les données non initialisées, et dans la philosophie Windows, on a coutume de faire un ZeroMemory de la taille de la structure :
  en gros, tous les octets de la structure sont mis a zéro -> une sorte de constructeur assez violent, mais rapide et fiable dans la philosophie C.

  ofn.lStructSize

  Il est important quand meme de remplir le champ précédant : la taille de la structure en l'occurrence.

  	ofn.lpstrFile = tmp;
	ofn.nMaxFile = 1024;

  Ces 2 lignes permettent de me dire ou je veux que la réponse (le noms des fichiers selectionnés) soit écrite.
  -> je choisis mon buffer tmp, et je sécurise, en mettant nMaxFile a 1024 : ça veut dire que j'impose a la fonction
  GetOpenFileName ou GetSaveFileName de ne surtout pas dépasser (par exemple) 1024 octets.
  Ainsi, je suis sécurisé : j'évite tout débordement.
  Les fonctions ne couperont pas le nom d'un fichier (dans une chaine simple ou multiple), 
  si le fichier ne tient pas dans le buffer, il n'est pas mis.

  ofn.lpstrTitle parle de lui meme.

  ofn.lpstrFilter = "Tous (*.*)\0*.*\0Textes (*.txt)\0*.TXT\0" ;

  Cette ligne n'est pas trop compliquée en fait : Comme on a vu plus haut, c'est une chaine multiple.
  Ici, j'ai donc 4 chaines. On les considere 2 par 2.
  --> Il faut donc qu'il y en ait un nombre PAIR.

  Pour un couple de chaines :
  - la premiere s'affichera pour l'utilisateur dans la case "fichiers de type"
  - la deuxieme sera un indice pour l'ordi, pour qu'il sache quels fichiers afficher.

  ofn.Flags -> ben les flags :)

  Il y a plein d'autres options que je n'ai pas rempli, utiles pour personnaliser davantage les choses.

  hwndOwner -> la fenetre navigue indépendamment. Mais si dans Paint, par exemple, vous faite "sauvegarder", vous 
  constaterez que la fenetre ne peut pas sortir de celle de paint, parce qu'elle lui appartient : 
  dans ce cas, hwndOwner a comme valeur le HANDLE de la fenetre qui lui appartient. 
  Dans notre programme, 0 donc (= NULL) pour dire qu'elle est indépendante.

  lpstrInitialDir -> si vous voulez mettre le repertoire dans lequel enregistrer/charger. 
  Vous avez vu que par défaut, on est dans "mes documents"

  lpstrFileTitle & nMaxFileTitle : si vous voulez recevoir, comme réponse, le nom des fichiers sans le nom du repertoire, 
  changez ces données.

  Il y en a plein d'autres, je vous laisse les découvrir grace a ce lien :
  http://msdn2.microsoft.com/en-us/library/ms646839.aspx
  
  Note : cette fonction a 2 versions, une version avec des char*, et une version UNICODE (voir tuto sur MessageBox a la fin)

  Note2 : la fonction _tprintf est simplement printf si on n'est pas en UNICODE, et wprintf si on est en UNICODE.
   
   Avec %s, printf affiche les char*, et wprintf les wchar_t*
   Avec %S, printf affiche les wchar_t* et wprintf les char*