For the sake of speed and simplicity, I want to
read an embedded resource from a .NET assembly file
without loading this assembly via reflexion. Can
I, for example, read the assembly via File.
ReadAllBytes() and locate an embedded text rea-
source in the byte array or stream?
Here is the simplest thing that works:
using System;
using System.IO;
using System.Collections.Generic;
using System.Text;
using System.Resources;
namespace ResUtil
{
class ShiftedStream: Stream
{ Stream Base;
long Orig;
public override bool CanSeek
{ get
{ return Base.CanSeek; }
}
public override bool CanRead
{ get
{ return Base.CanRead; }
}
public override bool CanTimeout
{
get { return Base.CanTimeout; }
}
public override Boolean CanWrite {
get {
return Base.CanWrite;
}
}
public override long Position
{ get
{ return Base.Position - Orig; }
set
{ Base.Position = value + Orig; }
}
public override Int64 Length {
get { return Base.Length - Orig; }
}
public override void SetLength(Int64 value)
{ Base.SetLength( value + Orig ); }
public override Int32 Read(Byte[] buffer,Int32 offset,Int32 count)
{ return Base.Read( buffer, offset, count ); }
public override void Write(Byte[] buffer,Int32 offset,Int32 count)
{ Base.Write( buffer, offset, count ); }
public ShiftedStream( Stream s )
{ Base = s;
Orig = s.Position;
}
public override long Seek( long ofs, SeekOrigin orig )
{ if( orig == SeekOrigin.Begin )
{ ofs = ofs + Orig; }
return Base.Seek( ofs, orig );
}
public override void Flush()
{ Base.Flush(); }
}
public static class BinResReader
{ // TODO: a more generic overload accepting a stream instead of the file path
public static void Get
( string file, string resName, out string type, out byte[] data )
{ FileStream s;
ResourceReader rr;
long offset;
bool found;
type = null;
data = null;
s = new FileStream( file, FileMode.Open );
while( true )
{ if( !FindResHeader( s ) )
{ break; }
offset = s.Position;
try
{ rr = new ResourceReader( new ShiftedStream( s ) ); }
catch( BadImageFormatException ) // no resource here
{ continue; }
found = true;
// WARN: for the last resource in the file <data> will be read
// through till the end of file.
try
{ rr.GetResourceData( resName, out type, out data ); }
catch // resource res may not be present
{ found = false; }
rr.Dispose();
if( found )
{ break; }
s.Position = offset;
s.ReadByte();
if( !s.CanRead )
{ break; }
}
s.Dispose();
}
public static string GetStr( string file, string resName )
{ return GetStr( file, resName, Encoding.ASCII ); }
public static string GetStr( string file, string resName, Encoding enc )
{ byte[] data;
string text;
string type;
int count;
int index;
int len;
text = null;
Get( file, resName, out type, out data );
if( data != null )
{ switch( type )
{ case "ResourceTypeCode.ByteArray":
count = GetIntBE( data );
index = 4;
break;
case "ResourceTypeCode.String":
len = FindLen( data );
index = UsedBytes( len );
count = len - index;
break;
default: throw new Exception( "Cannot convert resource type " +
type + " to string.");
}
text = enc.GetString( data, index, count );
}
return text;
}
// Since I don't know the length of the length prefix, I must perform
// idiotic C-like acrobatics:
static int FindLen( byte[] data )
{ for( int i = 0; i < data.Length; i++ )
{ if( data[i] == 0 )
{ return i; }
}
return data.Length;
}
static int UsedBytes( int len )
{ int maxLen = Byte.MaxValue;
int i = 1;
while( ( len - i ) > maxLen )
{ i++;
maxLen = maxLen * Byte.MaxValue;
}
return i;
}
static bool FindResHeader( Stream s )
{ int b;
bool found = false;
int looking;
looking = 0;
while( true )
{ b = s.ReadByte();
if( b == -1 )
{ break; }
Repeat: // How would you write it without the goto?
if( b == MagicBytes[looking] )
{ if( looking == 3 )
{ found = true;
s.Seek( -4, SeekOrigin.Current );
break;
}
else
{ looking++; }
}
else
{ if( looking > 0 ) { looking = 0; goto Repeat; } }
}
return found;
}
const int ByteMagn = Byte.MaxValue + 1; // Just in case...
static int GetIntBE( byte[] bytes )
{ int res;
res = 0;
for( int i = 3; i >= 0; i-- )
{ Gorner( ref res, ByteMagn, bytes[i] ); }
return res;
}
static void Gorner( ref int value, int @base, int coef )
{ value = value * @base + coef; }
static int[] MagicBytes;
static BinResReader()
{
uint magic;
MagicBytes = new int[4];
magic = (uint)ResourceManager.MagicNumber;
for( int i = 0; i < 4; i++ )
{ MagicBytes[i] = (int)( magic % ByteMagn );
magic = magic / ByteMagn;
}
}
}
}
It may be improved in several ways, including:
a. using the name of the embedded resource file
in search, in addition to the name of the re-
source,
b. reading several resources in one pass.
--
() ascii ribbon campaign - against html e-mail
/\ http://preview.tinyurl.com/qcy6mjc [archived]