IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

API, COM et SDKs Delphi Discussion :

Comment extraire un fichier depuis un CD avec secteurs illisibles ?


Sujet :

API, COM et SDKs Delphi

  1. #1
    Nouveau membre du Club
    Inscrit en
    Novembre 2004
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Novembre 2004
    Messages : 50
    Points : 25
    Points
    25
    Par défaut Comment extraire un fichier depuis un CD avec secteurs illisibles ?
    Bonjour,

    Je dispose d'un cd dont je cherche à extraire les fichiers.
    Malheureusement l'un de ces fichiers possède une zone avec des secteurs illisibles.

    Comment puis-je copier ce fichier sachant que l'explorer de windows commence la copie, arrive sur la zone illisible, et plante.

    J'ai essayé avec la fonction FileRead, du début à la fin du fichier, mais mon essai ne fut pas concluant.
    cette fonction a le même problème que explorer.Exe, arrivée sur la zone illisible, le lecteur dvd tente de lire la zone à l'infinie. Seule solution : éjecter le cd et le remettre poru continuer la copie secteur par secteur (cette solution ne me convient pas).
    J'ai essayé de mettre par la suite le code avec FileRead dans un thread (pour ne pas planter tout le système). Malheureusement le résultat est le même que précédemment.

    Après quelque recherche, j'ai découvert dans l'api de windows, la fonction ReadFile, possédant une lecture asynchrone.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    37
    38
    39
    type
      TBuffer = Array[0..2047] of Byte;
    var
      Buffer : TBuffer;  // buffer utilisé pour la copie
      SrcHandle, DestHandle : THandle;  // handle
      Overlapped : POverlapped; 
      Pos : integer;  // position du curseur sur le fichier source en coursde lecture
      ReadSuccess : boolean; // indique si la lecture s'est bien déroulée
      ReadCount : integer;
     
     
    SrcHandle := CreateFile('E:\missions.dat', GENERIC_READ, 0, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED or FILE_FLAG_NO_BUFFERING, 0); // ouvre le fichier source
    DestHandle := CreateFile('C:\temp\test.test', fmCreate or fmOpenWrite); // crée le fichier cible
     
    Pos := 0;
    FileSeek(SrcHandle, Pos, 0); // Se positionne au début du fichier source
     
    New(Overlapped); // Création de la structure Overlapped
    Overlapped.Offset :=0;
    Overlapped.OffsetHigh := 0;
    Overlapped.hEvent := 0;
     
    ReadSuccess := ReadFile(SrcHandle, Buffer, 2048, ReadCount, Overlapped); // lecture
    if not ReadSuccess then Begin // la lecture ne se déroule jamais correctement au 1er coup. on cherche la raison
      case GetLastError of
        ERROR_HANDLE_EOF :  // fin du fichier
        begin
          // placer code de fin
        end;
     
        ERROR_IO_PENDING :  // fichier en cours de lecture
        begin
          ReadSuccess := GetOverlappedResult(SrcHandle, Overlapped^, readCount, FALSE);  // teste si la lecture s'est bien déroulée.
          if ReadSuccess then Begin
            FileWrite(DestHandle, Buffer, 2048);  // écriture du buffer dans le fichier
          End;
        End;
      End;
    End;
    Le code ci-dessus marche très bien, mais pour la lecture des 2048 premiers octets du fichiers.
    impossible de faire un fileseek (ou SetFilePointer) pour me déplacer dessus, je lis toujours les mêmes 2048 premiers octets du fichier.

    Si quelqu'un a une piste pour m'aider, car je ne maitrise vraiment pas la lecture asynchrone.
    Merci

    Bertrand

  2. #2
    Expert confirmé
    Avatar de Sub0
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2002
    Messages
    3 573
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2002
    Messages : 3 573
    Points : 4 219
    Points
    4 219
    Par défaut
    Salut!

    Je ne sais pas si ça correspond à ta recherche...
    J'ai réalisé une fonction de copie intégrant le fameux dialogue "Abandon, Ignorer, Recommencer..."
    En effet, Win annule entièrement la copie lorsqu'une erreur est rencontrée.

    http://sub0.developpez.com/delphi/mycopy.zip

    ps: Je vais améliorer ce soir cette fonction; "Ignorer" ignorera jusqu'à ne plus trouver d'erreurs...

    à+

  3. #3
    Nouveau membre du Club
    Inscrit en
    Novembre 2004
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Novembre 2004
    Messages : 50
    Points : 25
    Points
    25
    Par défaut
    J'ai regarder rapidement ton code.
    Il utilise la fonction "BlockRead".
    Je vais tester celle-ci afin de voir si elle s'execute tant qu'elle a pas lu complètement le block, ou si elle se termine au bout d'un timeout.

    Mon problème provient de la protection du cd, et dans un cas plus standard d'une rayure\entaille sur le cd.
    la zone est physiquement détruite. La commande windows essaye d'y accéder, et tente à l'infini d'accéder à cette donnée.
    le seul moyen d'arreter l'accès permanent au secteur de donnée consiste à éjecter le tiroir du cd :S

    Je te remercie pour ta proposition

  4. #4
    Nouveau membre du Club
    Inscrit en
    Novembre 2004
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Novembre 2004
    Messages : 50
    Points : 25
    Points
    25
    Par défaut
    je viens de penser à un truc.

    est ce qu'il serait possible d'avoir :
    - un thread dédié à la copie (favec les fonctions fileseek, fileread, filewrite)
    - un thread lançant le thread de copie et vérifiant que celui-ci s'exécute bien. si aucune réponse dans un délai imparti, kill du thread et tentative de copie du secteur suivant

    par contre, je ne sais pas comment le coder

  5. #5
    Expert confirmé
    Avatar de Sub0
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2002
    Messages
    3 573
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2002
    Messages : 3 573
    Points : 4 219
    Points
    4 219
    Par défaut
    Je suis en train de recoder la fonction.
    Lorsque la copie rencontre une erreur et si on choisie "Ignorer", la copie ignorera toutes les erreurs jusqu'à la fin.
    Tu remarqueras que la démo possède un bouton d'annulation... donc pas besoin d'ajouter un TimeOut, non?

  6. #6
    Expert confirmé
    Avatar de Sub0
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2002
    Messages
    3 573
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2002
    Messages : 3 573
    Points : 4 219
    Points
    4 219
    Par défaut
    Citation Envoyé par Millenod
    je viens de penser à un truc.

    est ce qu'il serait possible d'avoir :
    - un thread dédié à la copie (favec les fonctions fileseek, fileread, filewrite)
    - un thread lançant le thread de copie et vérifiant que celui-ci s'exécute bien. si aucune réponse dans un délai imparti, kill du thread et tentative de copie du secteur suivant

    par contre, je ne sais pas comment le coder
    Bien entendu!
    Mais le TimeOut n'est pas nécessaire si tu utilises un bouton d'annulation comme la logique semble nous dicter.
    Dans la FAQ, tu trouveras un article et tutoriel sur les threads...

    Je pensais aussi à un truc, je pense ajouter une option pour définir le nombre d'essais lorsqu'on choisi "Réessayer"...

    Bon courage!

  7. #7
    Nouveau membre du Club
    Inscrit en
    Novembre 2004
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Novembre 2004
    Messages : 50
    Points : 25
    Points
    25
    Par défaut
    je viens de tester.
    BlockRead semble passer, via mon lecteur liteon.
    je testerais sur le lecteur matshita de mon portable demain. ce lecteur étant plus médiocre, cela réserve des surprises.

    Seul bémol, mon lecteur semble s'etre emballé...à tester avec ta future version. la fonction "ignorer" et passer au secteur suivant sont intéressante.

    pour le tuto sur les thread, j'en ai lu beaucoup en anglais en général.
    mais je ne me souviens pas en avoir trouvé sur l'échange de donnée, ou communication entre thread..

    merci beaucoup
    (et bonne nuit )

  8. #8
    Expert confirmé
    Avatar de Sub0
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2002
    Messages
    3 573
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2002
    Messages : 3 573
    Points : 4 219
    Points
    4 219
    Par défaut
    Voici la nouvelle version, il y avait pas mal de choses à corriger...

    http://sub0.developpez.com/delphi/mycopy.zip

    J'ai donc ajouté la mémorisation de la fonction "ignorer" pour tout le reste de la copie. J'ai jouté une CheckBox pour définir si l'on copie des "0" à la place des octets non lus ou bien si on n'écrit uniquement les octets qui ont été bien lus. J'ai ajouté une "Led" qui permet de connaître l'état en cours de la copie (vert = tout va bien, jaune = octets lus mais la copie a déjà eu des échecs ignorés, rouge = la lecture des données a échoué mais la copie continue). Et enfin, j'ai ajouté un TLabel pour afficher le nombre de kilo octets réellement copiés. Je n'ai pas encore ajouté le code pour définir le nombre de tentatives de lecture avant de statuer l'échec, mais je ne pense pas que cela soit bien nécessaire...

    Grâce à ce petit programme, j'ai déjà pu récupérer certains fichiers sur CD...
    Attention, la copie peut prendre un certain temps selon l'état du CD!

    à+

  9. #9
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 50
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Si tu n'as pas un besoin impératif de le faire sous Delphi, une solution alternative peut être la suivante :
    - Faire une image du CD avec CloneCD en mode "Correction" (ça peut prendre 3 à 4 heures parfois, mais bon... Faut ce qu'il faut). Ce logiciel est impressionnant sur sa capacité à récupérer des données pourries sur un CD.
    - Utiliser un Mounter ISO pour créer un lecteur CD virtuel à partir de cette image. J'utilise "Virtual CD-ROM Control Panel XP" de Microsoft, il est capable de monter n'importe quelle image compatible avec un PC (y compris des DVD vidéo). Tu as aussi l'option de regraver un nouveau CD, bien sûr.
    - A partir de ce moment, faire une copie depuis le lecteur CD virtuel vers ton disque dur "normalement".

    Testé sur un CD-R qui avait pris un coup de soleil (données endommagées), j'ai pu presque tout récupérer alors qu'au départ, le CD plantait à l'ouverture d'un Explorer dessus... ;-)

  10. #10
    Expert confirmé
    Avatar de Sub0
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2002
    Messages
    3 573
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2002
    Messages : 3 573
    Points : 4 219
    Points
    4 219
    Par défaut
    Pour ma part, je vais continuer à m'amuser avec Delphi...
    Les outils que propose Mac LAK sont très intérressants. J'essayerai de m'inspirer de ces fonctions éventuellement... Pour le moment, je vais ajouté la fonction suivante en option : Lorsque le programme détecte un BlockRead ayant échoué, elle va réessayer en se positionnant sur chaque octet du bloc. Autrement dit, au lieu d'utiliser BlockRead, elle utilisera alors Read (octet par octet). Evidemment, ce traitement prendra encore du temps en plus, mais permettra probablement de récupérer quelques octets dans le bloc... De plus, je pense qu'au lieu de remplacer les octets défectueux par des zéros, je vais utiliser la valeur du dernier octet valide. Le top de la réparation serait d'analyser les données pour reconstituer d'éventuelles séquences d'octets et si possible, prendre en compte le type du fichier, mais le temps de traitement risque de grimper grave!! à+

  11. #11
    Nouveau membre du Club
    Inscrit en
    Novembre 2004
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Novembre 2004
    Messages : 50
    Points : 25
    Points
    25
    Par défaut
    @Mac LAK :
    BlindRead v2 marche et fait exactement ce dont j'ai besoin.
    Mais disons que j'ai envie de voir jusqu'où je peux arriver

    Je ne connaissais pas "Virtual CD-ROM Control Panel XP".
    Cet outil, du même principe que "Daemon Tools" me renvoie sur une question "comment créer un périphérique virtuel"... mais ce n'est pas encore de mon niveau :p

    @Sub0 :
    L'ensemble des fonctions de copie présentes sous delphis ont donc : Blockread, FileRead, Read ?
    as-tu réussi à utiliser le mode asynchrone de ReadFile (API Windows)? pour ma part impossible de copier autre chose que le 1er secteur.

  12. #12
    Expert confirmé
    Avatar de Sub0
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2002
    Messages
    3 573
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2002
    Messages : 3 573
    Points : 4 219
    Points
    4 219
    Par défaut
    Pour la lecture de données de fichiers, j'utilise soit BlockRead pour lire un bloc de données, soit Read pour lire un type de données (souvent un octet). Il m'arrive aussi d'utiliser TFileStream de temps en temps. Les autres fonctions ne m'intérressent pas vraiment puisque j'obtiens déjà tout ce dont j'ai besoin. De plus, je trouve l'utilisation de FileRead plus compliqué d'une part et d'autre part, elle n'apporte rien de plus, c'est équivalent, FileRead permet de lire octet par octet et par bloc. Par exemple, il suffira d'utiliser FileSeek pour définir à quel endroit démarre la lecture... Voici un exemple (pas testé) d'une boucle de lecture :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    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
    32
    33
    34
    35
    36
    Const
      iBuffLength: Integer = $10000;
    Var
      iFileHandle: Integer;
      iBytesRead: Integer;
      iFileLength: Integer;
      iFilePos, i: Integer;
      Buffer: PChar;
     
    Begin
      If OpenDialog1.Execute Then
      Begin
        Try
          iFileHandle := FileOpen(OpenDialog1.FileName, fmOpenRead);
          iFileLength := FileSeek(iFileHandle, 0, 2);
          Buffer := PChar(AllocMem(iBuffLength + 1));
          iFilePos := 0;
          Repeat
            FileSeek(iFileHandle, iFilePos, 0);
            iBytesRead := FileRead(iFileHandle, Buffer, iBuffLength);
            For i := 0 To iBytesRead - 1 Do
            Begin
              StringGrid1.RowCount := StringGrid1.RowCount + 1;
              StringGrid1.Cells[1, i + 1] := Buffer[i];
              StringGrid1.Cells[2, i + 1] := IntToStr(Integer(Buffer[i]));
            End;
            iFilePos := iFilePos + iBytesRead;
            Application.ProcessMessages;
          Until (iFilePos >= iFileLength) Or
                (iBytesRead < iBuffLength);
          FileClose(iFileHandle);
        Finally
          FreeMem(Buffer);
        End;
      End;
    End;

  13. #13
    Nouveau membre du Club
    Inscrit en
    Novembre 2004
    Messages
    50
    Détails du profil
    Informations forums :
    Inscription : Novembre 2004
    Messages : 50
    Points : 25
    Points
    25
    Par défaut
    Je suis en train de recoder un petit truc avec Blockread.
    Cette fonction semble marcher comme je le désire. Reste à voir si le lecteur dvd va apprécier toutes ces tentatives de lecture

    Une remarque, sur ton code, tu utilise une messagebox avec les bouton abort, retry, ignore.
    La valeur retournée de "Ignore" est 5 sur mon pc.
    sur ton code tu l'as definit comme = 2.

    Je vais tester la fonction Read dans la journée. je te tiens au courant.

    Pour mon problème de pointeur sur le fichier, je n'y arrive pas avec fileseek, et je ne comprends pas vraiment comment fonctionne la variable overlapped de la fonction ReadFile.
    http://msdn.microsoft.com/library/en...e/readfile.asp

    Merci beaucoup pour ton aide.
    Si tout est ok, je valide le sujet dans le week end

  14. #14
    Expert confirmé
    Avatar de Sub0
    Homme Profil pro
    Développeur Web
    Inscrit en
    Décembre 2002
    Messages
    3 573
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 51

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Décembre 2002
    Messages : 3 573
    Points : 4 219
    Points
    4 219
    Par défaut
    Citation Envoyé par Millenod
    sur ton code tu l'as definit comme = 2.
    Citation Envoyé par Sub0
    Voici la nouvelle version, il y avait pas mal de choses à corriger...

    http://sub0.developpez.com/delphi/mycopy.zip

    Citation Envoyé par [img
    http://sub0.developpez.com/tf1_.png[/img]]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    Valeur    Valeur numérique  Signification
     
    IDOK      1                 L'utilisateur a choisi le bouton OK.
    IDCANCEL  2                 L'utilisateur a choisi le bouton Annuler.
    IDABORT   3                 L'utilisateur a choisi le bouton Abandonner.
    IDRETRY   4                 L'utilisateur a choisi le bouton Réessayer.
    IDIGNORE  5                 L'utilisateur a choisi le bouton Ignorer.
    IDYES     6                 L'utilisateur a choisi le bouton Oui.
    IDNO      7                 L'utilisateur a choisi le bouton Non.
    Citation Envoyé par Millenod
    Mais disons que j'ai envie de voir jusqu'où je peux arriver
    Commence déjà par te servir de la touche ...
    L'aide en ligne est une vraie mine d'or!
    Bon courage!

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 2
    Dernier message: 29/03/2012, 12h54
  2. Réponses: 5
    Dernier message: 01/03/2012, 10h19
  3. Réponses: 5
    Dernier message: 27/06/2006, 10h08
  4. Réponses: 6
    Dernier message: 18/05/2006, 09h29
  5. [WebForms]Comment télécharger un fichier depuis le serveur ?
    Par pepin21 dans le forum Général Dotnet
    Réponses: 6
    Dernier message: 26/04/2006, 16h26

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo