Accueil
Accueil
Le
Club
Delphi
Kylix
C
C++
Java
J2EE
DotNET
& C#
Visual
Basic
Access
Pascal
Dev
Web
PHP
ASP
XML
UML
SQL
SGBD
Win
Linux
Autres

        


Comment est-ce que je peux lancer un programme externe ?
auteur : Hdd34
Bien que ce ne soit pas généralement une solution à choisir (Turbo Pascal permet de créer des fonctions autonomes, et indépendantes du système d'exploitation utilisé), il peut parfois être nécessaire d'appeler un programme externe. Par exemple, on peut vouloir lancer l'interpréteur de commandes MS-DOS, COMMAND.COM. Pour ce faire, il faut avoir recourt à la procédure Exec(Programme, Parametres), présente dans l'unité Dos, qu'il faudra ajouter à votre clause uses.


Exec('COMMAND.COM', '');


Telle quelle, cette manière de procéder n'a toutefois que peu de chances de réussir. En effet, d'autres éléments sont à prendre en compte.
Pour commencer, votre programme peut définir ses propres vecteurs d'interruption. De fait, en exécutant un programme externe, vous prenez le risque de corrompre ces vecteurs d'interruption. Pour vous prémunir de ceci, il vous suffit d'appeler la procédure SwapVectors avant et après l'appel à Exec. Cette procédure va se charger d'échanger les vecteurs d'interruption par défaut et les vôtres.


SwapVectors; Exec('COMMAND.COM', ''); SwapVectors;


Autre point à prendre considération : la mémoire. En effet, un programme externe a besoin de mémoire pour pouvoir se lancer. Or, par défaut, un programme créé avec Turbo Pascal s'attribue toute la mémoire libre disponible à son lancement, n'en laissant aucune pour un autre programme supplémentaire. La solution la plus simple consiste alors à indiquer à Turbo Pascal de ne réserver qu'une partie de la mémoire libre.
Pour ce faire, il faut utiliser la directive de compilation {$M Stack, MinHeap, MaxHeap}, où MinHeap représente la taille minimale du tas (0 par défaut), MaxHeap la taille maximale du tas (655360 par défaut) et Stack la taille de la pile (16384 par défaut). De fait, si vous n'avez pas modifié les options par défaut de Turbo Pascal (dans Options|Memory sizes...), tout programme est déclaré implicitement avec la directive {$M 16384, 0, 655360}.
Votre programme, comme tous les programmes Dos en mode réel, ne peut accéder qu'aux 640 Ko de mémoire conventionnelle basse, à savoir 655360 octets. Par conséquent, en laissant la valeur par défaut pour MaxHeap, votre programme va tenter d'allouer toute la mémoire disponible.
Si vous désirez laisser de la mémoire pour d'autres programmes, il convient de réduire cette valeur. Il vous faut donc trouver un juste milieu entre la mémoire que vous désirez laisser libre aux autres applications, et la mémoire dont votre programme a besoin (la mémoire dynamique ici, dont vous avez besoin lors de l'utilisation de New ou GetMem).
On peut, par exemple, décider de s'allouer 200 Ko de mémoire, on aura alors :


program Exemple; {$M 16384, 0, 204800} ... begin ... SwapVectors; Exec('COMMAND.COM', '') ; SwapVectors; ... end.


Dernière élément à prendre compte : le succès de notre opération. En effet, il pourrait être intéressant de savoir si le programme que l'on a cherché à lancer s'est bel et bien lancé comme prévu. Pour cela, il suffit de consulter la valeur de la variable DosError au retour du programme externe. Toute valeur différente de zéro indique alors une erreur. Les erreurs les plus courantes sont :


Code d'erreur

Signification

2 Fichier non trouvé
3 Répertoire incorrect
5 Accès refusé
8 Mémoire insuffisante



... SwapVectors; Exec('COMMAND.COM', ''); SwapVectors; if DosError <> 0 then ... ...


Les programmeurs avertis auront toutefois remarqué la gêne que peut occasionner la limitation de la mémoire dans le code source même du programme. En effet, il peut arriver qu'un programme ait besoin de près de 600 Ko de mémoire dans un de ses modules, sans que cela dure toute la durée du programme. Ainsi, lors de l'exécution du programme externe, on pourrait très bien n'avoir besoin à ce moment précis que d'une centaine de Ko de mémoire. De fait, la méthode utilisant la directive $M, bien que préconisée par Borland, manque singulièrement de flexibilité.
Les deux procédures suivantes se chargent d'améliorer considérablement cette flexibilité. Toutefois, non officielles, elles sont à utiliser sous votre propre responsabilité. Celles-ci consistent à réduire la taille du tas au minimum possible (sans perdre vos variables allouées dynamiquement) avant l'exécution du programme externe (HeapShrink), puis à lui redonner sa taille originale au retour du programme externe (HeapExpand). Nous n'expliquerons pas ici plus en détail le code de ces procédures.


program Exemple; {$M 16384, 0, 655360} { On peut allouer toute la mémoire au démarrage } ... procedure HeapShrink; { Réduit le tas au maximum } var RegBx: Word; begin RegBx := MemW[Seg(HeapPtr):Ofs(HeapPtr) + 2] - PrefixSeg; asm mov bx, RegBx mov es, PrefixSeg mov ah, 4Ah int 21h end; end; procedure HeapExpand; { Redonne au tas sa taille originale } var RegBx: Word; begin RegBx := MemW[Seg(HeapEnd):Ofs(HeapEnd) + 2] - PrefixSeg; asm mov bx, RegBx mov es, PrefixSeg mov ah, 4Ah int 21h end; end; ... begin ... HeapShrink; SwapVectors; Exec('COMMAND.COM',''); SwapVectors; HeapExpand; if DosError <> 0 then ... ... end.


Remarque :

  • Nous avons utilisé ici comme programme d'exemple COMMAND.COM. Il s'agit de l'interpréteur de commandes MS-DOS de Dos et de Windows 9x. Toutefois, avec Windows 2000 et XP, si cet interpréteur n'a pas disparu, il a toutefois changé de nom, et se nomme CMD.EXE.
    Par conséquent, si on désire faire un programme capable de fonctionner sur n'importe quel système d'exploitation compatible MS-DOS, il convient de prendre en compte ce problème, en sachant qu'un système d'exploitation différent pourra très bien ne pas appeler son interpréteur de commandes COMMAND.COM ou CMD.EXE.
    Comment faire alors ? C'est simple, le système d'exploitation se doit de définir la variable d'environnement COMSPEC, qui contient le nom de l'interpréteur de commandes. Pour lire une variable d'environnement, il faut se servir de la fonction GetEnv(NomVarEnv). D'où :


Exec(GetEnv('COMSPEC'), '');




Comment vérifier si un fichier existe ?
auteur : Hdd34
Deux manières sont envisageables : soit tenter d'ouvrir le fichier, et vérifier si une erreur s'est produite ; soit faire une recherche disque sur ce fichier.
  • La première méthode demande de s'arrêter sur un point. En effet, s'il s'avère que le fichier que l'on recherche est en lecture seule, il faudra prendre garde à ne tenter une ouverture qu'en lecture seule. Or, par défaut, l'ouverture d'un fichier s'effectue en lecture-écriture. On changera donc les options d'ouverture des fichiers avant la vérification, pour ensuite les rétablir.
    Dernier détail : le périphérique NULL, ayant pour nom 'NULL', peut toujours être ouvert. Or, ce n'est pas un vrai fichier. On l'éliminera donc dans notre code.


function FileExists(const FileName: string); var f: file; OldFileMode: Word; begin OldFileMode := FileMode; { On modifie la méthode d'ouverture : lecture seule } FileMode := 0; {$I-} Assign(f, FileName); Reset(f, 1); { Tentative d'ouverture } Close(f); {$I+} FileMode := OldFileMode; FileExists := (IOResult = 0) and (FileName <> 'NULL'); end;


  • La deuxième méthode a recourir à FindFirst, et donc à l'unité Dos. On prendra garde d'éliminer tout répertoire ou nom de volume de notre recherche.


uses Dos; ... function FileExists(const FileName: string); var S : SearchRec; begin FindFirst(FileName, AnyFile, S); FileExists := (DosError = 0) and (S.Attr and (VolumeID or Directory) = 0); end;


Remarque :

  • Certains compilateurs et systèmes d'exploitation attendent l'utilisation de FindClose une fois l'utilisation de FindFirst terminée. C'est notamment le cas sous Windows 9x et suivants. Toutefois, Turbo Pascal ne connaît pas cette procédure, car inutile sous Dos.


Comment accéder aux noms de fichier longs ?
auteur : Hdd34
Les noms de fichier longs sont apparus avec Windows 95. Turbo Pascal ne sait donc naturellement pas les gérer. Néanmoins, il est possible de créer une gestion pour ceux-ci. Le plus simple reste de faire appel à une unité extérieure. Il en existe plusieurs se chargeant très bien de cette tâche sur Internet. La plus complète est peut-être  Dos70.pas, de Cristi Streng.

téléchargement : Unité Dos70 de Cristi Streng

Comment rechercher un fichier sur le disque ?
auteurs : Hdd34, King Kaiser
Afin de rechercher un fichier sur le disque dur (ou tout autre lecteur, qu'il soit de disquettes ou bien CD-ROM), il faut recourir aux fonctions FindFirst, FindNext et FindClose de l'unité Dos.

Un recherche de fichiers s'effectue toujours en plusieurs étapes :

  • L'initialisation de la recherche
  • La recherche itérative des fichiers correspondant à certains critères
  • La clôture de la recherche
Il est à noter que la dernière étape peut être facultative sur certains compilateurs et/ou systèmes d'exploitation. C'est notamment le cas de Turbo Pascal, qui ne fait pas usage de FindClose (cette procédure n'est alors pas déclarée).

L'initialisation d'une recherche s'effectue avec FindFirst(Path: string; Attr: Word; var S: SearchRec); où :
  • Path correspond au nom de fichier recherché. Les caractères génériques sont autorisés ('*.TXT' par exemple')
  • Attr désigne les attributs de fichier réclamés pour la recherche (voir le tableau ci-dessous)
  • S est la variable enregistrant le résultat de la recherche

Le paramètre Attr peut prendre les valeurs suivantes :

Nom de la constante

Valeur

Attribut

ReadOnly (ou faReadOnly) $01 Fichier en lecture seule
Hidden (ou faHidden) $02 Fichier caché
SysFile (ou faSysFile) $04 Fichier système
VolumeID (ou faVolumeID) $08 Nom de volume d'un disque
Directory (ou faDirectory) $10 Répertoire
Archive (ou faArchive) $20 Fichier archive (attribut par défaut)
AnyFile (ou faAnyFile) $3F Tous les attributs ensembles

Tous ces attributs peuvent bien sûr être combinés afin, par exemple, de rechercher des fichiers système en lecture seule, etc...

La variable S: SearchRec contient le résultat d'une recherche, fichier par fichier. En effet, une recherche de fichiers s'effectue de manière itérative, et on ne peut obtenir qu'un seul fichier à la fois. C'est pourquoi il faut faire appel à FindNext(var S: SearchRec) qui se charge de rechercher le fichier suivant correspondant aux critères de départ.

Lorsque tous les fichiers ont été trouvés, on fait appel à FindClose(var S: SearchRec) pour clôturer la recherche.


Attention ! Il se peut qu'aucun fichier ne soit trouvé. Ainsi, il convient d'utiliser une boucle de type while lors d'une recherche.


Bien entendu, il faut savoir lorsque la recherche est terminée, autrement dit lorsque tous les fichiers correspondant à nos critères ont été trouvés. Pour cela, deux méthodes différentes existent en fonction du compilateur utilisé :
  • Utiliser la variable DosError : elle vaut 0 tant que la recherche ne pose pas de problèmes
  • Si FindFirst et FindNext sont des fonctions, regarder leur valeur de retour. Une valeur nulle indique que la recherche se poursuit


Etudions différents exemples concrets à présent. Tous les exemples suivants s'appuient sur la variable DosError.

  • La recherche de fichiers

    Simulons une recherche de fichiers texte sur le disque, avec affichage de leur nom à l'écran.

program ChercheText; uses Dos; var S: SearchRec; begin { Recherche des fichiers texte du disque C, ayant l'attribut archive } FindFirst('C:\*.TXT', Archive, S); while DosError = 0 do { Tant qu'il n'y a pas d'erreurs... } begin WriteLn(S.Name); { Affichage du nom du fichier } FindNext(S); { Recherche de la prochaine occurence } end; FindClose(S); { Clôture la recherche (ligne à supprimer avec Turbo Pascal) } end.

  • La recherche de répertoires

    Avant de se lancer dans l'exemple, il convient de prendre en compte une remarque importante : certaines fois, les répertoires sont mal rapportés. On préfèrera donc le plus souvent faire une recherche générale (*.*) et tester l'attribut S.Attr directement. De plus, il faut savoir que chaque répertoire contient deux sous-répertoires "fictifs", nommés "." et "..". Le premier représente le répertoire en lui-même, et le deuxième le répertoire parent. Il faut savoir être prudent face à ces répertoires, car si on effectue un programme parcourant toute l'arborescence du disque, il est possible de créer une boucle infinie en examinant sans cesse le répertoire ".".

    L'exemple suivant liste tous les répertoires à la racine du disque. Il se révèle quasiment identique au précédent.


program ListeDir; uses Dos; var S: SearchRec; begin FindFirst('C:\*.*', Directory, S); while DosError = 0 do begin WriteLn(S.Name); FindNext(S); end; FindClose(S); end.

  • Obtenir le nom de volume du disque

    L'exemple suivant lit le nom de volume du disque dur et l'affiche à l'écran.


program VolumeDisque; uses Dos; var S: SearchRec; begin FindFirst('C:\', VolumeID, S); if DosError = 0 then begin WriteLn('C: [', S.Name, ']'); FindClose(S); end; end.


Remarque :
  • Si votre compilateur déclare FindFirst et FindNext comme fonctions, il ne faut plus utiliser DosError. A la place, on utilisera une méthode de ce genre :

... if FindFirst('*.TXT', faArchive, S) = 0 then begin repeat ... until FindNext(S) <> 0; FindClose(S); end; ...
lien : F.A.Q. Delphi : Comment lister les fichiers d'un répertoire ?

Comment créer un répertoire ?
auteur : Hdd34
Pour créer un répertoire, il faut se servir de la procédure MkDir(Path: string) de l'unité Dos :


uses Dos; begin ... MkDir('C:\TEST'); ... end.

Remarque :
  • Si vous désirez utiliser des noms longs, ils vous faut utiliser une unité spécifique

lien : Comment accéder aux noms de fichier longs ?

Comment supprimer un répertoire ?
auteur : Hdd34
Pour supprimer un répertoire, on se sert de la procédure RmDir(S: string) de l'unité Dos :


uses Dos; begin ... RmDir('C:\TEST'); ... end.


Attention ! La procédure RmDir ne peut supprimer qu'un répertoire vide, sinon elle déclenche une erreur. L'exemple suivant implémente une procédure récursive Deltree qui se charge de supprimer tout le contenu du répertoire avant de supprimer le répertoire en lui-même.


uses Dos; procedure Deltree(Path: string); var S: SearchRec; f: file; L: Byte; begin { On fait en sorte que le chemin ne se termine pas par '\' } L := Length(Path); if Path[L] = '\' then Delete(Path, L, 1); { On recherche tous les fichiers possibles } FindFirst(Path + '\*.*', ReadOnly or SysFile or Archive or Directory, S); while DosError = 0 do begin { On c'est un répertoire (sauf . et ..), on le supprime } if S.Attr and Directory <> 0 then begin if (S.Name <> '.') and (S.Name <> '..') then Deltree(Path + '\' + S.Name); end else begin { Sinon, il s'agit d'un fichier, que l'on va supprimer } Assign(f, Path + '\' + S.Name); { On le passe en Archive, sinon, on ne peut pas le supprimer } SetFAttr(f, Archive); { On le supprime } Erase(f); end; { On passe au fichier suivant } FindNext(S); end; { Si vous n'utilisez pas Turbo Pascal, ajoutez la ligne : } { FindClose(S); } { On supprime le répertoire vide } RmDir(Path); end; begin Deltree('C:\TEST'); end.

Comment renommer un fichier ?
auteur : Hdd34
Pour renommer un fichier, servez-vous de la procédure Rename(var f; Name: string) sur un fichier assigné :


var f: file; begin ... Assign(f, 'OLD.TXT'); Rename(f, 'NEW.TXT'); ... end.

Remarque :
  • Si vous renommez un fichier dans un autre répertoire que le répertoire en cours, vous devez en plus du nouveau nom indiquer aussi totalement le chemin d'accès au fichier, sans quoi une erreur se produirait.

    Exemple :

var f: file; begin ... Assign(f, 'C:\TEST\OLD.TXT'); Rename(f, 'C:\TEST\NEW.TXT'); { On précise aussi le répertoire } ... end.

Comment supprimer un fichier ?
auteur : Hdd34
Pour supprimer un fichier, il faut recourir à la procédure Erase(var f), après avoir assigné un fichier à f.


var f: file; begin ... Assign(f, 'TEST.TXT'); Erase(f); ... end.


Attention !

Vous ne devez jamais utiliser Erase sur un fichier ouvert !


Comment copier des fichiers ?
auteur : Hdd34
Pour copier un fichier, il faut se servir des procédures BlockRead et BlockWrite en utilisant tous les paramètres disponibles. La copie s'effectue en plusieurs étapes :

  • Créer le fichier de destination (Rewrite)
  • Ouvrir le fichier source (Reset)
  • Lire le contenu du fichier source (BlockRead) et l'écrire dans le fichier destination (BlockWrite)
  • Fermer les fichiers (Close)
Considérons donc l'exemple suivant. Le fichier source est Source, et le fichier destination Dest. Le tampon de copie est Buffer, d'une taille de 1 Ko et Count le nombre d'octets copiés à chaque passe.

function CopyFile(const SourceFile, DestFile: string): Boolean; var Source, Dest: file; Buffer: array[0..1023] of Byte; Count: Word; OldFileMode: Word; begin { Désactivation des erreurs E/S } {$I-} { Assignation des fichiers } Assign(Source, SourceFile); Assign(Dest, DestFile); { Ouverture en lecture seule de Source et création de Dest } OldFileMode := FileMode; FileMode := 0; Reset(Source, 1); Rewrite(Dest, 1); { Boucle principale de copie, s'arrête quand la fin du fichier est atteinte } repeat { Remplissage du buffer : 1 Ko prévus, Count octets réels } BlockRead(Source, Buffer, SizeOf(Buffer), Count); { Ecriture du contenu du buffer } BlockWrite(Dest, Buffer, Count); until (Count = 0) or (Count <> SizeOf(Buffer)); { Fermeture des fichiers } Close(Source); Close(Dest); { Réactivation des erreurs d'E/S et rétablissement du mode de lecture } FileMode := OldFileMode; {$I+} CopyFile := (IOResult = 0); end;

Attention ! La procédure précédente ne vérifie pas si le fichier de destination existe déjà ou non. Si tel est le cas, alors celui-ci sera écrasé sans que l'utilisateur soit prévenu.

lien : Cherchez de l'aide

Comment déplacer un fichier ?
auteur : Hdd34
Le Pascal ne permet pas de déplacer directement un fichier. Il faut procéder en deux étapes successives :
  • Copier le fichier source vers la destination
  • Supprimer le fichier original

Reportez-vous à la question concernant la copie de fichier pour pouvoir compléter le code source :

uses Dos; function CopyFile(const SourceFile, DestFile: string): Boolean; ... function MoveFile(const FileName, Path: string): Boolean; var S, Dir, FName, Ext: string; f: file; begin S := Path; if S[Length(S)] <> '\' then S := S + '\'; FSplit(FileName, Dir, FName, Ext); S := S + FName + Ext; if CopyFile(FileName, S) then begin Assign(f, FileName); {$I-} Erase(f); {$I+} MoveFile := (IOResult = 0); end else MoveFile := False; end;
lien : Comment copier des fichiers ?

Pourquoi je ne peux pas ouvrir mon fichier alors qu'il existe ?
auteur : Hdd34
Parfois, lorsque l'on tente d'ouvrir un fichier, il se produit une erreur d'accès disque. Cela peut provenir du fait que le fichier est en lecture seule. En effet, par défaut, depuis Dos 3.0, les fichiers sont ouverts en lecture et écriture. Or, un fichier en lecture seule, par définition, ne peut être ouvert qu'en lecture.

Heureusement, il est possible de choisir le mode d'ouverture des fichiers. Pour cela, il faut se servir de la variable FileMode. Les valeurs possibles les plus courantes affectant Reset sont :

Valeur Signification
0 Lecture seule
1 Ecriture seule
2 Lecture et écriture
Par conséquent, avant d'essayer d'ouvrir un fichier en lecture seule, on modifiera la valeur de FileMode. On obtient alors un programme de ce genre :

var f: file; OldFMode: Word; begin OldFMode := FileMode; {$I-} FileMode := 0; { Passage en lecture seule } Assign(f, 'FICHIER.EXT'); Reset(f, 1); ... Close(f); {$I+} FileMode := OldFMode; end;

Comment récupérer le nom de mon application ?
auteur : Hdd34
Il peut parfois être utile de connaître le nom de l'EXE correspondant à votre application. Celui-ci est stocké avec les paramètres de la ligne de commande, et possède l'indice 0. On peut le récupérer avec ParamStr :


begin ... WriteLn('Nom de l''application : ', ParamStr(0)); ... end;

Comment savoir si un disque existe sur le système ?
auteur : Hdd34
Pour savoir si un disque existe, et donc s'il est accessible, on peut tenter de basculer sur ce disque et ensuite vérifier que tout s'est bien passé. Pour ce faire, on utilise les fonctions 0Eh et 19h de l'interruption 21h.
Le code suivant se sépare en plusieurs parties. Tout d'abord, on récupère le lecteur en cours, puis on tente de basculer sur le lecteur désiré et on compare avec la nouvelle valeur du lecteur en cours. Si cette nouvelle valeur diffère du lecteur désiré, alors c'est que ce lecteur n'existe pas et/ou n'est pas accessible. Enfin, on rétablit le lecteur d'origine.


uses Dos; function DriveExists(const Drive: Char): Boolean; var Regs: Registers; CurrentDrive, NewDrive: Byte; begin DriveExists := False; { Test de validité } if not (Upcase(Drive) in ['A'..'Z']) then Exit; { On récupère le lecteur actuel } Regs.ah := $19; MsDos(Regs); CurrentDrive := Regs.al; { On bascule sur le nouveau lecteur } NewDrive := Ord(UpCase(Drive)) - Ord('A'); Regs.ah := $0E; Regs.dl := NewDrive; MsDos(Regs); { On vérifie que tout s'est bien déroulé } Regs.ah := $19; MsDos(Regs); DriveExists := (Regs.al = NewDrive); { On réactive le lecteur précédent } Regs.ah := $0E; Regs.dl := CurrentDrive; MsDos(Regs); end;

        

Consultez les autres F.A.Q's

Les sources présentés sur cette pages sont libre de droits, et vous pouvez les utiliser à votre convenance. Par contre cette page de présentation de ces sources constitue une oeuvre intellectuelle protégée par les droits d'auteurs. Copyright ©2004  Developpez LLC. Tout droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à 3 ans de prison et jusqu'à 300 000 E de dommages et intérets. Cette page est déposée à la SACD.

Vos questions techniques : forum d'entraide Accueil - Publiez vos articles, tutoriels et cours
et rejoignez-nous dans l'équipe de rédaction du club d'entraide des développeurs francophones
Nous contacter - Copyright 2000..2005 www.developpez.com