Crc32 compatible winrar et winzip ( basé sur le code c++ de capa6t )

Soyez le premier à donner votre avis sur cette source.

Vue 18 713 fois - Téléchargée 529 fois


Description

Voici une classe qui vous permet de générer le CRC32 d'un fichier.
Je me suis basé sur la source C++ de Capa6T (http://www.cppfrance.com/code.aspx?ID=24351)
qui marche à merveille.
La conversion en C# n'a pas été très simple mais c'était un bon challenge.
J'ai aussi essayé de le rendre un peu plus rapide en lisant des block de 4ko au lieu de lire
byte par byte , ce qui peu rendre le calcul de fichiers volumineux tres long.
Je vous epargne les détails (si vous en voulez , les sources sont pleins de commentaires ^^ )

Vous pouvez vous servir directement de cette classe en l appelant ainsi :
uint crc = CRC32.GetCrc32( "nom_de_fichier.ext" , false , null ) ; // sans bar de progression.
uint crc = CRC32.GetCrc32( "nom_de_fichier.ext" , true , System.Windows.forms.ProgressBar ) //avec bar de progression

/* NOTE : Utilisez cette fonction dans un thread , ca évitera de "figer" votre appli */

Dans le zip j'ai inclus ma source C# et celle que j ai ecris en C++ (avec la classe de l'auteur original dedans).

Source / Exemple :


using System;
using System.Windows.Forms;
using System.IO;

		//************************************************************
		//			CALCUL CRC32 EN C#	
		//		           27 janvier 2005 par lunatik
		//		 Base sur le code C++ de Capa6T
		//	      http://www.cppfrance.com/code.aspx?ID=24351
		//	(et mon site -> http://www.kawaiistudio.no-ip.com/lunatikv6 ^^ )
		//	Merci à Xya et cbeyls de csharpfr.com pour leurs conseils !
		//************************************************************

namespace CRC32
{
	public class CRC32
	{
		/// <summary>
		/// Calculer un CRC32 ( Cyclic Redundancy Check ) d'un fichier.
		/// </summary>
		/// <param name="filePath">Chemin du fichier.</param>
		/// <param name="enableProgress">Si enableProgress est vrai , la progression du calcul sera affiche
		/// dans le System.Windows.Forms.ProgressBar que vous aurez definit en 3eme argument de cette fonction</param>
		/// <param name="pBar">Le ProgressBar ou sera affiche la progression ( si enableProgress est "faux" mettez "null" ).</param>
		/// <returns>Le CRC sous forme de uint ( 32bit unsigned ) , utilisez crc32.ToString("X"); pour l'affichage
		/// hexadecimal.</returns>
		public static uint GetCrc32( string filePath , bool enableProgress ,ProgressBar pBar )
		{
			int myTimer = Environment.TickCount;
			//on initialise un filestream.
			FileStream myStream = new FileStream( filePath , FileMode.Open , FileAccess.Read );
			//On stock la taille du fichier.
			long fileSize = myStream.Length;
			if( enableProgress ) pBar.Maximum = ( int )fileSize + 2 ;

			//quelques variables...
			uint[]	LookUpTable	= new long[ 256 ];
			uint 	poly		= 0xEDB88320;
			uint 	crc32		= 0;
			//   *NOTE* , je n'utilise pas le meme polynome que celui utilise dans la source C++ dont je me suis inspire
			//   ni le meme algorithme pour generer la Table , mais au final la table est strictement la meme. ^^

			uint val;
			//Generation de la Table
			for( int i = 0 ; i <= 0xFF ; ++i)
			{
				val = i;
				for( int j = 8 ; j > 0 ; --j )
				{
					//Capa6T , dans sa source faisait une comparaison booleene 
					// en calculant    val & 1 qui donne 1 ou 0  donc true ou false en C++
					//or en C# on ne peut plus utiliser 1 et 0 (ni -1 d ailleurs) pour faire une comparaison booleene
					// ** c etait juste une petite note ^^ **
					if( ( val & 1 ) == 1 )
					{
						val = ( val >> 1 ) ^ poly ; 	// <-- la j'ai un peu compresse l equation, mais ca passe hehe : )
					}
					else
						val >>= 1 ;						
				}
				LookUpTable[ i ] = val; // on stock le resultat dans notre table. 
			}

			/* si vous voulez voir a quoi ressemble la table.
			 *

  • string table = "";
  • for( int z = 0 ; z <= 0xFF / 3 ; )
  • {
  • table += "0x" + LookUpTable[ z ].ToString("X");
  • table += " , 0x" + LookUpTable[ z+1 ].ToString("X");
  • table += " , 0x" + LookUpTable[ z+2 ].ToString("X") + "\n";
  • z+=3;
  • }
  • MessageBox.Show( table );
  • /
//on va lire le fichier par block de 4096 byte (4k) pour eviter un surchargement de memoire. //NOTE : cette partie du code à été modifiée, utilisons la méthode de cbeyls //, plus simple , pratique et econome ( mieux quoi ... ^^" ) byte[] byteArray = new byte[ 4096 ]; int readBytes; crc32 = 0xFFFFFFFF; while( ( readBytes = myStream.Read( byteArray , 0 , 4096 ) ) != 0 ) { for( int y = 0 ; y < readBytes ; ++y) { //maintenant voici le tit bout de code qui vient de la source de Capa6T //qui genere un CRC32 avec notre Table generee precedement. crc32 = ( crc32 >> 8 ) ^ LookUpTable[ ( crc32 & 0xFF ) ^ byteArray[ y ] ]; } if( enableProgress ) pBar.Value = offset ; } //fermeture du stream myStream.Close(); int mySecondTimer = Environment.TickCount; //ca c'est si vous voulez connaitre le temps int ms = mySecondTimer - myTimer; //de calcul exact en m.seconds. //MessageBox.Show( "Le CRC32 a ete calcule en "+ ms.ToString() + " Millisecondes.","Infos"); return crc32^0xFFFFFFFF; } } }

Conclusion :


Ce code n'est certainement pas le plus rapide pour effectuer un tel calcul ,
si vous avez des idées pour l 'améliorer , je suis preneur !! ^^
  • NOTE* -> le temps approximatif prevu a été calculé , par rapport aux résultats que donnait mon programme

sur mon ordi (càd un athlon1800+ avec une vieille ddr 166 , le dur pas defragmenté , 2~3 visual studio plus quelques applis qui tournaient par derriere).
J'aimerais , si certains d entre vous ont le temps , avoir les résultats sur des ordi moins puissants .
un grand merci a Capa6T pour son algo crc32 ( j en ai cherché un pendant longtemps ^^ )

Je vous rappele qu'il est déconseillé d'utiliser le crc32 en matiere de protection , pour un usage professionel utilisez plutot un hash MD5 (par ex celui dispo dans la librairie CsLib de ce site ^^ )

Codes Sources

A voir également

Ajouter un commentaire Commentaires
Messages postés
31
Date d'inscription
samedi 22 janvier 2005
Statut
Membre
Dernière intervention
13 mars 2005
1
> et pour éviter que l appli se freeze je mettais tout ça dans un thread , mais dans la fonction directement c'est beaucoup mieux !

En fait c'est mieux de le mettre dans un 2e Thread, mais dans les sources que j'ai téléchargées ici, tu fais tout dans le même. Et puis, quand tu crées un 2e Thread, il y a un problème: seul le Thread de l'interface graphique est capable de modifier l'interface graphique, donc dans un 2e Thread, la progressBar ne bougerais pas (même si je n'ai pas testé mais c'est toujours le même problème avec les interfaces graphiques WinForms). Quand un 2e Thread doit modifier l'interface graphique, il faut utiliser un delegate et différer son exécution mais bon, on va dire que c'est hors sujet! L'avantage majeur d'utiliser un 2e Thread c'est que, quand l'utilisateur clique sur la croix pour fermer le programme, le Thread graphique peut dire au 2e: Stop! Autrement dit, Thread.Abort()
Ici ce n'est pas le cas, tout se fait dans le même Thread, résultat, une fois que le calcul du CRC est commencé, impossible de l'arrêter comme ça. Alors pour l'arrêter, on pourrait ajouter une méthode Stop() à la classe, qui fait passer un booléen à false et qui permet de sortir de la boucle qui calcule le CRC. C'est juste une suggestion.

La progressBar pose un autre problème: vu la façon dont on l'utilise, la taille du fichier en bytes est passée à MaxValue qui est un int. Ca limite l'utilisation à des fichiers de maximum 2 exposant 31 bytes, autrement dit 2 gigabytes. Pour les fichiers plus gros, ça ne fonctionne pas avec cette méthode.

J'ai pour principe de toujours séparer l'interface graphique du reste du code, donc moi la progressBar je l'ai virée et je n'ai donc pas tous ces problèmes, même s'il est vrai que ça pourrait être bien de connaître le statut de la progression, mais alors via d'autres moyens comme par exemple enregistrer un callback sous la forme d'une fonction qui prend en paramètre une valeur qui représente un pourcentage de progression, par exemple.

Niveau performances, j'ai fait des tests avec plusieurs fichiers sur différents disques durs, et il me semble que la valeur idéale pour le buffer de lecture est de 16384 bytes. Ca ne va pas plus vite que 4096 bytes, mais ça consomme un peu moins de CPU. Au-delà, ça commence à ralentir donc je ne conseille pas.
Messages postés
57
Date d'inscription
lundi 23 février 2004
Statut
Membre
Dernière intervention
11 septembre 2008
1
Salut cbeyls ,
merci beaucoup pour tes messages,
Tout ça améliore carement la fonction,
( pour le booleen en 2nd argument tu as entierement raison , c'est une étourderie de ma part ^^" ).
En fait c'est vrai qu'on a pas à faire filesize +2 lol,
c'est pendant que j ecrivais la fonction , la premiere fois j ai eu des problèmes avec la progressebar donc j ai rajouté +2 au max size ^^" ..... mais là c'est complètement inutile.
et pour éviter que l appli se freeze je mettais tout ça dans un thread , mais dans la fonction directement c'est beaucoup mieux !
et si effectivement on a là , l'algo le plus rapide pour le moment , c'est grâce à Capa6T ^^ , moi j'ai fait que copier lol...

Merci encore ,
++

lunatik
Messages postés
31
Date d'inscription
samedi 22 janvier 2005
Statut
Membre
Dernière intervention
13 mars 2005
1
J'ai encore oublié un petit détail :)

if( pBar != null )
{
pBar.Minimum = 0;
pBar.Maximum = (int)myStream.Length;
pBar.Value = 0;
}

Faut mettre la valeur de la progressBar à zéro au début, voilà ;)
J'espère que tu as tout pigé, si jamais tu as des questions, n'hésite pas à me contacter.
Messages postés
31
Date d'inscription
samedi 22 janvier 2005
Statut
Membre
Dernière intervention
13 mars 2005
1
Hello, c'est encore moi. J'ai vu que tu as changé ta source, c'est sympa. Mais j'ai encore quelques remarques.

En fait j'ai écrit une classe optimisée pour mon usage personnel en m'inspirant de ton code et d'une source en C (l'algorithme est le même, apparemment, c'est le plus rapide, en tous cas j'ai pas trouvé mieux), j'ai enlevé la progressBar, mis quelques commentaires en anglais et j'ai utilisé un Stream en entrée au lieu d'un nom de fichier. C'est un bout de ce code que je t'ai copié-collé.

Seulement, comme toi tu utilises une progressBar, il y a quelques petites choses que tu devrais changer:

1) Tout d'abord d'un point de vue purement esthétique et pratique, je ne vois pas l'intérêt du 2e paramètre, le booléen enableProgress. Si j'étais toi, je l'enlèverais et je demanderais à l'utilisateur d'attribuer la valeur null à l'argument pBar s'il ne souhaite pas de progressBar. Ensuite au lieu de tester
if(enableProgress)
tu testerais
if(pBar != null)

2) Je t'ai dit que tu n'aurais plus besoin de la variable offset, mais tu l'emploies toujours pour la progressBar. Il y a toujours moyen d'utiliser la progressBar sans cette variable, mais il faut un peu adapter ton code. Au début, remplace

long fileSize = myStream.Length;
if( enableProgress ) pBar.Maximum = ( int )fileSize + 2 ;

par

if( pBar != null )
{
pBar.Minimum = 0;
pBar.Maximum = (int)myStream.Length;
}

J'avoue aussi que je n'ai pas compris pourquoi tu faisais +2, normalement la taille du fichier devrait suffire. Ensuite, dans le code de la boucle, remplace:

if( enableProgress ) pBar.Value = offset ;

par

if( pBar != null )
{
pBar.Value += readBytes;
Application.DoEvents();
}

Le Application.DoEvents() c'est pour éviter que ton interface graphique ne plante complètement pendant le calcul du CRC32, et continue à réagir aux événements de la souris et à rester "vivante". Ceci parce que tu effectues le calcul du CRC32 dans le cadre du Thread qui s'occupe de l'affichage. Si tu n'appelles pas DoEvents() ton application est incapable de réagir à la souris ou au clic sur un bouton pendant tout le calcul ! Si tu veux éviter que quelqu'un clique sur un bouton pendant le calcul, grise les boutons ou change le pointeur en sablier avant le calcul et revient à la normale après, mais laisse le DoEvents(), ça évitera à Windows de croire que le programme est planté si tu t'acharnes à la souris dessus.

3) Dernière remarque, mais cela je suppose que tu le sais déjà, le calcul est plus rapide lorsque le code est compilé en mode Release par rapport au mode Debug.
Messages postés
57
Date d'inscription
lundi 23 février 2004
Statut
Membre
Dernière intervention
11 septembre 2008
1
Salut Capa6T ça fait plaisir de voir un message de ta part ici !
Je suis content que ça te plaise et que ça puisse te donner une bonne idée du C# ( Tu verras c'est d'une simplicité enfantine comparé au c++ lol ^^" ) .
Je viens de modifier la source ( mais pas encore le zip désolé ... ) j'ai utilisé la methode de cbeyls pour la lecture et utilisé des uint a la place de mes vieux longs ( qui sont désormais 64bits en C# lol fait gaffe ^^ ) merci a Xya.
Dailleurs, Xya , j'ai essayé le clr profiler , c'est terrible mais l'espece de graphique qu'il te sort le machin mdrrrr je vais regarder ça de plus pres quand je serais plus frais lol , en tout cas merci beaucoup !
++

lunatik
Afficher les 16 commentaires

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.