Page 1 sur 1

Crypter des fichiers tous types en AES

Publié : sam. 07/mai/2011 19:10
par Jacobus
Salut à tous. J'ai remarqué quelques sujets récents traitant de l'encodage en AES, mais uniquement sur des chaînes de caractères. Ce qui limite quand même l'utilisation de cette excellente librairie. Voici donc deux procédures pour chiffrer/déchiffrer des fichiers en AES 128, 192 ou 256 bit. Il est ainsi possible de chiffrer n'importe quel fichier. Il faudra l'adapter à votre usage, mais l'essentiel est là.

Code : Tout sélectionner

;--------------------------------------
;ENCODER UN FICHIER (TOUS TYPE)
;--------------------------------------
;128 bit par défaut
;Pour modifier le niveau de chiffrement, il suffit de l'indiquer lors de l'appel de la procédure
;Ainsi:
;AEScrypt(fichier$,192) pour encoder sur 192 bit
;AEScrypt(fichier$,256) pour encoder sur 256 bit
;Faire de même pour décoder.
;--------------------------------------

DataSection
  Key:
  Data.b $06, $A9, $21, $40, $36, $B8, $A1, $5B, $51, $2E, $03, $D5, $34, $12, $00, $06
  
  InitializationVector:
  Data.b $3D, $AF, $BA, $42, $9D, $9E, $B4, $30, $B4, $22, $DA, $80, $2C, $9F, $AC, $41
EndDataSection

Procedure.s AEScrypt(fichier$,level.l=128)
  
  If ReadFile(0, fichier$) 
    length = Lof(0)                            ; Lit la taille en octets du fichier 
    *MemoryID = AllocateMemory(length)         ; alloue un bloc mémoire de la taille du fichier
    If *MemoryID
      bytes = ReadData(0, *MemoryID, length)   ; Lit les données du fichier et les place dans le bloc mémoire
    EndIf
    CloseFile(0)
  EndIf 
 ;On attaque le chiffrement des données placées en mémoire... 
  *CipheredData = AllocateMemory(MemorySize(*MemoryID)+1) ; allocation d'un autre bloc mémoire de la taille du fichier original+1
  If AESEncoder(*MemoryID, *CipheredData, MemorySize(*MemoryID), ?Key, level, ?InitializationVector)
    FreeMemory(*MemoryID) ; libère la mémoire précédemment allouée dont on a plus besoin
    If *CipheredData > 0
      ; On crée le fichier en ajoutant simplement une nouvelle extension
      ; cela permet de savoir de quel type de fichier original il s'agit et de facilement le décoder plus tard
      CipheredFile = CreateFile(#PB_Any, fichier$+".aes")
      If CipheredFile
        WriteData(CipheredFile, *CipheredData, length)
        CloseFile(CipheredFile)  
      EndIf 
      FreeMemory(*CipheredData) ; là aussi on libère la mémoire utilisée car plus nécessaire
      ErrorMessage$ = "Fichier chiffré!"
    Else 
      ErrorMessage$ = "Allocate Memory Error! - Impossible d'allouer le bloc mémoire nécessaire au chiffrement." 
    EndIf 
  Else 
    ErrorMessage$ = "AESEncoder() error! - Le processus d'encodage n'a pu aboutir."
  EndIf 
  ProcedureReturn ErrorMessage$
EndProcedure
Procedure.s AESdecrypt(fichier$,level.l=128)
  
  If ReadFile(0, fichier$) 
    length = Lof(0)                     
    *MemoryID = AllocateMemory(length)  
    If *MemoryID
      bytes = ReadData(0, *MemoryID, length) 
    EndIf
    CloseFile(0)
  EndIf
  
  *DecipheredData = AllocateMemory(MemorySize(*MemoryID)+1)
  If AESDecoder(*MemoryID, *DecipheredData, MemorySize(*MemoryID), ?Key, level, ?InitializationVector)
    FreeMemory(*MemoryID)
    ;On récupère le nom original du fichier simplement en retirant l'extension .aes
    DecFile$ = GetPathPart(fichier$) + Left(GetFilePart(fichier$), Len(GetFilePart(fichier$)) - Len(GetExtensionPart(fichier$)) - 1) 
    If *DecipheredData > 0
      DecipheredFile = CreateFile(#PB_Any,DecFile$)
      If DecipheredFile
        WriteData(DecipheredFile, *DecipheredData, length)
        CloseFile(DecipheredFile)
      EndIf
      FreeMemory(*DecipheredData)
      ErrorMessage$ = "Fichier déchiffré!" 
    Else 
      ErrorMessage$ = "Allocate Memory Error! - Impossible d'allouer le bloc mémoire nécessaire au déchiffrement." 
    EndIf 
  Else 
    ErrorMessage$ =  "AESDecoder() error! - Le processus de décodage n'a pu aboutir."
  EndIf 
  ProcedureReturn ErrorMessage$
EndProcedure

;Test encrypt/
file$ = OpenFileRequester("Choisissez un fichier à crypter", "c:\*.*", "Tous les fichiers (*.*)|*.*", 0)
If file$
  confirm$ = AEScrypt(file$)
  If confirm$ <> "Fichier chiffré!"
    MessageRequester("Erreur!","Une erreur est survenue lors du chiffrement de "+GetFilePart(file$)+Chr(10)+"Message : "+confirm$,#MB_ICONERROR)
  Else 
    ;;suppression de l'original / à vous de voir /
    ;DeleteFile(file$)
  EndIf 
EndIf 

;Test decrypt/
file$ = OpenFileRequester("Choisissez un fichier à décrypter", "c:\*.aes", "Fichiers AES (*.aes)|*.aes", 0)
If file$
  confirm$ = AESdecrypt(file$)
  If confirm$ <> "Fichier déchiffré!"
    MessageRequester("Erreur!","Une erreur est survenue lors du déchiffrement de "+GetFilePart(file$)+Chr(10)+"Message : "+confirm$,#MB_ICONERROR)
  Else 
    ;;suppression du fichier crypté / à vous de voir /
    ;DeleteFile(file$)
  EndIf
EndIf 
@+ Jacobus

Re: Crypter des fichiers tous types en AES

Publié : sam. 07/mai/2011 22:37
par case
merci pour le code ,)

Re: Crypter des fichiers tous types en AES

Publié : sam. 07/mai/2011 23:29
par Kwai chang caine
Merci 8)

Re: Crypter des fichiers tous types en AES

Publié : dim. 08/mai/2011 8:48
par Backup
Merci pour ce code :)

Re: Crypter des fichiers tous types en AES

Publié : dim. 08/mai/2011 10:41
par venom
ype merci pour ton code Jacobus, 8)








@++

Re: Crypter des fichiers tous types en AES

Publié : jeu. 12/mai/2011 14:33
par dayvid
Cool :) merci :D

Re: Crypter des fichiers tous types en AES

Publié : mer. 29/juin/2011 16:19
par Anonyme2
Excellent Jacobus, j'ai modifié pour intégrer le décryptage dans PureIconManager.
Je stabilise le code au maximum pour éviter les crash mémoire, pointeur etc.

Je teste les adresses mémoire avec la valeur <> de 0 et pas par > 0.

J'ai utilisée des codes d'erreur et une table d'adresse des codes d'erreur.

Voilà la version que j'utiliserais (enfin presque), je vous la laisse, à prendre ou à laisser, peu m'importe :mrgreen:

Code : Tout sélectionner

;--------------------------------------
;ENCODER UN FICHIER (TOUS TYPE)
;--------------------------------------
;128 bit par défaut
;Pour modifier le niveau de chiffrement, il suffit de l'indiquer lors de l'appel de la procédure
;Ainsi:
;AEScrypt(fichier$,192) pour encoder sur 192 bit
;AEScrypt(fichier$,256) pour encoder sur 256 bit
;Faire de même pour décoder.
;--------------------------------------

Define i

EnableExplicit


#Return_Error = 0
#Return_No_Error = 1

Enumeration 0    ; les erreurs
     #Error_AESEncoder_size_key          ;  erreur de la taille de la clé encodage, uniquement 128, 192, 256 autorisé
     #Error_AESDecoder_size_key          ;  erreur de la taille de la clé décodage, uniquement 128, 192, 256 autorisé
     #Error_ReadFile_Cryp_Failed         ;  erreur d'ouverture en lecture du fichier à coder
     #Error_ReadFile_Decrypt_Failed      ;  erreur d'ouverture du fichier à décodage en lecture
     #Error_CreateFile_Cryp_Failed       ;  erreur d'ouverture du fichier pour codage
     #Error_CreateFile_Decrypt_Failed    ;  erreur d'ouverture du fichier pour décodage
     #Error_Bad_Readata_Length           ;  Mauvaise longueur lues des données
     #Error_length_Null_crypt            ;  erreur, la taille du fichier est de 0 octet
     #Error_length_Null_decrypt          ;  erreur, la taille du fichier est de 0 octet
     #Error_Memory_Allocation            ;  erreur d'allocation mémoire
     #Error_AESEncoder_Failed            ;  l'encodage a échoué
     #Error_AESDecoder_Failed            ;  le decodage a échoué
     
     #Derniere_Erreur
EndEnumeration

; table des adresses des chaines pour affichage
Dim Table_Erreur.i(#Derniere_Erreur-1)
Table_Erreur(#Error_AESEncoder_size_key) = ?Error_AESEncoder_size_key
Table_Erreur(#Error_AESDecoder_size_key) = ?Error_AESDecoder_size_key
Table_Erreur(#Error_ReadFile_Cryp_Failed) = ?Error_ReadFile_Cryp_Failed
Table_Erreur(#Error_ReadFile_Decrypt_Failed) = ?Error_ReadFile_Decrypt_Failed
Table_Erreur(#Error_CreateFile_Cryp_Failed) = ?Error_CreateFile_Cryp_Failed
Table_Erreur(#Error_CreateFile_Decrypt_Failed) = ?Error_CreateFile_Decrypt_Failed
Table_Erreur(#Error_Bad_Readata_Length) = ?Error_Bad_Readata_Length
Table_Erreur(#Error_length_Null_crypt) = ?Error_length_Null_crypt
Table_Erreur(#Error_length_Null_decrypt) = ?Error_length_Null_decrypt
Table_Erreur(#Error_Memory_Allocation) = ?Error_Memory_Allocation
Table_Erreur(#Error_AESEncoder_Failed) = ?Error_AESEncoder_Failed
Table_Erreur(#Error_AESDecoder_Failed) = ?Error_AESDecoder_Failed

#File_To_convert = 0
#Converted_File  = 1

;  mémorise l'erreur au cas ou...
Global Erreur
; mémorise le nom du fichier à coder
Global file$


Macro   Set_Error_Code(Code_Erreur)
     Erreur = Code_Erreur
EndMacro

Macro   Get_Error_Code()
     (Erreur)
EndMacro


Procedure.i AEScrypt(fichier$, level=128)
     
     ; mémorise la longueur du fichier
     Protected length
     ; mémorise la taille des données lues par ReadData()
     Protected bytes
     ; mémorise le retour de CreateFile() pour les données cryptées
     Protected CipheredFile
     ; pointeur mémoire
     Protected *MemoryID
     ; pointeur pour le cryptage
     Protected *CipheredData
     
     
     ; teste les valeurs
     Select level
          Case 128, 192, 256
               
          Default
               Set_Error_Code(#Error_AESEncoder_size_key)
               ProcedureReturn #Return_Error
     EndSelect
     
     ; ouverture du fichier en lecture
     If ReadFile(#File_To_convert, fichier$) = 0
          Set_Error_Code(#Error_ReadFile_Cryp_Failed)
          ProcedureReturn #Return_Error
     EndIf
     
     ; Lit la taille en octets du fichier
     length = Lof(#File_To_convert)
     If length = 0
          ;  fermeture du fichier
          CloseFile(#File_To_convert)
          Set_Error_Code(#Error_length_Null_crypt)
          ProcedureReturn #Return_Error
     EndIf
     
     ; alloue un bloc mémoire de la taille du fichier
     *MemoryID = AllocateMemory(length)
     If *MemoryID = 0
          ;  fermeture du fichier
          CloseFile(#File_To_convert)
          Set_Error_Code(#Error_Memory_Allocation)
          ProcedureReturn #Return_Error
     EndIf
     
     ; Lit les données du fichier et les place dans le bloc mémoire
     bytes = ReadData(#File_To_convert, *MemoryID, length)
     
     ;  fermeture du fichier
     CloseFile(#File_To_convert)
     
     ;  teste que la lecture s'est bien déroulée
     If bytes <> MemorySize(*MemoryID)
          ;  libère la mémoire
          FreeMemory(*MemoryID)
          Set_Error_Code(#Error_Bad_Readata_Length)
          ProcedureReturn #Return_Error
     EndIf
     
     ;On attaque le chiffrement des données placées en mémoire...
     ; allocation d'un autre bloc mémoire de la taille du fichier original+1
     *CipheredData = AllocateMemory(MemorySize(*MemoryID)+1)
     If *CipheredData = 0
          ;  libère la mémoire
          FreeMemory(*MemoryID)
          Set_Error_Code(#Error_Memory_Allocation)
          ProcedureReturn #Return_Error
     EndIf
     
     If AESEncoder(*MemoryID, *CipheredData, MemorySize(*MemoryID), ?Key, level, ?InitializationVector) = 0
          ;  libère la mémoire
          FreeMemory(*MemoryID)
          ;  libère la mémoire
          FreeMemory(*CipheredData)
          Set_Error_Code(#Error_AESEncoder_Failed)
          ProcedureReturn #Return_Error
     EndIf
     
     ;  libère la mémoire précédante dont on a plus besoin
     FreeMemory(*MemoryID)
     
     ; On crée le fichier en ajoutant simplement une nouvelle extension
     ; cela permet de savoir de quel type de fichier original il s'agit et de facilement le décoder plus tard
     CipheredFile = CreateFile(#PB_Any, fichier$ + ".aes")
     If CipheredFile = 0
          ;  libère la mémoire
          FreeMemory(*CipheredData)
          Set_Error_Code(#Error_CreateFile_Cryp_Failed)
          ProcedureReturn #Return_Error
     EndIf
     
     WriteData(CipheredFile, *CipheredData, length)
     
     CloseFile(CipheredFile)
     
     ;  libère la mémoire
     FreeMemory(*CipheredData)
     
     ; tout est OK
     ProcedureReturn #Return_No_Error
EndProcedure


Procedure.i AESdecrypt(fichier$,level=128)
     ; mémorise la longueur du fichier
     Protected length
     ; mémorise la taille des données lues par ReadData()
     Protected bytes
     ; mémorise le retour de CreateFile() pour les données décryptées
     Protected DecipheredFile
     ; pointeur mémoire
     Protected *MemoryID
     ; pointeur pour le décryptage
     Protected *DecipheredData
     ; nom riginal du fichier à décrypter
     Protected DecFile$
     
     
     ; teste les valeurs
     Select level
          Case 128, 192, 256
               
          Default
               Set_Error_Code(#Error_AESDecoder_size_key)
               ProcedureReturn #Return_Error
     EndSelect
     
     ; ouverture du fichier en lecture
     If ReadFile(#Converted_File, fichier$) = 0
          Set_Error_Code(#Error_ReadFile_Decrypt_Failed)
          ProcedureReturn #Return_Error
     EndIf
     
     length = Lof(#Converted_File)
     If length = 0
          ;  fermeture du fichier
          CloseFile(#Converted_File)
          Set_Error_Code(#Error_length_Null_decrypt)
          ProcedureReturn #Return_Error
     EndIf
     
     *MemoryID = AllocateMemory(length)
     If *MemoryID = 0
          ;  fermeture du fichier
          CloseFile(#Converted_File)
          Set_Error_Code(#Error_Memory_Allocation)
          ProcedureReturn #Return_Error
     EndIf
     
     ; Lit les données du fichier et les place dans le bloc mémoire
     bytes = ReadData(#Converted_File, *MemoryID, length)
     
     ;  fermeture du fichier
     CloseFile(#Converted_File)
     
     ;  teste que la lecture s'est bien déroulée
     If bytes <> MemorySize(*MemoryID)
          ;  libère la mémoire
          FreeMemory(*MemoryID)
          Set_Error_Code(#Error_Bad_Readata_Length)
          ProcedureReturn #Return_Error
     EndIf
     
     ; allocation d'un autre bloc mémoire de la taille du fichier original+1
     *DecipheredData = AllocateMemory(MemorySize(*MemoryID)+1)
     If *DecipheredData = 0
          ;  libère la mémoire
          FreeMemory(*MemoryID)
          Set_Error_Code(#Error_Memory_Allocation)
          ProcedureReturn #Return_Error
     EndIf
     
     If AESDecoder(*MemoryID, *DecipheredData, MemorySize(*MemoryID), ?Key, level, ?InitializationVector) = 0
          ;  libère la mémoire
          FreeMemory(*MemoryID)
          ;  libère la mémoire
          FreeMemory(*DecipheredData)
          Set_Error_Code(#Error_AESDecoder_Failed)
          ProcedureReturn #Return_Error
     EndIf
     
     ;  libère la mémoire
     FreeMemory(*MemoryID)
     
     ;On récupère le nom original du fichier simplement en retirant l'extension .aes
     DecFile$ = GetPathPart(fichier$) + Left(GetFilePart(fichier$), Len(GetFilePart(fichier$)) - Len(GetExtensionPart(fichier$)) - 1)
     
     DecipheredFile = CreateFile(#PB_Any,DecFile$)
     If DecipheredFile = 0
          ;  libère la mémoire
          FreeMemory(*DecipheredData)
          Set_Error_Code(#Error_CreateFile_Decrypt_Failed)
          ProcedureReturn #Return_Error
     EndIf
     
     ; écriture des données décodées dans le fichier
     WriteData(DecipheredFile, *DecipheredData, length)
     
     ;  fermeture du fichier
     CloseFile(DecipheredFile)
     
     ;  libère la mémoire
     FreeMemory(*DecipheredData)
     
     ; tout est OK
     ProcedureReturn #Return_No_Error
EndProcedure



;Test encrypt/
file$ = OpenFileRequester("Choisissez un fichier à crypter", "c:\*.*", "Tous les fichiers (*.*)|*.*", 0)
If file$
     Select AEScrypt(file$)
          Case  #Return_Error
               ; il y a une erreur, on affiche
               MessageRequester("Erreur", PeekS(Table_Erreur(Get_Error_Code()))+Chr(10)+"Erreur " + Str(Get_Error_Code()), 16)
               End
               
          Default
               ; ici c'est Ok
               MessageRequester("Info", "Fichier chiffré! : " + file$, 16)
     EndSelect
     
Else
     ;  on ne sélectionne pas de fichier, on termine sans rien faire
     End
EndIf


;Test decrypt/
file$ = SaveFileRequester("Choisissez un fichier à décrypter", "c:\*.aes", "Fichiers AES (*.aes)|*.aes", 0)
If file$
     
     Select AESdecrypt(file$)
          Case  #Return_Error
               ; il y a une erreur, on affiche
               MessageRequester("Erreur", PeekS(Table_Erreur(Get_Error_Code()))+Chr(10)+"Erreur " + Str(Get_Error_Code()), 16)
               
               End
               
          Default
               ; ici c'est Ok
               MessageRequester("Info", "Fichier déchiffré! : " + file$, 16)
               
     EndSelect
     
     
     ;  on ne sélectionne pas de fichier, on termine sans rien faire
EndIf

End



DataSection
     ; les messages d'erreur
     Error_AESEncoder_size_key:
     Data.s    "Mauvaise taille de la clé d'encodage (128, 192, 256 uniquement)"
     
     Error_AESDecoder_size_key:
     Data.s    "Mauvaise taille de la clé de décodage (128, 192, 256 uniquement)"
     
     Error_ReadFile_Cryp_Failed:
     Data.s    "erreur d'ouverture en lecture du fichier à coder"
     
     Error_ReadFile_Decrypt_Failed:
     Data.s    "erreur d'ouverture du fichier à décodage en lecture"
     
     Error_CreateFile_Cryp_Failed:
     Data.s    "erreur d'ouverture du fichier pour codage"
     
     Error_CreateFile_Decrypt_Failed:
     Data.s    "erreur d'ouverture du fichier pour décodage"
     
     Error_Bad_Readata_Length:
     Data.s    "Mauvaise longueur lues des données"
     
     Error_length_Null_crypt:
     Data.s    "Le fichier à coder est vide"
     
     Error_length_Null_decrypt:
     Data.s    "Le fichier à décoder est vide"
     
     Error_Memory_Allocation:
     Data.s    "Impossible d'allouer de la mémoire"
     
     Error_AESEncoder_Failed:
     Data.s    "Le processus d'encodage n'a pu aboutir"
     
     Error_AESDecoder_Failed:
     Data.s    "Le processus de décodage n'a pu aboutir"
     
EndDataSection

DataSection
     Key:
     Data.b $06, $A9, $21, $40, $36, $B8, $A1, $5B, $51, $2E, $03, $D5, $34, $12, $00, $06
     
     InitializationVector:
     Data.b $3D, $AF, $BA, $42, $9D, $9E, $B4, $30, $B4, $22, $DA, $80, $2C, $9F, $AC, $41
     
EndDataSection

Re: Crypter des fichiers tous types en AES

Publié : mer. 29/juin/2011 19:09
par Jacobus
Je reconnais bien là le perfectionniste prudent que tu es :mrgreen:
Excellente gestion des erreurs éventuelles, j'adopte, on ne sait jamais...

Re: Crypter des fichiers tous types en AES

Publié : mer. 29/juin/2011 19:27
par Anonyme2
Il y a une petite erreur de copier /coller ici

Code : Tout sélectionner

Table_Erreur(#Error_ReadFile_Decrypt_Failed) = #Error_ReadFile_Decrypt_Failed
il faut mettre un ? à la place du #, je vais corriger le code

Re: Crypter des fichiers tous types en AES

Publié : mer. 29/juin/2011 19:47
par Jacobus
Heureusement, dans l'éditeur ça se voit bien ce type d'erreur, avec la coloration du code... :)

Re: Crypter des fichiers tous types en AES

Publié : jeu. 30/juin/2011 6:40
par Anonyme2
Jacobus, sur de grands programmes, j'utilise des erreurs secondaires qui me permettent de trouver facilement ou est l'erreur lorsque ça plante et côté plantage, je m'y connais :mrgreen: .
C'est pour cela que je teste tout, création des gadgets, fenêtres, enfin tout ce qui peut retourner une erreur.
Jamais de Firstelement() ou autre sans test. En 64 bits, j'ai eu des valeurs écrasées (mauvaises structures ou types) et on se retrouve avec des paramètres de fonction erronés etc.
Ca me fait penser que je pense avoir trouvé un bug sur une interface (Istorage/Istream) en 64 bits, il faut que je prenne le temps de mettre plusieurs exemples sur le forum en (ça me fait c..., vraiment nulle la suppression des bug du forum fr).

Pour continuer sur les erreurs, par exemple l'erreur d'un mauvais format de fichier.

Code : Tout sélectionner

#Error_Bad_File_Format   =   1
Dans PureIconManager, je l'ai environ 70 fois et lorsque qu'il y a l'erreur, pas facile sur un très long code de retrouver ou c'est.
j'ajoute des sous-erreurs avec le même nom d'erreur précédé de #Sous_Error_" suivi du numéro de sous-erreur.
avec une petite boucle, on crée facilement les constantes des erreurs secondaires.
Je rajoute à la fin ' ; utilisé' lorsque je l'ai utilisé

Code : Tout sélectionner

#Sous_Error_1_Error_Bad_File_Format   =   1 << 16   ; utilisé
#Sous_Error_2_Error_Bad_File_Format   =   2 << 16 
#Sous_Error_3_Error_Bad_File_Format   =   3 << 16 
#Sous_Error_4_Error_Bad_File_Format   =   4 << 16 
Je crée l'erreur + erreur secondaire avec un |

Code : Tout sélectionner

Set_Error_Code(#Sous_Error_1_Error_Bad_File_Forma|#Error_Bad_File_Format )
Je n'ai qu'une procedure d'affichage des erreurs, voici un extrait (toutes mes erreurs sont déclarées en ressources, c'est pratique pour la gestion des langues, fr/en).

Code : Tout sélectionner

Procedure Display_Error(Display_Error_code)
     ;// mémorise l'erreur retrouvée
     Protected Error
     ;// mémorise le texte de la messagerequester
     Protected Texte$ = "", Texte2$ = "", Texte3$ = ""
     ;// mémorise la longueur de la chaine en resource
     Protected Len_String
     ;// mémorise la sous-erreur
     Protected Sous_Erreur
     
     ;// retrouve le type d'erreur + erreur secondaire
     Error = Get_Error_Code()
     
     ;,// la sous erreur si existe
     Sous_Erreur = (Error & $FFFF0000) >> 16
     
     ;,// l'erreur est dans le word de poids faible
     Error & $0000FFFF
     
     ; le message indiquant le numéro de l'erreur
     If Sous_Erreur
          Texte2$ = Load_Resource_String(#Error_String, Langue_id) + Str(Error) + " / " + Load_Resource_String(#Sub_Error_String, Langue_id) + Str(Sous_Erreur)
     Else
          Texte2$ = Load_Resource_String(#Error_String, Langue_id) + Str(Error)
     EndIf