Technique : les Patch Files sous C++ Builder

DjmSoftware

Ce tutoriel démontre une des techniques utilisées pour mettre à jour des fichiers exécutables à l'aide des File Mapping avec un exemple d'application.

Sources du Programme disponible ici. ♪

- Titre : Technique : les Patch Files sous C++ Builder

- Auteur : DjmSoftware

- Parution : 21 août 2003

- Public visé :

- Licence : 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.

Sommaire

- I. Programme d'exemple

- I-A. Limitation

- II. Définition du GUI

- II-A. définition de la forme principale

- II-B. ImageList

- II-C. ActionsList

- II-D. ActionToolBar

- II-E. Menu Principal

- II-F. Boutons divers

- III. Classe TMMF

- III-A. Modification de la classe TMMF

- III-B. Modification de la classe TMMF

- IV. Classe Tform

- V. Explications sur les méthodes privées

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

- V-B. Méthode AdjustFileSize()

- V-C. Méthode PatchFile()

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

- V-E. Méthode ClearFields()

- V-F. Méthode IsBigger

- VI. Méthodes publiées (published)

- VI-A. Méthode OpenOlDfileExecute

- VI-B. Méthode OpenPatchNameExecute

- VI-C. Méthode AboutExecute

- VI-D. Méthode CloseAppExecute

- VI-E. Méthode MakeExecute

- VII. Conclusion

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 sont utilisées certaines fonctionnalités VCL qui rendent la partie GUI non portable sur un autre compilateur, l'adaptation du code est par contre possible.

II - Définition du GUI

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 quatre images dans le répertoire fichiers communs de Borland comme dans la copie d'écran ci-dessous :

II-C - ActionsList

Création des actions

Ouvrir le composant ActionManager1.

Touche droite de souris sur le composant, cliquer sur Personnaliser.

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 :

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 :

Dans la vue arborescente des objets, placer le curseur de la souris sur ActionBars, clic touche droite de la souris sur ajouter un élément dans l'ActionToolbar nouvellement créé, placer le curseur de la souris sur l'élément Items puis créer cinq nouveaux éléments les noms donnés 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 l'image suivante :

Donner aux Items Actions les valeurs suivantes :

Item

Action

OpenFile1

OpenOlDfile

OpenPatchFile1

OpenPatchName

Make1

Make

Close1

Close

About1

About

II-F - Boutons divers

Donner aux actions de boutons 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 https://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 fonctionnalité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 taille 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

1. bool __fastcall TMMF::Execute()

2. {

3. try

4. {

5. GetFile();

6. GetSizeofFile();

7. if (FMapFileOffset==0)

8. FMapFileOffset=FFileSize;

9. if (FBigger)

10. FMapFileSizeLow=FFileSize+FMapFileOffset;

11. else

12. FMapFileSizeLow=FMapFileOffset;

13. FMapFileSizeHigh=FFileSizeHigh;

14. GetMapping();

15. GetPtrAdresse();

16. }

17. catch (EOSError &E)

18. {

19. FErrorMsg=E.Message;

20. return false;

21. }

22. return true;

23. }

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 :

1. void __fastcall TMMF::GetMapping()

2. {

3. FFileMap=CreateFileMapping(

4. FFileHandle, // handle to file to map

5. NULL, // optional security attributes

6. PAGE_READWRITE, // protection for mapping object

7. FMapFileSizeHigh, // high-order 32 bits of object size

8. FMapFileSizeLow, // low-order 32 bits of object size

9. NULL);

10. if (!FFileMap)

11. RaiseLastOSError();

13. }

IV - Classe Tform

Déclaration de la classe les champs suivants 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 taille du fichier à Patcher

champ

FSizePatchFile

private

contient le taille du fichier Patch

Champ

FDiff

private

Contient la différence de taille entre les deux 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 - Explications sur les méthodes privées

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

1. bool __fastcall TForm1::GetFileSize(AnsiString FileName,long * Size)

2. {

3. long FileSize;

4. int FHandle=FileOpen(FileName,fmOpenRead);

5. if (FHandle!=-1)

6. {

7. FileSize=FileSeek(FHandle,0,2);

8. FileClose(FHandle);

9. *Size= FileSize;

10. return true;

11. }

12. else return false;

13. }

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()

1. void __fastcall TForm1::AdjustFileSize()

2. {

3. HANDLE FFileHandle;

4. FFileHandle=CreateFile(

5. FoldFileName.c_str(), // pointer to name of the file

6. GENERIC_READ|GENERIC_WRITE, // access (read-write) mode

7. FILE_SHARE_READ|FILE_SHARE_WRITE,// share mode

8. NULL, // pointer to security attributes

9. OPEN_EXISTING, // how to create

10. FILE_ATTRIBUTE_NORMAL, // file attributes

11. NULL);

13. SetFilePointer(

14. FFileHandle,

15. FStreamSize,

16. NULL,

17. FILE_BEGIN);

19. SetEndOfFile(FFileHandle);

20. CloseHandle(FFileHandle);

21. }

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 SetFilePointer en lui indiquant où placer le pointeur de fin de fichier(EOF). On appelle ensuiteSetEndOfFile qui va ajuster le fichier à sa nouvelle taille.

V-C - Méthode PatchFile()

1. bool __fastcall TForm1::PatchFile()

2. {

3. TMMF *oldMMF,*PatchMMF;

4. PatchMMF= new TMMF(FPatchFileName);

5. if (PatchMMF->Execute())

6. {

7. FStreamSize=PatchMMF->FileSize;

8. oldMMF= new TMMF(FoldFileName,FDiff,FPatchIsBigger);

9. if (oldMMF->Execute())

10. {

11. memcpy(oldMMF->PtrMapping,PatchMMF->PtrMapping,FStreamSize);

12. delete oldMMF;

13. if (!FPatchIsBigger)AdjustFileSize();

14. }

15. else

16. {

17. MessageDlg(oldMMF->ErrorMessage, mtError, TMsgDlgButtons() << mbOK, 0);

18. StatusBar1->Panels->Items[0]->Text ="Erreur lors de la copie ...";

19. delete oldMMF;

20. return false;

21. }

22. delete PatchMMF;

23. }

24. else

25. {

26. MessageDlg(PatchMMF->ErrorMessage, mtError, TMsgDlgButtons() << mbOK, 0);

27. StatusBar1->Panels->Items[0]->Text ="Erreur lors du traitement ...";

28. return false;

29. }

30. return true;

31. }

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 créé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 à 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 à libérer la mémoire.

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

1. void __fastcall TForm1::DisplayErrorMessage(AnsiString msgE)

2. {

3. MessageDlg(msgE, mtError, TMsgDlgButtons() << mbOK, 0);

4. StatusBar1->Panels->Items[0]->Text =msgE;

5. }

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

V-E - Méthode ClearFields()

1. void __fastcall TForm1::ClearFields()

2. {

3. FPatchFileName="";

4. FoldFileName="";

5. edNewFile->Text="";

6. edOldFile->Text="";

7. }

La méthode ClearFields réinitialise les variables.

V-F - Méthode IsBigger

1. bool __fastcall TForm1::IsBigger(long * size)

2. {

3. if (FSizePatchFile>FSizeOldFile)

4. {

5. *size= FSizePatchFile-FSizeOldFile;

6. return true;

7. }

8. else

9. {

10. *size=FSizePatchFile;

11. return false;

12. }

13. }

Cette méthode teste si le fichier patch est plus grand que le fichier à 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 automatiquement par l'IDE en fonction des actions générées par le composant ActionManager.

VI-A - Méthode OpenOlDfileExecute

1. void __fastcall TForm1::OpenOlDfileExecute(TObject *Sender)

2. {

3. OpenDialog1->Title="Sélectionner le fichier à patcher";

4. if (OpenDialog1->Execute())

5. {

6. edOldFile->Text=OpenDialog1->FileName;

7. FoldFileName=OpenDialog1->FileName;

8. StatusBar1->Panels->Items[0]->Text ="Sélectionner le Patch";

9. }

10. }

Ouverture de la boite de dialogue de sélection de fichiers, initialisation des membres FoldFileName, et du TLabeledEdit edOldFile.

VI-B - Méthode OpenPatchNameExecute

1. void __fastcall TForm1::OpenPatchNameExecute(TObject *Sender)

2. {

3. OpenDialog1->Title="Sélectionner le Patch";

4. if (OpenDialog1->Execute())

5. {

6. edNewFile->Text=OpenDialog1->FileName;

7. FPatchFileName=OpenDialog1->FileName;

8. StatusBar1->Panels->Items[0]->Text ="Presser la touche Make...";

9. }

10. }

Ouverture de la boite de dialogue de sélection de fichiers, initialisation des membres FPatchFileName et du TLabeledEdit edNewFile.

VI-C - Méthode AboutExecute

1. void __fastcall TForm1::AboutExecute(TObject *Sender)

2. {

3. AboutBox->ShowModal();

4. }

Ouverture de la boite de dialogue « À propos de ».

VI-D - Méthode CloseAppExecute

1. void __fastcall TForm1::CloseAppExecute(TObject *Sender)

2. {

3. Close();

4. }

Fermeture de l'application.

VI-E - Méthode MakeExecute

1. void __fastcall TForm1::MakeExecute(TObject *Sender)

2. {

3. FPatchIsBigger=false;

4. if (!GetFileSize(FoldFileName,&FSizeOldFile))

5. {

6. DisplayErrorMessage("Veuillez choisir un fichier valide");

7. FPatchFileName="";

8. FoldFileName="";

9. return ;

10. }

11. if (!GetFileSize(FPatchFileName,&FSizePatchFile))

12. {

13. DisplayErrorMessage("Veuillez choisir un fichier valide");

14. FPatchFileName="";

15. FoldFileName="";

16. return ;

17. }

18. FPatchIsBigger=IsBigger(&FDiff);

19. if (PatchFile())

20. {

21. AnsiString msg="Le fichier \r\n";

22. msg+=FoldFileName;

23. msg+="\r\na été mis a jour avec succès avec le patch suivant\r\n";

24. msg+=FPatchFileName;

25. MessageDlg(msg, mtInformation, TMsgDlgButtons() << mbOK, 0);

26. StatusBar1->Panels->Items[0]->Text ="Opération terminée avec succès...";

27. ClearFields();

28. }

29. }

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 fonctionnalités par exemple :

- exécution par lignes de commande ;

- lecture d'une liste de fichiers à 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 :

- la FAQ C++ Builder par Geronimo ;

- la FAQ C ;

- la FAQ C++.