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
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 :
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 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)▲
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()▲
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 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()▲
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
{
MessageDlg
(
oldMMF->
ErrorMessage, mtError, TMsgDlgButtons
(
) <<
mbOK, 0
);
StatusBar1->
Panels->
Items[0
]->
Text =
"
Erreur lors de la copie ...
"
;
delete oldMMF;
return
false
;
}
delete PatchMMF;
}
else
{
MessageDlg
(
PatchMMF->
ErrorMessage, mtError, TMsgDlgButtons
(
) <<
mbOK, 0
);
StatusBar1->
Panels->
Items[0
]->
Text =
"
Erreur lors du traitement ...
"
;
return
false
;
}
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 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)▲
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 un message dialog ainsi que dans la barre de status le message d'erreur.
V-E. Méthode ClearFields()▲
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▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
bool __fastcall TForm1::IsBigger
(
long
*
size)
{
if
(
FSizePatchFile>
FSizeOldFile)
{
*
size=
FSizePatchFile-
FSizeOldFile;
return
true
;
}
else
{
*
size=
FSizePatchFile;
return
false
;
}
}
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▲
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
(
))
{
edOldFile->
Text=
OpenDialog1->
FileName;
FoldFileName=
OpenDialog1->
FileName;
StatusBar1->
Panels->
Items[0
]->
Text =
"
Sélectionner le Patch
"
;
}
}
Ouverture de la boite de dialogue de sélection de fichiers, initialisation des membres FoldFileName, et du TLabeledEdit edOldFile.
VI-B. Méthode OpenPatchNameExecute▲
2.
3.
4.
5.
6.
7.
8.
9.
10.
void
__fastcall TForm1::OpenPatchNameExecute
(
TObject *
Sender)
{
OpenDialog1->
Title=
"
Sélectionner le Patch
"
;
if
(
OpenDialog1->
Execute
(
))
{
edNewFile->
Text=
OpenDialog1->
FileName;
FPatchFileName=
OpenDialog1->
FileName;
StatusBar1->
Panels->
Items[0
]->
Text =
"
Presser la touche Make...
"
;
}
}
Ouverture de la boite de dialogue de sélection de fichiers, initialisation des membres FPatchFileName et du TLabeledEdit edNewFile.
VI-C. Méthode AboutExecute▲
2.
3.
4.
void
__fastcall TForm1::AboutExecute
(
TObject *
Sender)
{
AboutBox->
ShowModal
(
);
}
Ouverture de la boite de dialogue « À propos de ».
VI-D. Méthode CloseAppExecute▲
2.
3.
4.
void
__fastcall TForm1::CloseAppExecute
(
TObject *
Sender)
{
Close
(
);
}
Fermeture de l'application.
VI-E. Méthode MakeExecute▲
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))
{
DisplayErrorMessage
(
"
Veuillez choisir un fichier valide
"
);
FPatchFileName=
""
;
FoldFileName=
""
;
return
;
}
if
(!
GetFileSize
(
FPatchFileName,&
FSizePatchFile))
{
DisplayErrorMessage
(
"
Veuillez choisir un fichier valide
"
);
FPatchFileName=
""
;
FoldFileName=
""
;
return
;
}
FPatchIsBigger=
IsBigger
(&
FDiff);
if
(
PatchFile
(
))
{
AnsiString msg=
"
Le fichier
\r\n
"
;
msg+=
FoldFileName;
msg+=
"
\r\n
a été mis a jour avec succès avec le patch suivant
\r\n
"
;
msg+=
FPatchFileName;
MessageDlg
(
msg, mtInformation, TMsgDlgButtons
(
) <<
mbOK, 0
);
StatusBar1->
Panels->
Items[0
]->
Text =
"
Opération terminée avec succès...
"
;
ClearFields
(
);
}
}
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 :