Technique : les Patch Files sous C++ Builder

ce tutorial démontre une des technique utilisée pour mettre à jour des Fichiers executables a l'aide des File Mapping avec un exemple d'application

Sources du Programme disponible ici.

Article lu   fois.

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Programme d'exemple

pour démontrer une des techniques des PatchFiles, je vous propose une application basique permettant de patcher un fichier exécutable ou une Dll.

I-A. Limitation

Ce programme est écrit en C++ à l'aide de C++ Builder 6.

Dans ce programme est utilisé certaines fonctionnalités VCL qui rendent la parti GUI non portable sur une autre compilateur, l'adaptation du code est par contre possible.

II. Définition du GUI

Image non disponible

II-A. définition de la forme principale

Pour Commencer nous allons définir la forme Principale

Déposer les Composants suivants sur une Form

Composant Nom
1 TPanel Align = alClient
1 TGroupBox GroupBox1
1 TMainMenu MainMenu1
1 TOpenDialog OpenDialog1
1 TActionManager ActionManager1
1 TStatusBar StatusBar1

Déposer les Composants suivants sur le GroupBox

1 TLabeledEdit edOldFile
1 TLabeledEdit edNewFile
1 TBitBtn BitBtn1
1 TBitBtn BitBtn2

II-B. ImageList

Nous allons Charger les Images dans le composant ImageList1, sélectionner 4 images dans le répertoire fichiers communs de Borland comme dans la copie écran ci dessous

Image non disponible

II-C. ActionsList

Creation des actions

Ouvrir le composant ActionManager1

touche droite de souris sur le composant clicker sur personaliser

créer les actions suivantes

nom de l'action Caption Image index OnExecute
OpenOlDfile Open File 0 OpenOlDfileExecute
OpenPatchName Open Patch File 0 OpenPatchNameExecute
Make Make 3 MakeExecute
About About 1 AboutExecute
CloseApp Close 2 CloseApp

le Résultat devrait être le suivant

Image non disponible

II-D. ActionToolBar

nous allons créer maintenant une action ToolsBar

dans le composant ActionManager activer l'onglet Barre d'outils

presser le bouton nouvelle

Le résultat devrait ëtre le suivant

Image non disponible

dans la vue arborescente des objets placer le curseur de la souris sur ActionBars, click touche droite de la souris sur ajouter un élément dans l'ActionToolbar nouvellement crée placer le curseur de la souris sur sur l'élément Items puis créer 4 nouveaux éléments les noms données aux éléments sont les suivants

nom de l'élément Action
TActionClients[0] OpenOlDfile
TActionClients[1] OpenPatchName
TActionClients[2] Make
TActionClients[3] About
TActionClients[4] Close

II-E. Menu Principal

Création d'un menu selon Image Suivante

Image non disponible

donnez aux Items Actions les valeurs suivantes

Item Action
OpenFile1 OpenOlDfile
OpenPatchFile1 OpenPatchName
Make1 Make
Close1 Close
About1 About

II-F. Boutons divers

Donnez aux aux Actions de Buttons les Valeurs Suivantes

Item Action
BitBtn1 Make
BitBtn2 Close

Le design de l'application est maintenant terminé

III. Classe TMMF

la Classe TMMF a été expliquée dans mon tutoriel utilisation des files Mappping http://djmsoftware.developpez.com/FileMapping

III-A. Modification de la classe TMMF

j'ai modifié l'implémentation de la classe TMMF pour lui ajouter de nouvelles fonctionalités

Liste des éléments modifiés ainsi que leur signification

Type Nom partie détail
Champ FMapFileSizeLow private indique la taille basse de la zone mappée
Champ FMapFileSizeHigh private indique la taille haute de la zone mappée
champ FMapFileOffset private contient la différence de taille par rapport au fichier d'origine
Champ FBigger private indique si la taille du Patch est plus grande que la taiille d'origine
Constructeur TMMF(AnsiString FileName, long size, bool Bigger) public initalize FMapFileOffset et FBigger

III-B. Modification de la classe TMMF

la méthode Execute est modifiée comme suit

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
bool __fastcall TMMF::Execute()
{
    try 
    {
        GetFile();
        GetSizeofFile();
        if (FMapFileOffset==0)
        FMapFileOffset=FFileSize;
        if (FBigger)
        FMapFileSizeLow=FFileSize+FMapFileOffset;
        else 
        FMapFileSizeLow=FMapFileOffset;
        FMapFileSizeHigh=FFileSizeHigh;
        GetMapping();
        GetPtrAdresse();
        }
    catch (EOSError &E)
    {
        FErrorMsg=E.Message;
        return false;
        }
    return true;
    }

on va initialiser le champ FFileSize avec l'appel à GetSizeofFile

on teste le contenu de FMapFileOffset s'il est égal à 0 alors on initialise MapFileOffset à la valeur de FFileSize si la variable FBuffer est vraie alors FMapFileSizeLow devient égale à FFileSize+ FMapFileOffset sinon FMapFileSizeLow est égale à FFileSize

la méthode GetMapping est modifiée comme suit

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
void __fastcall TMMF::GetMapping()
{
    FFileMap=CreateFileMapping(
        FFileHandle, // handle to file to map
        NULL, // optional security attributes
        PAGE_READWRITE, // protection for mapping object
        FMapFileSizeHigh, // high-order 32 bits of object size
        FMapFileSizeLow, // low-order 32 bits of object size
        NULL);
    if (!FFileMap)
    RaiseLastOSError();
    
} 

IV. Classe Tform

Déclaration de la classe les champs suivantes sont déclarés

Type Nom partie détail
Champ FStreamSize private contient la taille du fichier Patch Mappé en mémoire
Champ FSizeOldFile private contient la taile du fichier à Patcher
champ FSizePatchFile private contient le taille du fichier Patch
Champ FDiff private Contient la différence de taille entre les 2 Fichiers
Champ FPatchIsBigger private Flag indiquant si le Patch est plus grand que le fichier d'origine
Champ FoldFileName private Contient le nom du fichier à patcher
Champ FPatchFileName private Contient le nom du fichier Patch

les Méthodes suivantes sont déclarées

Type Nom partie
Méthode bool __fastcall GetFileSize(AnsiString FileName,long* Size) private
Méthode void __fastcall AdjustFileSize(); private
Méthode bool __fastcall PatchFile(); private
Méthode void __fastcall DisplayErrorMessage(AnsiString msgE); private
Méthode void __fastcall ClearFields(); private
Méthode bool __fastcall IsBigger(long* size); private

V. Explicatif sur les méthodes Privées

V-A. Méthode GetFileSize(AnsiString FileName,long* Size)

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
bool __fastcall TForm1::GetFileSize(AnsiString FileName,long * Size)
{
long FileSize;
int FHandle=FileOpen(FileName,fmOpenRead);
if (FHandle!=-1)
{
    FileSize=FileSeek(FHandle,0,2);
    FileClose(FHandle);
    *Size= FileSize;
    return true;
    }
else return false;
}

cette méthode retourne true si le fichier existe le paramètre Size passé par référence retourne la taille du fichier.

V-B. Méthode AdjustFileSize()

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
void __fastcall TForm1::AdjustFileSize()
{
HANDLE FFileHandle;
FFileHandle=CreateFile(
    FoldFileName.c_str(), // pointer to name of the file
    GENERIC_READ|GENERIC_WRITE, // access (read-write) mode
    FILE_SHARE_READ|FILE_SHARE_WRITE,// share mode
    NULL, // pointer to security attributes
    OPEN_EXISTING, // how to create
    FILE_ATTRIBUTE_NORMAL, // file attributes
    NULL);

SetFilePointer(
    FFileHandle,
    FStreamSize,
    NULL,
    FILE_BEGIN);

SetEndOfFile(FFileHandle);
CloseHandle(FFileHandle);
}

cette méthode est utilisée pour ajuster la taille du fichier patché,en effet sans cette méthode si le fichier patch et plus petit que le fichier à patcher, la taille finale du fichier patché serait trop grande

pour ce faire on ouvre le fichier, avec le Handle ainsi obtenu on appelle l'API Win32 SetFilePointeren lui indiqant ou placer le pointeur de fin de fichier(EOF) on appelle ensuiteSetEndOfFilequi va ajuster le fichier à sa nouvelle taille

V-C. Méthode PatchFile()

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
bool __fastcall TForm1::PatchFile()
{
TMMF *oldMMF,*PatchMMF;
PatchMMF= new TMMF(FPatchFileName);
if (PatchMMF->Execute())
{
    FStreamSize=PatchMMF->FileSize;
    oldMMF= new TMMF(FoldFileName,FDiff,FPatchIsBigger);
    if (oldMMF->Execute())
    {
        memcpy(oldMMF->PtrMapping,PatchMMF->PtrMapping,FStreamSize);
        delete oldMMF;
        if (!FPatchIsBigger)AdjustFileSize();
        }
    else 
    {
&#160;&#160;&#160; &#160;&#160;&#160; MessageDlg(oldMMF->ErrorMessage, mtError, TMsgDlgButtons() << mbOK, 0);
&#160;&#160;&#160; &#160;&#160;&#160; StatusBar1->Panels->Items[0]->Text ="Erreur lors de la copie ...";
&#160;&#160;&#160; &#160;&#160;&#160; delete oldMMF;
&#160;&#160;&#160; &#160;&#160;&#160; return false;
&#160;&#160;&#160; &#160;&#160;&#160; }
&#160;&#160;&#160; delete PatchMMF;
&#160;&#160;&#160; }
else 
{
&#160;&#160;&#160; MessageDlg(PatchMMF->ErrorMessage, mtError, TMsgDlgButtons() << mbOK, 0);
&#160;&#160;&#160; StatusBar1->Panels->Items[0]->Text ="Erreur lors du traitement ...";
&#160;&#160;&#160; return false;
&#160;&#160;&#160; }
return true;
} 

cette méthode est la principale de l'application

on crée tout d'abord un objet PatchMMF de Type TMMF avec en paramètre le nom du fichier Patch si la méthode execute de PatchMMF se termine avec succès on initialise le champ FStreamSize avec la taille du fichier Mapping

on creée ensuite un object oldMMF avec comme paramètre le nom du fichier, la taille,et la variable Boolean si la méthode execute de oldMMF se termine avec succès il reste maintenant a copier le pointeur PatchMMF->PtrMapping dans le pointeur oldMMF->PtrMapping

la zone mappée par OldMMF devient égale alors à PatchMMF le fichier est donc copié il nous reste alors a libérer la mémoire

V-D. Méthode DisplayErrorMessage(AnsiString msgE)

 
Sélectionnez
1.
2.
3.
4.
5.
void __fastcall TForm1::DisplayErrorMessage(AnsiString msgE)
{
MessageDlg(msgE, mtError, TMsgDlgButtons() << mbOK, 0);
StatusBar1->Panels->Items[0]->Text =msgE;
} 

cette méthode est utilisée pour afficher dans une message dialog ainsi que dans la barre de status le message d'erreur

V-E. Méthode ClearFields()

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
void __fastcall TForm1::ClearFields()
{
FPatchFileName="";
FoldFileName="";
edNewFile->Text="";
edOldFile->Text="";
}

la méthode ClearFields réinitialise les variables

V-F. Méthode IsBigger

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
bool __fastcall TForm1::IsBigger(long * size)
{
if (FSizePatchFile>FSizeOldFile)
{
&#160;&#160;&#160; *size= FSizePatchFile-FSizeOldFile;
&#160;&#160;&#160; return true;
&#160;&#160;&#160; }
else 
{
&#160;&#160;&#160; *size=FSizePatchFile;
&#160;&#160;&#160; return false;
&#160;&#160;&#160; }
}

cette méthode teste si le fichier patch est plus grand que le fichier a patcher le paramètre Size est initialisé avec la différence de taille entre les fichiers si la taille du patch est plus petite que le fichier à patcher Size contient alors la taille du fichier Patch

VI. Méthodes Publiées(published)

Les méthodes suivantes sont générées automqtiquement par l'IDE en fonction des actions générées par le composant ActionManager

VI-A. Méthode OpenOlDfileExecute

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
void __fastcall TForm1::OpenOlDfileExecute(TObject *Sender)
{
OpenDialog1->Title="Sélectionner le fichier à patcher";
if (OpenDialog1->Execute())
{
&#160;&#160;&#160; edOldFile->Text=OpenDialog1->FileName;
&#160;&#160;&#160; FoldFileName=OpenDialog1->FileName;
&#160;&#160;&#160; StatusBar1->Panels->Items[0]->Text ="Sélectionner le Patch";
&#160;&#160;&#160; }
}

ouverture de la boite de dialogue de sélection de fichier,initialisation des membres FoldFileName, et du TLabeledEdit edOldFile

VI-B. Méthode OpenPatchNameExecute

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
void __fastcall TForm1::OpenPatchNameExecute(TObject *Sender)
{
OpenDialog1->Title="Sélectionner le Patch";
if (OpenDialog1->Execute())
{
&#160;&#160;&#160; edNewFile->Text=OpenDialog1->FileName;
&#160;&#160;&#160; FPatchFileName=OpenDialog1->FileName;
&#160;&#160;&#160; StatusBar1->Panels->Items[0]->Text ="Presser la touche Make...";
&#160;&#160;&#160; }
}

ouverture de la boite de dialogue de sélection de fichier,initialisation des membres FPatchFileName, et du TLabeledEdit edNewFile

VI-C. Méthode AboutExecute

 
Sélectionnez
1.
2.
3.
4.
void __fastcall TForm1::AboutExecute(TObject *Sender)
{
&#160;&#160;&#160; AboutBox->ShowModal();
&#160;&#160;&#160; }

ouverture de la boite de dialogue A propos de

VI-D. Méthode CloseAppExecute

 
Sélectionnez
1.
2.
3.
4.
void __fastcall TForm1::CloseAppExecute(TObject *Sender)
{
&#160;&#160;&#160; Close();
&#160;&#160;&#160; }

Fermeture de l'application

VI-E. Méthode MakeExecute

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
void __fastcall TForm1::MakeExecute(TObject *Sender)
{
FPatchIsBigger=false;
if (!GetFileSize(FoldFileName,&FSizeOldFile))
{
&#160;&#160;&#160; DisplayErrorMessage("Veuillez choisir un fichier valide");
&#160;&#160;&#160; FPatchFileName="";
&#160;&#160;&#160; FoldFileName="";
&#160;&#160;&#160; return ;
&#160;&#160;&#160; }
if (!GetFileSize(FPatchFileName,&FSizePatchFile))
{
&#160;&#160;&#160; DisplayErrorMessage("Veuillez choisir un fichier valide");
&#160;&#160;&#160; FPatchFileName="";
&#160;&#160;&#160; FoldFileName="";
&#160;&#160;&#160; return ;
&#160;&#160;&#160; }
FPatchIsBigger=IsBigger(&FDiff);
if (PatchFile())
{
&#160;&#160;&#160; AnsiString msg="Le fichier \r\n";
&#160;&#160;&#160; msg+=FoldFileName;
&#160;&#160;&#160; msg+="\r\na été mis a jour avec succès avec le patch suivant\r\n";
&#160;&#160;&#160; msg+=FPatchFileName;
&#160;&#160;&#160; MessageDlg(msg, mtInformation, TMsgDlgButtons() << mbOK, 0);
&#160;&#160;&#160; StatusBar1->Panels->Items[0]->Text ="Opération terminée avec succès...";
&#160;&#160;&#160; ClearFields();
&#160;&#160;&#160; }
} 

cette méthode teste tout d'abord la validité des fichiers,appelle en cas de succès la méthode PatchFile, en retour positif de cette méthode une messageBox est affichée indiquant le résultat de l'opération

VII. Conclusion

l'application proposée est basique il est aisé de l'améliorer en lui rajoutant de nouvelles fonctionalités par exemple

  • Execution par lignes de commandes
  • lecture d'une liste de fichiers a traiter dans un fichier Ini
  • etc..

Voir également mes autres articles :

  • Utilisation des Files Mapping sous C++Builder C++ Builder
  • La technique des PatchFiles C++ Builder
  • Maîtrisez les files d'impression sous windows C++ Builder
  • Travailler avec les Interfaces en C++Builder Partie 1 C++ Builder
  • Composant de gestion des ports d'imprimantes C++ Builder
  • Programme de Test Version ActiveX du composant TDLIoport Delphi

Sans oublier :

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

Voir également mes autres articles :
Utilisation des Files Mapping sous C++Builder C++ Builder
La technique des PatchFiles C++ Builder
Maîtrisez les files d'impression sous windows C++ Builder
Composant de gestion des ports d'imprimantes C++ Builder
Programme de Test Version ActiveX du composant TDLIoport Delphi
Sans oublier :
la FAQ C++ Builder par Geronimo
la FAQ C
la FAQ C++

  

Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2013 DjmSoftware. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.