Le meilleur moyen de lire un fichier binaire ?

Résolu
cs_Squallou Messages postés 249 Date d'inscription mardi 5 août 2003 Statut Membre Dernière intervention 15 juillet 2006 - 28 mai 2006 à 12:45
 nguetho - 30 mai 2006 à 22:13
Salut tout le monde.

J'aimerais ouvrir un fichier pour exploiter son contenu (en octets pas en texte).

J'ai essayé avec un BlockRead dans une boucle mais ça met un peu de temps à charger (à peine deux secondes mais bon... -_-).

J'ai entendu parler de Stream, MemoryStream, FileStream. J'ai cherché de la doc mais j'arrive pas à piger et ça m'a embrouiller.

Admettons que je veuille afficher tous les octets d'un fichier binaire dans un TMemo, que je modifie ce TMemo et que j'enregistre son contenu en écrasant mon fichier.

Quel serait le moyen le plus pratique et rapide ?

Merci d'avance

22 réponses

cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
28 mai 2006 à 18:20
>Squallou:
Il y a 2 cas possibles:

1/Ce que tu veux modifier dans ton fichier de sauvegarde ne changera pas la taille de ce dernier. (j'ai cru comprendre que c'était ton cas).
Dans ce cas-là, pas de problème, si ton fichier s'appelle c:\sauvegarde.dat:
procedure Patch;
var
  Stream:TStream;
const
  Modif:string=#96#20#97;
begin
CopyFile('c:\machin.dat','c:\machin.dat.bak',True); //<-Création d'un backup avant toute modification (si le backup existe déjà il ne sera pas créé)
 Stream:=TFileStream.Create('c:\sauvegarde.dat',fmOpenReadWrite);
  try
Stream.Position:=55;
  Stream.Write(Modif[1],Length(Modif));
  finally
Stream.Destroy;
end;
end;

Ce petit bout de code vas juste modifier les 3 octets à l'offset 55 (c'est à dire 55 octets depuis le début du fichier) par la suite d'octets 96 20 97 (attention ce n'est pas de l'hexa, mais de la base 10). Ceci dit, les modifications sont définitives (cad pas de retour en arrière possible) puisqu'on travaille sur un seul fichier. C'est pour ça qu'avant on écrit 

CopyFile('c:\machin.dat','c:\machin.dat.bak',True)

pour faire un backup. Le block try...finally...end sert à s'assurer que même s'il y a une erreur dans le code, le fichier va bien être refermé après utilisation.

2/Ta modification va changer la taille du fichier (cad tu vas insérer des octets ou en supprimer). Le mieux dans ce cas là est de reconstruire le fichier de A à Z.

procedure Patch;
var
  Stream1,Stream2:TStream;
begin
  Stream1:=TFileStream.Create('c:\test.dat',fmOpenRead); //<-Ancient fichier ouvert en lecture seule
  Stream1.Position:=0; //<-Nouveau fichier ouvert en écriture

try
  Stream2:=TFileStream.Create('c:\testpatched.dat',fmOpenWrite or FMCreate);

  Stream2.CopyFrom(Stream1,10); //<-On remet les 10 premiers octets de l'ancien fichier dans le nouveau

  Stream2.Write('123',3); //<-On écrit les 3 caractères '1' '2' et '3'

Stream1.Position:=34; //<-On se met à la position 34 dans l'ancien fichier
  Stream2.CopyFrom(Stream1,Stream1.Size-34); //<-On écrit tous les octets qui restent dans l'ancient fichier vers le nouveau

finally
  Stream2.Destroy;
  Stream1.Destroy;
end;
end;

Cette fois-ci, ce code crée un 2ème fichier c:\testpatched.dat qui contient les 10 premiers octets de c:\test.dat, puis les 3 caractères '1' '2' et '3' puis toues les octets restants de c:\test.dat à partir de l'octet 34 (c'est juste un exemple débile pour illustrer...).

Tu peux tout à fait faire la même chose avec BlockRead, BlockWrite, Seek... mais ce sera strictement équivalent au niveau performance (un stream est juste l'encapsulation de ces procédures dans une classe) et beaucoup moins joli.

Ceci dit, tout ce que j'ai écrit ici suppose que tes fichier existent et font la bonne taille. Pour raffiner un peu, il faudrait inclure des tests pour vérifier tout ça...
3
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
28 mai 2006 à 19:45
Le TFileStream bien sur ! une fois qu'on le connais je ne vois pas comment pour pourrais revenir au vieille methode du turbo pascal avec assignfile, reset, read write.

ensuite contrairement a ce qu'a marquer Forman :


procedure Patch(const FileName, BackupName : string);
var
  Stream1,Stream2 : TFileStream;
BA : array[0..9] of byte;
N : integer;
begin
if not FileExist(FileName) then exit;
try
  Stream1 := TFileStream.Create(FileName, fmOpenRead);
Stream2 := TFileStream.Create(BackupName, fmCreate);

  Stream1.Seek(0,SoFromBeginning);
Stream2.CopyFrom(Stream1, $0A);

Stream1.Seek($0A, SoFromBeginning);
Stream1.BlockRead(BA,SizeOf(BA));
for N := 0 to 9 do
BA[N] := $00;
Stream2.BlockWrite(BA,SizeOf(BA));

Stream1.Seek($0A+SizeOf(BA)+1,SoFromBeginning);
Stream2.CopyFrom(Stream1,Stream1.Size-Stream1.Position);
finally
  Stream2.Free;
  Stream1.Free;
end;
end;

il ne faut pas appeler Destroy directement, mais Free, Free fait des verifications avant
de liberer l'objet contrairement a Destroy. cela eviteras de possibles erreurs.

ensuite on eviteras de travailler avec "position" du Stream mais on utiliser Seek pour
se positionner dans le fichier.

enfin, quand on crée un objet, on s'assure de declarer le meme type au variable et a la creation.
on eviterat au maximum d'utiliser l'ancetre surtout qu'ici ce n'est pas utile car on utilise
qu'un seul type de stream.
2
Utilisateur anonyme
28 mai 2006 à 13:43
Salut,


Je ne vois pas l'intérer d'ouvrir un fichier et tout réécrire dedans : autant le créer.


Un peu d'aide sur les blocs et les streams :

1-Les blocks

a-Lire


procedure TForm1.Button1Click(Sender: TObject);

var // on commence par définir nos variables

buff : array[1..1024] of Char; // On crée une variable buff de type tableau de 1024 caractères : c'est notre buffer

Fichier : File; // Fichier est le nom de la variable que l'on va utiliser pour notre fichier

lu : integer; // c'est la variable qui va contenir ce que l'on a réellement lu

begin

//Ca c'est facile

if opendialog1.execute then

edit1.text:=opendialog1.filename;  //En cliquant sur le bouton 1
on commence par récupérer le fichier sélectionné dans l'edit1

//première étape : on assigne notre fichier à la variable Fichier

assignfile(Fichier,edit1.text); // au lieu de prendre l'edit on aurait pu écrire opendialog1.filename

//Deuxième étape : on se place au début de notre fichier pour l'initialiser

reset(Fichier,1);

//Tout est pret : il nous plus qu'à lire notre fichier et mettre les données dans notre buffer

//Si le fichier est un peu gros, il ne pourra pas tenir dans un seul buffer. On va donc en utiliser plusieurs

//Au lieu d'utiliser plusieurs buffer, on va en utiliser qu'un seul mais on va réaliser une boucle

Repeat

BlockRead(Fichier, Buff, SizeOf(Buff),lu); // On va lire notre fichier
F, on va mettre ce que l'on lit dans le buffer buff jusqu'à ce que ce
dernier soit plein "sizeof(buff)", puis on met en mémoire dans la
variable lu la quantité totale de ce que l'on a lu de notre fichier

memo1.lines.text := buff; // On met dans notre memo ce qui a été mis en mémoire dans le bloc

until lu =0; // On répète notre boucle jusqu'à ce qu'il y ai plus rien à lire

closefile(Fichier); // Puis on ferme notre fichier

end;


end.

b-Enregistrer


procedure TForm1.Button1Click(Sender: TObject);

begin

if Opendialog1.Execute then

begin

edit1.text:=Opendialog1.filename;

end;

end;


procedure TForm1.Button2Click(Sender: TObject);

begin

if SaveDialog1.Execute then

begin

edit2.text:=savedialog1.filename;

end;

end;


procedure TForm1.Button3Click(Sender: TObject);

var  //Bien là c'est pareil que précédemment

Fichier1, Fichier2 : file;

lu, ecrit:integer;

Buff: array[1..1024] of byte;

begin

AssignFile(Fichier1, opendialog1.FileName); // On assigne notre fichier1 à la variable fichier1

AssignFile(Fichier2, savedialog1.FileName); // On assigne notre fichier2 à la variable fichier2

Reset(Fichier1, 1); // Reset : on se place au début du fichier1 et on l'initialise

Rewrite(Fichier2, 1); // Rewrite : préparation en écriture. Si le fichier existe, on va l'écraser

repeat // Vous savez maintenant pourquoi on fait une boucle

BlockRead(Fichier1, Buff, SizeOf(Buff), lu); // Maintenant on lit les données du fichier1

BlockWrite(Fichier2, Buff, lu, ecrit); // On écrit le contenu du buffer dans le fichier2

until lu = 0 ;

CloseFile(Fichier1); // On ferme le fichier 1

CloseFile(Fichier2); // On ferme le fichier 2

end;

end.

2-Les streams

a-Lecture


procedure TForm1.Button1Click(Sender: TObject);

var // on commence par définir nos variables

FS : TFileStream;

begin


if opendialog1.execute then

edit1.text:=opendialog1.filename;

//Première étape : on crée le stream

FS:=TFileStream.Create(opendialog1.filename,fmOpenRead);

try

//Deuxième étape étape : on charge le contenu du fichier dans le memo

Memo1.Lines.LoadFromStream(FS);

//Troisième étape : on libère le stream

Finally

FS.Free;

end;

end;

end.

b-Enregistrement


procedure TForm1.Button1Click(Sender: TObject);

begin

if Opendialog1.Execute then

begin

edit1.text:=Opendialog1.filename;

end;

end;


procedure TForm1.Button2Click(Sender: TObject);

begin

if SaveDialog1.Execute then

begin

edit2.text:=savedialog1.filename;

end;

end;


procedure TForm1.Button3Click(Sender: TObject);

var

FichierDepart, FichierArrive : TFileStream;


begin

//Première étape : on crée les streams

FichierDepart:=TFileStream.Create(opendialog1.filename,fmOpenRead);

FichierArrive:=TFileStream.Create(savedialog1.filename,fmOpenWrite or fmCreate);

Try

//Deuxième étape : On copie le Fichier de départ dans le fichier d'arrivé

FichierArrive.CopyFrom(FichierDepart,FichierDepart.Size);

Finally

//Troisième étape : on libère les flux

FichierDepart.free;

FichierArrive.free;

end;

end;

end.


Les codes au-dessus font la meme chose si tu compares block-streams.


Lit cela attentivement.


Il est évident que les streams sont plus simples mais attention parfois on est obligés d'utiliser les blocks


@+ et bon courage
0
Utilisateur anonyme
28 mai 2006 à 13:54
Petite remarque : modifier un fichier est le meilleur moyen pour le
bousiller. Dans la mesure ou tu es débutant sache que cela est possible
mais c'est très très très très difficile.


Faisons preuve de pédagogie :

-Quand tu fais un clique droit sur un executable et que tu vas dans
propriété, tu vas voir la date de création et la taille de ton
executable. Tout cela est contenu dans une partie de ton executable (le
header) .


-Tu modifies ton fichier : ton fichier aura une taille différente. A
ton avis si ton fichier est reconnu comme un fichier de 1Mo et qu'en
réalité il  fait 2M0 (suite à ta modif), il va se passer quoi
quand tu vas essayer de l'ouvrir ??. Un gros BEUG .


-Evidemment c'est pareil pour plein d'autres choses comme la date de création.


Donc si tu veux tu communiques ta date d'anniversaire et on t'offrira une paire de rames car là tu vas en avoir besoin . Vu ton niveau sans vouloir te vexer, ce n'est pas à ta portée .


Bon courage et @+
0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
Utilisateur anonyme
28 mai 2006 à 14:01
Pour enregistrer ton mémo dans un fichier :


procedure TForm1.Button1Click(Sender: TObject);

begin

memo1.Lines.savetofile('Mon fichier.extension');

end;
0
cs_Squallou Messages postés 249 Date d'inscription mardi 5 août 2003 Statut Membre Dernière intervention 15 juillet 2006
28 mai 2006 à 14:17
L'intérêt ? heu ben par exemple ouvrir un fichier d'une sauvegarde d'un jeu et le modifier -_-

Le réécrire ? oui en effet, on peut charger son contenu dans un stream, travailler sur ce stream et réécrire un nouveau fichier pourquoi pas :)

Merci pour ton aide
0
Utilisateur anonyme
28 mai 2006 à 14:47
Re,


"L'intérêt ? heu ben par exemple ouvrir un fichier d'une sauvegarde
d'un jeu et le modifier -_-" : pas besoin de passer par un stream ou
block, un savetofile écrasera ton ancien fichier.


"Le réécrire ? Oui pk pas " : tu connais la structure d'un executable
ou d'un bmp, ou d'un  JPeg, ou d'un ce que tu veux ? Quand tu
codes sous Delphi, le compilateur ne se contente pas de transformer tes
caractères en une suite de 0 et 1. Il rajoute plein d'autres choses. Si
tu penses y arriver bien dis toi que tu es plus balaise que tout les
membres de CS réunis . Tu ne pourras
pas modifier ton fichier. Est ce que tu seras capable de dire que telle
partie correspond à telle partie ? Est ce que tu pourras trouver
l'endroit qui fait que ton fichier à telle extension, est ce que tu es
capable de dire tiens cette suite de 0 et 1 correspond exactement au
nom de mon fichier? Est ce que tu pourras dire tiens c'est ici que la
taille de mon programme est inscrite ? Est ce que tu pourras dire tiens
la date de création de mon fichier correspond exactement à cette partie
la de ma suite de 0 et 1 ?

Si tu penses y arriver je te donnerai qu'un conseil : arrette de fumer la moquette de ton salon .


Dans la mesure ou j'ai répondu à tes questions sur les streams, merci
de valider la réponse en cliquant sur le bouton en haut à droite .
0
cs_Squallou Messages postés 249 Date d'inscription mardi 5 août 2003 Statut Membre Dernière intervention 15 juillet 2006
28 mai 2006 à 14:56
non mais c'est un fichier qui contient les textes d'un jeu. En modifiant le fichier avec un éditeur hexa on modifie le texte. Beaucoup de gens le font pour ce jeu. Je voulais juste faire un programme qui simplifiait et rendait ça plus convivial. On connait la structure du fichier en plus.

Jsuis pas totalement newbie non plus. Je demandais juste conseil sur la méthode la + rapide pour lire un fichier binaire...
0
cs_Squallou Messages postés 249 Date d'inscription mardi 5 août 2003 Statut Membre Dernière intervention 15 juillet 2006
28 mai 2006 à 15:41
Bon pour simplifier j'aimerais savoir s'il est possible de charger un fichier en tant qu'octets dans un Stream. Du genre un fichier binaire contenant les octets "96 20 97" qui sera chargé dans un Stream qui contiendra alors "96 20 97".

Pour ensuite modifier un par un les octets que je veux dans mon Stream.

Et enfin écrire tout mon Stream dans un (nouveau) fichier binaire.
0
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
28 mai 2006 à 18:21
oups pas vu la réponse de francky désolé       :-(
0
cs_Squallou Messages postés 249 Date d'inscription mardi 5 août 2003 Statut Membre Dernière intervention 15 juillet 2006
28 mai 2006 à 19:04
la plupart du temps ça peut changer la taille mais ce n'est pas du tout un problème vu le type du fichier. Avant le texte qui nous interesse on a des octets qui nous renseignent justement sur la taille du texte suivant ! Pratique :)

Merci pour ton exemple Forman. Ca me parait bcp plus clair :)
0
cs_Squallou Messages postés 249 Date d'inscription mardi 5 août 2003 Statut Membre Dernière intervention 15 juillet 2006
28 mai 2006 à 19:07
Sinon petite question : c'est plus rapide d'utiliser un FileStream ou d'utiliser BlockRead pour remplir un tableau de bytes ?

Je pense le FileStream mais j'aimerais être sûr ^^'
0
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
28 mai 2006 à 19:31
C'est pareil.
TFileStream utilise ReadFile (une fonction API windows) et BlockRead aussi (voir System.pas)
TFileStream est juste beaucoup plus pratique et élégant...
0
Utilisateur anonyme
28 mai 2006 à 20:52
*Petite remarque : "TFileStream n'utilise pas de mémoires à la
différence des blocks". Personnellement je pense que les Streams sont
plus rapides dans la mesure ou il y a un phénomène "d'optimisation".


*Forman je tiens, si tu me le permets , à signaler en rouge que : modifier un fichier n'est pas aussi simple que cela. 
Il ne faut pas oublier qu'un fichier contient plusieurs parties et
qu'il faut savoir à partir de quel octet on peut faire la modification .
Si tu prends un executable et que tu tapes en plein dans le "Header",
ben tu vas voir ce qu'il va se passer. Il faut arriver à identifier qui
est qui afin de savoir ce que l'on peut modifier


*En ce qui concerne la taille du fichier : fais une source contenant juste la procedure suivante :

Prodecude TForm1.Button1Click(Sender : TObject);

Begin

Edit1.text:='Salut à tous';

end;

end.


Compile le avec Delphi. Modifier juste la partie 'Salut à tous' en
'Salut à toi' avec vos méthodes. Relance ton executable et tu va voir
ce qu'il va se passer. On ne peut pas modifier un fichier comme ca
!!!!!!!!!!!!!!!!!!!


Le seul exemple sur CS pour modifier la valeur d'un string dans un
executable (c'est tres limitatif de surcrois) est d'un tel niveau que
meme DelphiProg avait du mal à comprendre le source de ce membre. Je ne
veux pas parler de cela 107 ans : Pour résumer, on ne peut pas modifier
un fichier comme ca. Il faut recréer toute la structure du fichier.


@+
0
cs_Squallou Messages postés 249 Date d'inscription mardi 5 août 2003 Statut Membre Dernière intervention 15 juillet 2006
28 mai 2006 à 20:53
Merci beaucoup pour votre aide :) C'est pigé ;)
0
cs_Squallou Messages postés 249 Date d'inscription mardi 5 août 2003 Statut Membre Dernière intervention 15 juillet 2006
28 mai 2006 à 20:57
Stop avec l'exe ! On sait qu'un exe ne se modifie pas comme ça ! Il faut conserver la taille et parfois même si l'exe fais un controle d'intégrité style md5 ou autre ça va pas. On le sait ok !

Mais ce n'est pas pour un exe que je demande ! ça a rien de grave de modifier mon fichier ! Il est tellement simpliste qu'il sagirait presque d'un fichier séquentiel style *.txt !

Mais merci d'avoir précisé 5 fois inutilement qu'il ne fallait pas modifier un exe :)
0
Utilisateur anonyme
28 mai 2006 à 21:09
Squallou, ce n'est pas inutile. Si toi tu le sais tout le monde n'est pas comme toi . Si je l'ai précisé c'est que les réponses pouvaient laisser entendre que.


 De plus ce que j'ai dis pour un exe n'est pas spécifique à un exe
(mais est vrai pour tout type de fichier. J'ai pris un fichier exe
comme exemple car c'est plus parlant pour tout le monde) : il en va de
meme pour un fichier .com, .bat, .jpg,. bmp,.ico ect ect. Le seul cas
de figure ou on peut le faire est un fichier texte. Et il est était
important de bien le souligner.
0
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
28 mai 2006 à 21:20
>francky: lol je sais que modifier un exe n'importe comment peut poser problème, merci. Ceci dit quel est le rapport avec le sujet? Je ne me souviens pas que Squallou ait parlé de ça?

Contrairement à ce qu'a marqué f0xi:
1/les try...finally se mettent après avoir créé la resource à libérer (sinon, que se passe-t-il si Stream2 ne peut pas être créé par exemple car il y a eu un problème avec le fichier? l'instance n'a pas été créée, et tu vas pourtant la détruire dans ton finally...end!).
2/Free ne fait rien de plus que tester si l'instance n'est pas nulle avant d'appeler le destructeur: (copié-collé de Borland)
procedure TObject.Free;
begin
  if Self <> nil then
    Destroy;
end;

Ca n'a aucun intérêt de l'utiliser si tu ne fais pas Stream1:=nil ensuite. En effet, si tu fais 2 fois de suite Stream1.Free, tu auras quand même une erreur (tu peux essayer avant de me dire que j'ai tort).

Une bonne alternative est FreeAndNil qui fait les 2 à la fois.

De plus, rien ne te dit que les variables locales de ta fonction sont
initialisées à nil avant l'appel de la fonction (Borlan là encore est
très clair, les seules variables initialisées sont les interfaces, les
tableaux dynamiques et les chaînes). Donc il se peut que ton instance
Stream2 contienne un pointeur aléatoire au début de ta fonction. Donc là encore, un TObject.Free ne sert à rien. Plutôt que de se reposer sur une sécurité illusoire, il faut mieux structurer son code (voir plus bas)


3/SetPosition appelle la méthode Seek, donc je ne vois pas ce qu'il y a de mal à utiliser la propriété Position qui est plus parlante pour un débutant qui veut comprendre les streams. Le gain de performance sera de toute façon purement négligeable.

4/Enfin, je ne vois pas où est le problème d'utiliser le nom de la classe ancêtre. C'est absolument équivalent au point de vue rapidité (dans les 2 cas il y a recherche dans la VMT des méthodes virtuelles) et si je peux me permettre, utiliser le code avec l'ancètre est plus pédagogique et permet du code réexploitable.

Voilà un code plus rigoureux qui va passionner ton côté maniaque f0xi:
procedure Patch(const FileName, BackupName : string);
var
  Stream1,Stream2 : TFileStream;
BA : array[0..9] of byte;
N : integer;
begin
if not FileExist(FileName) then Exit;
Stream1 := TFileStream.Create(FileName, fmOpenRead);
try
Stream2 := TFileStream.Create(BackupName, fmCreate or fmOpenWrite);
try

  Stream1.Seek(0,SoFromBeginning);
Stream2.CopyFrom(Stream1, $0A);

Stream1.Seek($0A, SoFromBeginning);
Stream1.BlockRead(BA,SizeOf(BA));
for N := 0 to 9 do
BA[N] := $00;
Stream2.BlockWrite(BA,SizeOf(BA));

Stream1.Seek($0A+SizeOf(BA)+1,SoFromBeginning);
Stream2.CopyFrom(Stream1,Stream1.Size-Stream1.Position);
finally
  FreeAndNil(Stream2);
end
finally
  FreeAndNil(Stream1);
end;
end;
0
f0xi Messages postés 4205 Date d'inscription samedi 16 octobre 2004 Statut Modérateur Dernière intervention 12 mars 2022 35
29 mai 2006 à 11:06
aaah forman, on vas etre copain je le sens (sans ironie) tu palieras a mon manque de Florenth ^^

quoting borland :

"Néanmoins, pour détruire un objet, vous devez toujours appeler la méthode Free (également héritée de TObject) car Free cherche une référence nil avant d'appeler Destroy."

soit on a pas regarder le meme passage de l'aide, soit nos fichiers d'aide sont different, soit d'une version a l'autre de delphi FREE n'a pas le meme resultat.

a qui se fier ?
a celui qui dit qui faut tester, bien donc testons cela :






provoque une erreur :








procedure TForm1.FormCreate(Sender: TObject);

var T : TStringlist;

begin

  T.Free;





  T.Destroy;
  FreeAndNil(T);




end;

-----

var T : TStringlist = nil;
procedure TForm1.FormCreate(Sender: TObject);
begin
  T.Destroy;
end;











ne provoque pas d'erreur :








procedure TForm1.FormCreate(Sender: TObject);

var T : TStringlist;

begin
  T := nil;
  T.Free;

  T := TStringlist.create;
  T.Free;

end;

-----

var T : TStringlist = nil;
procedure TForm1.FormCreate(Sender: TObject);
begin
  T.Free;
end;

ce qui voudrais dire que l'on devrais toujours initialiser les objets a NIL ... ce qui permettrais de ne pas avoir d'erreur avec Assigned ou Free ou FreeAndNil ...
bizarrement ... aucunes autre methodes ne fournis un moyen de control sur.
meme pas une possibilitée de faire un truc du genre :

if T <> INVALID_INSTANCE_POINTER then ...

ou encore une fonction ReallyAssigned(T)

par contre, je suis tout a fait d'accord, les Creates c'est hors des Try qu'il faut les mettres.
l'inconvegniant de repondre au questions quand on viens de se lever (se lever, pas de se reveiller)...

n'empeche ... je suis toujours etonné de voir qu'il n'y ai pas de methode pour detecter un pointeur erroné.
0
cs_Forman Messages postés 600 Date d'inscription samedi 8 juin 2002 Statut Membre Dernière intervention 6 avril 2010 1
29 mai 2006 à 15:13
J'ai effectivement lu le passage de l'aide auquel tu fais référence
Mais j'ai vite abandonné les Free après m'être rendu compte que ça ne permettait pas justement de détecter les pointeurs invalides.
Faire une fonction ReallyAssigned(T) ne me parait guère possible, étant donné que 

1/ca prend du temps de lire dans une table les espaces de mémoire allouée (c'est d'ailleurs ce qui fait la lenteur des allocations dynamiques)

2/Les blocs alloués sont plus grands que ce que tu demandes en général (si tu fais GetMem(1) tu auras dans la plupart des cas un bloc qui fait par exemple 32768 octets). Donc, lire après la fin de ton bloc à toi serait considéré comme valide, alors que non... Et tout système plus précis consommerait dans le pire des cas autant de mémoire que la mémoire allouée!!!

3/Il faut en plus prendre en compte l'allocation statique. Si tu écris var x:Integer implicitement tu fais allouer de la mémoire (4 octets en l'occurence). Cette mémoire n'est pas allouée par ton programme, mais par le loader. Ca devient un peu compliqué, mais en gros ton code est segmenté une fois compilé en plusieurs sections qui contiennent par exemple du code, des constantes initialisées et autre. Les différentes sections sont décrites dans l'header du fichier .exe, et le loader (de Microsoft si on est sous Windows) alloue la mémoire désirée dans le mode désiré (lecture, écriture, exécution) en fonction de ce que contient l'header du programme. Or, déjà il faut prendre en compte que ton processus n'est pas toujours chargé en mémoire à l'adresse que tu spécifies (il y a alors relocalisation des adresses de l'allocation statique). Pour prendre en compte cette mémoire statique dans un gestionnaire il faudrait faire une grosse partie de code assez lente qui calculerait tout ça. Et en plus, il faudrait que le gestionnaire fasse la différence entre la mémoire qu'il alloue pour lui-même et la mémoire allouée par les autres parties du code!!!

Bref, ça me parait difficilement faisable. Il y a une api windows (je ne me souviens plus de son nom) qui en théorie te dit si une adresse mémoire est accessible en lecture ou écriture. Mais si tu lis msdn , il écrivent noir sur blanc que leur fonction est écrite en utilisant un try...except. C'est à dire, ils essaient de lire ou d'écrire à l'endroit que tu demandes, et s'il n'y a pas d'exception ils te disent True, sinon False!!!

Je crois d'ailleurs que cette fonction mérite le trophée de la plus pure aburdité windowesque! (la médaille d'argent revenant à
TryEnterCriticalSection)
0
Rejoignez-nous