Showing posts with label c#. Show all posts
Showing posts with label c#. Show all posts

Saturday, July 18, 2009

Some FileInfo Extensions

I kept on needing the same stuff regarding files when writing C#, so I came up with a set of helpers implemented as extensions to the FileInfo class.

Nothing particularly earth-shattering, but you might find them useful.

  • IsDirectory - does the obvious
  • GetMimeType - uses Windows' urlmon.dll to magically determine a file's mime type.
  • GetFileBytes - gets a chunk off the front of a file.
  • GetSha1, and friends - Computes the SHA1 hash of a file. Configurable buffer size.
  • ApplyToFolder - accepts a function to be applied to every file in a hierarchy.

Latest version here.

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text;

namespace QSha
{
public static class FileInfoExtensions
{
public static bool IsDirectory( this FileInfo fi )
{
if ( !( ( FileAttributes.Directory & fi.Attributes ) == FileAttributes.Directory ) )
{
return false;
}
return true;
}


public static byte[] GetFileBytes( this FileInfo fi, long maxBufSize )
{
byte[] buffer = new byte[( fi.Length > maxBufSize ? maxBufSize : fi.Length )];
using ( FileStream fs =
new FileStream( fi.FullName, FileMode.Open, FileAccess.Read, FileShare.Read, buffer.Length ) )
{
fs.Read( buffer, 0, buffer.Length );
fs.Close();
}

return buffer;
}

public static void ApplyToFolder( this FileInfo fi, Func fFileFound )
{
string[] subFolders;
try
{
subFolders = Directory.GetDirectories( fi.FullName );
}
catch ( UnauthorizedAccessException )
{
return;
}

string[] files;
foreach ( string folder in subFolders )
{
FileInfo tmpFi = new FileInfo( folder );
tmpFi.ApplyToFolder( fFileFound );

try
{
files = Directory.GetFiles( folder );
}
catch ( UnauthorizedAccessException )
{
continue;
}

foreach ( string file in files )
{
bool ffres = fFileFound( new FileInfo( file ) );

// we don't do any post-processing, so these are wasted cycles
/*
if ( !ffres )
continue;
*/
}
}
}

// mime-type stuff
public static string GetMimeType( this FileInfo fi )
{

if ( fi.IsDirectory() )
throw new FileNotFoundException( String.Format( "Is a directory, not a file: {0}", fi.FullName ) );

if ( !File.Exists( fi.FullName ) )
throw new FileNotFoundException( fi.FullName + " not found" );


byte[] buffer = new byte[256];
using ( FileStream fs = new FileStream( fi.FullName, FileMode.Open ) )
{
if ( fs.Length >= 256 )
fs.Read( buffer, 0, 256 );
else
fs.Read( buffer, 0, (int)fs.Length );
}
try
{
System.UInt32 mimetype;
FindMimeFromData( 0, null, buffer, 256, null, 0, out mimetype, 0 );
System.IntPtr mimeTypePtr = new IntPtr( mimetype );
string mime = Marshal.PtrToStringUni( mimeTypePtr );
Marshal.FreeCoTaskMem( mimeTypePtr );
return mime;
}
catch ( Exception )
{
return "unknown/unknown";
}
}

[DllImport( @"urlmon.dll", CharSet = CharSet.Auto )]
private extern static System.UInt32 FindMimeFromData(
System.UInt32 pBC,
[MarshalAs( UnmanagedType.LPStr )] System.String pwzUrl,
[MarshalAs( UnmanagedType.LPArray )] byte[] pBuffer,
System.UInt32 cbSize,
[MarshalAs( UnmanagedType.LPStr )] System.String pwzMimeProposed,
System.UInt32 dwMimeFlags,
out System.UInt32 ppwzMimeOut,
System.UInt32 dwReserverd
);
// /mime-type stuff

// hash stuff
public static string GetSha1Base64( this FileInfo fi, long maxBufSize )
{
return Convert.ToBase64String( fi.GetSha1( maxBufSize ) );
}

public static string GetSha1Hex( this FileInfo fi, long maxBufSize )
{
byte[] hash = fi.GetSha1(maxBufSize);
StringBuilder hex = new StringBuilder( hash.Length );
for ( int i = 0; i < hash.Length; i++ )
{
hex.Append( hash[i].ToString( "X2" ) );
}
return hex.ToString();
}

public static byte[] GetSha1( this FileInfo fi, long maxBufSize )
{
string ret = String.Empty;

if ( 0 == fi.Length )
return null;

if( 0 == maxBufSize )
{
maxBufSize = fi.Length;
}
byte[] buffer = fi.GetFileBytes( maxBufSize );

SHA1Managed sha1 = new SHA1Managed();
return sha1.ComputeHash( buffer );
}
}
}

Sunday, June 28, 2009

Custom Exception, Converting enum to String

There has to be a better way to do this...

I've got a custom exception with its own types, etc. It started out like this:
public class MyException : Exception
{
public MyException() : base()
{
}

public MyException( String message ) : base( message )
{
}
}

Nothing fancy there, right? At least I can now filter my catches appropriately.

Well, what if I want to use an enum to define the kinds of failures? Like so:

public enum ExceptionType
{
ScrewedUpA,
ScrewedUpB
}

So far, so good.

Until I want to call it thus:

MyException ex = new MyException( MyException.ExceptionType.ScrewedUpA );
throw ex;

Okay, so we add a constructor which takes a MyException.ExceptionType:
public MyException( ExceptionType exType ) 
{}

That's all very nice, but then what? We need some kind of message, right?

Getting the string from the enum isn't that hard. The enum is represented as an int, the type has fields we can query. The field we're interested in will be offset from the enum's int by one (the first field designates the type of the enum).

We'd need to call the constructor with the String signature, which means using the this object. However, the only place this works is in the function signature. Given all of that, this is what I ended up with:
public class MyException : Exception
{
public MyException() : base()
{
}

public MyException( String message ) : base( message )
{
}

public MyException( ExceptionType exType ) :
this (exType.GetType().GetFields()[(int)exType + 1].Name)
{}

public enum ExceptionType
{
ScrewedUpA,
ScrewedUpB
}
}

If there's a better way to do this, I'd like to know. Thanks.