2015-04-07 09:35:12 +01:00
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/ * = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* *
* * Class : ResourceReader
* *
* * < OWNER > [ . . . . ] < / OWNER >
* *
* *
* * Purpose : Default way to read streams of resources on
* * demand .
* *
* * Version 2 support on October 6 , 2003
* *
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = * /
namespace System.Resources {
using System ;
using System.IO ;
using System.Text ;
using System.Collections ;
using System.Collections.Generic ;
#if FEATURE_SERIALIZATION
using System.Runtime.Serialization ;
using System.Runtime.Serialization.Formatters ;
using System.Runtime.Serialization.Formatters.Binary ;
#endif // FEATURE_SERIALIZATION
using System.Reflection ;
using System.Security.Permissions ;
using System.Security ;
using System.Globalization ;
using System.Configuration.Assemblies ;
using System.Runtime.Versioning ;
using System.Diagnostics.Contracts ;
// Provides the default implementation of IResourceReader, reading
// .resources file from the system default binary format. This class
// can be treated as an enumerator once.
//
// See the RuntimeResourceSet overview for details on the system
// default file format.
//
internal struct ResourceLocator
{
internal Object _value ; // Can be null. Consider WeakReference instead?
internal int _dataPos ;
internal ResourceLocator ( int dataPos , Object value )
{
_dataPos = dataPos ;
_value = value ;
}
internal int DataPosition {
get { return _dataPos ; }
//set { _dataPos = value; }
}
// Allows adding in profiling data in a future version, or a special
// resource profiling build. We could also use WeakReference.
internal Object Value {
get { return _value ; }
set { _value = value ; }
}
internal static bool CanCache ( ResourceTypeCode value )
{
Contract . Assert ( value > = 0 , "negative ResourceTypeCode. What?" ) ;
return value < = ResourceTypeCode . LastPrimitive ;
}
}
[System.Runtime.InteropServices.ComVisible(true)]
public sealed class ResourceReader : IResourceReader
{
// A reasonable default buffer size for reading from files, especially
// when we will likely be seeking frequently. Could be smaller, but does
// it make sense to use anything less than one page?
private const int DefaultFileStreamBufferSize = 4096 ;
private BinaryReader _store ; // backing store we're reading from.
// Used by RuntimeResourceSet and this class's enumerator. Maps
// resource name to a value, a ResourceLocator, or a
// LooselyLinkedManifestResource.
internal Dictionary < String , ResourceLocator > _resCache ;
private long _nameSectionOffset ; // Offset to name section of file.
private long _dataSectionOffset ; // Offset to Data section of file.
// Note this class is tightly coupled with UnmanagedMemoryStream.
// At runtime when getting an embedded resource from an assembly,
// we're given an UnmanagedMemoryStream referring to the mmap'ed portion
// of the assembly. The pointers here are pointers into that block of
// memory controlled by the OS's loader.
private int [ ] _nameHashes ; // hash values for all names.
[SecurityCritical]
private unsafe int * _nameHashesPtr ; // In case we're using UnmanagedMemoryStream
private int [ ] _namePositions ; // relative locations of names
[SecurityCritical]
private unsafe int * _namePositionsPtr ; // If we're using UnmanagedMemoryStream
private RuntimeType [ ] _typeTable ; // Lazy array of Types for resource values.
private int [ ] _typeNamePositions ; // To delay initialize type table
#if FEATURE_SERIALIZATION
private BinaryFormatter _objFormatter ; // Deserialization stuff.
#endif // FEATURE_SERIALIZATION
private int _numResources ; // Num of resources files, in case arrays aren't allocated.
// We'll include a separate code path that uses UnmanagedMemoryStream to
// avoid allocating String objects and the like.
private UnmanagedMemoryStream _ums ;
// Version number of .resources file, for compatibility
private int _version ;
#if RESOURCE_FILE_FORMAT_DEBUG
private bool _debug ; // Whether this file has debugging stuff in it.
#endif
#if ! FEATURE_PAL & & FEATURE_SERIALIZATION
private bool [ ] _safeToDeserialize ; // Whether to assert serialization permission
private TypeLimitingDeserializationBinder _typeLimitingBinder ;
// One of our goals is to make sure localizable Windows Forms apps
// work in semi-trusted scenarios (ie, without serialization permission).
// Unfortunate we're serializing out some complex types that currently
// require a security check on deserialization. We may fix this
// in a next version, but for now just hard-code a list.
// Hard-code in the assembly name (minus version) so people can't spoof us.
private static readonly String [ ] TypesSafeForDeserialization = {
"System.String[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.DateTime[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.Bitmap, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.Imaging.Metafile, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.Point, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.PointF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.Size, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.SizeF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.Font, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.Icon, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Drawing.Color, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef . MicrosoftPublicKeyToken ,
"System.Windows.Forms.Cursor, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.Padding, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.LinkArea, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.ImageListStreamer, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.ListViewGroup, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.ListViewItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.ListViewItem+ListViewSubItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.ListViewItem+ListViewSubItem+SubItemStyle, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.OwnerDrawPropertyBag, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken ,
"System.Windows.Forms.TreeNode, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef . EcmaPublicKeyToken
} ;
#endif // !FEATURE_PAL && FEATURE_SERIALIZATION
#if FEATURE_CORECLR
[System.Security.SecurityCritical] // auto-generated
#else
[System.Security.SecuritySafeCritical]
#endif
[ResourceExposure(ResourceScope.Machine)]
[ResourceConsumption(ResourceScope.Machine)]
public ResourceReader ( String fileName )
{
_resCache = new Dictionary < String , ResourceLocator > ( FastResourceComparer . Default ) ;
_store = new BinaryReader ( new FileStream ( fileName , FileMode . Open , FileAccess . Read , FileShare . Read , DefaultFileStreamBufferSize , FileOptions . RandomAccess , Path . GetFileName ( fileName ) , false ) , Encoding . UTF8 ) ;
BCLDebug . Log ( "RESMGRFILEFORMAT" , "ResourceReader .ctor(String). UnmanagedMemoryStream: " + ( _ums ! = null ) ) ;
try {
ReadResources ( ) ;
}
catch {
_store . Close ( ) ; // If we threw an exception, close the file.
throw ;
}
}
[System.Security.SecurityCritical] // auto-generated_required
public ResourceReader ( Stream stream )
{
if ( stream = = null )
throw new ArgumentNullException ( "stream" ) ;
if ( ! stream . CanRead )
throw new ArgumentException ( Environment . GetResourceString ( "Argument_StreamNotReadable" ) ) ;
Contract . EndContractBlock ( ) ;
_resCache = new Dictionary < String , ResourceLocator > ( FastResourceComparer . Default ) ;
_store = new BinaryReader ( stream , Encoding . UTF8 ) ;
// We have a faster code path for reading resource files from an assembly.
_ums = stream as UnmanagedMemoryStream ;
BCLDebug . Log ( "RESMGRFILEFORMAT" , "ResourceReader .ctor(Stream). UnmanagedMemoryStream: " + ( _ums ! = null ) ) ;
ReadResources ( ) ;
}
// This is the constructor the RuntimeResourceSet calls,
// passing in the stream to read from and the RuntimeResourceSet's
// internal hash table (hash table of names with file offsets
// and values, coupled to this ResourceReader).
[System.Security.SecurityCritical] // auto-generated
internal ResourceReader ( Stream stream , Dictionary < String , ResourceLocator > resCache )
{
Contract . Requires ( stream ! = null , "Need a stream!" ) ;
Contract . Requires ( stream . CanRead , "Stream should be readable!" ) ;
Contract . Requires ( resCache ! = null , "Need a Dictionary!" ) ;
_resCache = resCache ;
_store = new BinaryReader ( stream , Encoding . UTF8 ) ;
_ums = stream as UnmanagedMemoryStream ;
BCLDebug . Log ( "RESMGRFILEFORMAT" , "ResourceReader .ctor(Stream, Hashtable). UnmanagedMemoryStream: " + ( _ums ! = null ) ) ;
ReadResources ( ) ;
}
public void Close ( )
{
Dispose ( true ) ;
}
public void Dispose ( )
{
Close ( ) ;
}
[System.Security.SecuritySafeCritical] // auto-generated
private unsafe void Dispose ( bool disposing )
{
if ( _store ! = null ) {
_resCache = null ;
if ( disposing ) {
// Close the stream in a thread-safe way. This fix means
// that we may call Close n times, but that's safe.
BinaryReader copyOfStore = _store ;
_store = null ;
if ( copyOfStore ! = null )
copyOfStore . Close ( ) ;
}
_store = null ;
_namePositions = null ;
_nameHashes = null ;
_ums = null ;
_namePositionsPtr = null ;
_nameHashesPtr = null ;
}
}
[System.Security.SecurityCritical] // auto-generated
internal static unsafe int ReadUnalignedI4 ( int * p )
{
byte * buffer = ( byte * ) p ;
// Unaligned, little endian format
return buffer [ 0 ] | ( buffer [ 1 ] < < 8 ) | ( buffer [ 2 ] < < 16 ) | ( buffer [ 3 ] < < 24 ) ;
}
private void SkipInt32 ( ) {
_store . BaseStream . Seek ( 4 , SeekOrigin . Current ) ;
}
private void SkipString ( ) {
int stringLength = _store . Read7BitEncodedInt ( ) ;
if ( stringLength < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_NegativeStringLength" ) ) ;
}
_store . BaseStream . Seek ( stringLength , SeekOrigin . Current ) ;
}
[System.Security.SecuritySafeCritical] // auto-generated
private unsafe int GetNameHash ( int index )
{
Contract . Assert ( index > = 0 & & index < _numResources , "Bad index into hash array. index: " + index ) ;
Contract . Assert ( ( _ums = = null & & _nameHashes ! = null & & _nameHashesPtr = = null ) | |
( _ums ! = null & & _nameHashes = = null & & _nameHashesPtr ! = null ) , "Internal state mangled." ) ;
if ( _ums = = null )
return _nameHashes [ index ] ;
else
return ReadUnalignedI4 ( & _nameHashesPtr [ index ] ) ;
}
[System.Security.SecuritySafeCritical] // auto-generated
private unsafe int GetNamePosition ( int index )
{
Contract . Assert ( index > = 0 & & index < _numResources , "Bad index into name position array. index: " + index ) ;
Contract . Assert ( ( _ums = = null & & _namePositions ! = null & & _namePositionsPtr = = null ) | |
( _ums ! = null & & _namePositions = = null & & _namePositionsPtr ! = null ) , "Internal state mangled." ) ;
int r ;
if ( _ums = = null )
r = _namePositions [ index ] ;
else
r = ReadUnalignedI4 ( & _namePositionsPtr [ index ] ) ;
if ( r < 0 | | r > _dataSectionOffset - _nameSectionOffset ) {
throw new FormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesNameInvalidOffset" , r ) ) ;
}
return r ;
}
IEnumerator IEnumerable . GetEnumerator ( )
{
return GetEnumerator ( ) ;
}
public IDictionaryEnumerator GetEnumerator ( )
{
if ( _resCache = = null )
throw new InvalidOperationException ( Environment . GetResourceString ( "ResourceReaderIsClosed" ) ) ;
return new ResourceEnumerator ( this ) ;
}
internal ResourceEnumerator GetEnumeratorInternal ( )
{
return new ResourceEnumerator ( this ) ;
}
// From a name, finds the associated virtual offset for the data.
// To read the data, seek to _dataSectionOffset + dataPos, then
// read the resource type & data.
// This does a binary search through the names.
internal int FindPosForResource ( String name )
{
Contract . Assert ( _store ! = null , "ResourceReader is closed!" ) ;
int hash = FastResourceComparer . HashFunction ( name ) ;
BCLDebug . Log ( "RESMGRFILEFORMAT" , "FindPosForResource for " + name + " hash: " + hash . ToString ( "x" , CultureInfo . InvariantCulture ) ) ;
// Binary search over the hashes. Use the _namePositions array to
// determine where they exist in the underlying stream.
int lo = 0 ;
int hi = _numResources - 1 ;
int index = - 1 ;
bool success = false ;
while ( lo < = hi ) {
index = ( lo + hi ) > > 1 ;
// Do NOT use subtraction here, since it will wrap for large
// negative numbers.
int currentHash = GetNameHash ( index ) ;
int c ;
if ( currentHash = = hash )
c = 0 ;
else if ( currentHash < hash )
c = - 1 ;
else
c = 1 ;
//BCLDebug.Log("RESMGRFILEFORMAT", " Probing index "+index+" lo: "+lo+" hi: "+hi+" c: "+c);
if ( c = = 0 ) {
success = true ;
break ;
}
if ( c < 0 )
lo = index + 1 ;
else
hi = index - 1 ;
}
if ( ! success ) {
#if RESOURCE_FILE_FORMAT_DEBUG
String lastReadString ;
lock ( this ) {
_store . BaseStream . Seek ( _nameSectionOffset + GetNamePosition ( index ) , SeekOrigin . Begin ) ;
lastReadString = _store . ReadString ( ) ;
}
BCLDebug . Log ( "RESMGRFILEFORMAT" , LogLevel . Status , "FindPosForResource for " , name , " failed. i: " , index , " lo: " , lo , " hi: " , hi , " last read string: \"" , lastReadString , '\'' ) ;
#endif
return - 1 ;
}
// index is the location in our hash array that corresponds with a
// value in the namePositions array.
// There could be collisions in our hash function. Check on both sides
// of index to find the range of hash values that are equal to the
// target hash value.
if ( lo ! = index ) {
lo = index ;
while ( lo > 0 & & GetNameHash ( lo - 1 ) = = hash )
lo - - ;
}
if ( hi ! = index ) {
hi = index ;
while ( hi < _numResources - 1 & & GetNameHash ( hi + 1 ) = = hash )
hi + + ;
}
lock ( this ) {
for ( int i = lo ; i < = hi ; i + + ) {
_store . BaseStream . Seek ( _nameSectionOffset + GetNamePosition ( i ) , SeekOrigin . Begin ) ;
if ( CompareStringEqualsName ( name ) ) {
int dataPos = _store . ReadInt32 ( ) ;
if ( dataPos < 0 | | dataPos > = _store . BaseStream . Length - _dataSectionOffset ) {
throw new FormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesDataInvalidOffset" , dataPos ) ) ;
}
return dataPos ;
}
}
}
BCLDebug . Log ( "RESMGRFILEFORMAT" , "FindPosForResource for " + name + ": Found a hash collision, HOWEVER, neither of these collided values equaled the given string." ) ;
return - 1 ;
}
// This compares the String in the .resources file at the current position
// with the string you pass in.
// Whoever calls this method should make sure that they take a lock
// so no one else can cause us to seek in the stream.
[System.Security.SecuritySafeCritical] // auto-generated
private unsafe bool CompareStringEqualsName ( String name )
{
Contract . Assert ( _store ! = null , "ResourceReader is closed!" ) ;
int byteLen = _store . Read7BitEncodedInt ( ) ;
if ( byteLen < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_NegativeStringLength" ) ) ;
}
if ( _ums ! = null ) {
byte * bytes = _ums . PositionPointer ;
// Skip over the data in the Stream, positioning ourselves right after it.
_ums . Seek ( byteLen , SeekOrigin . Current ) ;
if ( _ums . Position > _ums . Length ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesNameTooLong" ) ) ;
}
// On 64-bit machines, these char*'s may be misaligned. Use a
// byte-by-byte comparison instead.
//return FastResourceComparer.CompareOrdinal((char*)bytes, byteLen/2, name) == 0;
return FastResourceComparer . CompareOrdinal ( bytes , byteLen , name ) = = 0 ;
}
else {
// This code needs to be fast
byte [ ] bytes = new byte [ byteLen ] ;
int numBytesToRead = byteLen ;
while ( numBytesToRead > 0 ) {
int n = _store . Read ( bytes , byteLen - numBytesToRead , numBytesToRead ) ;
if ( n = = 0 )
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourceNameCorrupted" ) ) ;
numBytesToRead - = n ;
}
return FastResourceComparer . CompareOrdinal ( bytes , byteLen / 2 , name ) = = 0 ;
}
}
// This is used in the enumerator. The enumerator iterates from 0 to n
// of our resources and this returns the resource name for a particular
// index. The parameter is NOT a virtual offset.
[System.Security.SecurityCritical] // auto-generated
private unsafe String AllocateStringForNameIndex ( int index , out int dataOffset )
{
Contract . Assert ( _store ! = null , "ResourceReader is closed!" ) ;
byte [ ] bytes ;
int byteLen ;
long nameVA = GetNamePosition ( index ) ;
lock ( this ) {
_store . BaseStream . Seek ( nameVA + _nameSectionOffset , SeekOrigin . Begin ) ;
// Can't use _store.ReadString, since it's using UTF-8!
byteLen = _store . Read7BitEncodedInt ( ) ;
if ( byteLen < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_NegativeStringLength" ) ) ;
}
if ( _ums ! = null ) {
if ( _ums . Position > _ums . Length - byteLen )
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesIndexTooLong" , index ) ) ;
String s = null ;
char * charPtr = ( char * ) _ums . PositionPointer ;
#if IA64
if ( ( ( int ) charPtr & 1 ) ! = 0 ) {
char [ ] destArray = new char [ byteLen / 2 ] ;
fixed ( char * pDest = destArray ) {
Buffer . Memcpy ( ( byte * ) pDest , ( byte * ) charPtr , byteLen ) ;
}
s = new String ( destArray ) ;
}
else {
#endif //IA64
2015-08-26 07:17:56 -04:00
if ( ! BitConverter . IsLittleEndian ) {
byte * bytePtr = ( byte * ) charPtr ;
var dest = new byte [ byteLen ] ;
for ( int i = 0 ; i < byteLen ; i + = 2 ) {
dest [ i ] = * ( bytePtr + i + 1 ) ;
dest [ i + 1 ] = * ( bytePtr + i ) ;
}
fixed ( byte * pDest = dest ) {
s = new String ( ( char * ) pDest , 0 , byteLen / 2 ) ;
}
} else
s = new String ( charPtr , 0 , byteLen / 2 ) ;
2015-04-07 09:35:12 +01:00
#if IA64
}
#endif //IA64
_ums . Position + = byteLen ;
dataOffset = _store . ReadInt32 ( ) ;
if ( dataOffset < 0 | | dataOffset > = _store . BaseStream . Length - _dataSectionOffset ) {
throw new FormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesDataInvalidOffset" , dataOffset ) ) ;
}
return s ;
}
bytes = new byte [ byteLen ] ;
// We must read byteLen bytes, or we have a corrupted file.
// Use a blocking read in case the stream doesn't give us back
// everything immediately.
int count = byteLen ;
while ( count > 0 ) {
int n = _store . Read ( bytes , byteLen - count , count ) ;
if ( n = = 0 )
throw new EndOfStreamException ( Environment . GetResourceString ( "BadImageFormat_ResourceNameCorrupted_NameIndex" , index ) ) ;
count - = n ;
}
dataOffset = _store . ReadInt32 ( ) ;
if ( dataOffset < 0 | | dataOffset > = _store . BaseStream . Length - _dataSectionOffset ) {
throw new FormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesDataInvalidOffset" , dataOffset ) ) ;
}
}
return Encoding . Unicode . GetString ( bytes , 0 , byteLen ) ;
}
// This is used in the enumerator. The enumerator iterates from 0 to n
// of our resources and this returns the resource value for a particular
// index. The parameter is NOT a virtual offset.
private Object GetValueForNameIndex ( int index )
{
Contract . Assert ( _store ! = null , "ResourceReader is closed!" ) ;
long nameVA = GetNamePosition ( index ) ;
lock ( this ) {
_store . BaseStream . Seek ( nameVA + _nameSectionOffset , SeekOrigin . Begin ) ;
SkipString ( ) ;
//BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex for index: "+index+" skip (name length): "+skip);
int dataPos = _store . ReadInt32 ( ) ;
if ( dataPos < 0 | | dataPos > = _store . BaseStream . Length - _dataSectionOffset ) {
throw new FormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesDataInvalidOffset" , dataPos ) ) ;
}
BCLDebug . Log ( "RESMGRFILEFORMAT" , "GetValueForNameIndex: dataPos: " + dataPos ) ;
ResourceTypeCode junk ;
if ( _version = = 1 )
return LoadObjectV1 ( dataPos ) ;
else
return LoadObjectV2 ( dataPos , out junk ) ;
}
}
// This takes a virtual offset into the data section and reads a String
// from that location.
// Anyone who calls LoadObject should make sure they take a lock so
// no one can cause us to do a seek in here.
internal String LoadString ( int pos )
{
Contract . Assert ( _store ! = null , "ResourceReader is closed!" ) ;
_store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
String s = null ;
int typeIndex = _store . Read7BitEncodedInt ( ) ;
if ( _version = = 1 ) {
if ( typeIndex = = - 1 )
return null ;
if ( FindType ( typeIndex ) ! = typeof ( String ) )
throw new InvalidOperationException ( Environment . GetResourceString ( "InvalidOperation_ResourceNotString_Type" , FindType ( typeIndex ) . FullName ) ) ;
s = _store . ReadString ( ) ;
}
else {
ResourceTypeCode typeCode = ( ResourceTypeCode ) typeIndex ;
if ( typeCode ! = ResourceTypeCode . String & & typeCode ! = ResourceTypeCode . Null ) {
String typeString ;
if ( typeCode < ResourceTypeCode . StartOfUserTypes )
typeString = typeCode . ToString ( ) ;
else
typeString = FindType ( typeCode - ResourceTypeCode . StartOfUserTypes ) . FullName ;
throw new InvalidOperationException ( Environment . GetResourceString ( "InvalidOperation_ResourceNotString_Type" , typeString ) ) ;
}
if ( typeCode = = ResourceTypeCode . String ) // ignore Null
s = _store . ReadString ( ) ;
}
BCLDebug . Log ( "RESMGRFILEFORMAT" , "LoadString(" + pos . ToString ( "x" , CultureInfo . InvariantCulture ) + " returned " + ( s = = null ? "[a null string]" : s ) ) ;
return s ;
}
// Called from RuntimeResourceSet
internal Object LoadObject ( int pos )
{
if ( _version = = 1 )
return LoadObjectV1 ( pos ) ;
ResourceTypeCode typeCode ;
return LoadObjectV2 ( pos , out typeCode ) ;
}
internal Object LoadObject ( int pos , out ResourceTypeCode typeCode )
{
if ( _version = = 1 ) {
Object o = LoadObjectV1 ( pos ) ;
typeCode = ( o is String ) ? ResourceTypeCode . String : ResourceTypeCode . StartOfUserTypes ;
return o ;
}
return LoadObjectV2 ( pos , out typeCode ) ;
}
// This takes a virtual offset into the data section and reads an Object
// from that location.
// Anyone who calls LoadObject should make sure they take a lock so
// no one can cause us to do a seek in here.
internal Object LoadObjectV1 ( int pos )
{
Contract . Assert ( _store ! = null , "ResourceReader is closed!" ) ;
Contract . Assert ( _version = = 1 , ".resources file was not a V1 .resources file!" ) ;
try {
// mega try-catch performs exceptionally bad on x64; factored out body into
// _LoadObjectV1 and wrap here.
return _LoadObjectV1 ( pos ) ;
}
catch ( EndOfStreamException eof ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_TypeMismatch" ) , eof ) ;
}
catch ( ArgumentOutOfRangeException e ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_TypeMismatch" ) , e ) ;
}
}
#if FEATURE_SERIALIZATION
[SecuritySafeCritical]
#endif
private Object _LoadObjectV1 ( int pos ) {
_store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
int typeIndex = _store . Read7BitEncodedInt ( ) ;
if ( typeIndex = = - 1 )
return null ;
RuntimeType type = FindType ( typeIndex ) ;
BCLDebug . Log ( "RESMGRFILEFORMAT" , "LoadObject type: " + type . Name + " pos: 0x" + _store . BaseStream . Position . ToString ( "x" , CultureInfo . InvariantCulture ) ) ;
// Consider putting in logic to see if this type is a
// primitive or a value type first, so we can reach the
// deserialization code faster for arbitrary objects.
if ( type = = typeof ( String ) )
return _store . ReadString ( ) ;
else if ( type = = typeof ( Int32 ) )
return _store . ReadInt32 ( ) ;
else if ( type = = typeof ( Byte ) )
return _store . ReadByte ( ) ;
else if ( type = = typeof ( SByte ) )
return _store . ReadSByte ( ) ;
else if ( type = = typeof ( Int16 ) )
return _store . ReadInt16 ( ) ;
else if ( type = = typeof ( Int64 ) )
return _store . ReadInt64 ( ) ;
else if ( type = = typeof ( UInt16 ) )
return _store . ReadUInt16 ( ) ;
else if ( type = = typeof ( UInt32 ) )
return _store . ReadUInt32 ( ) ;
else if ( type = = typeof ( UInt64 ) )
return _store . ReadUInt64 ( ) ;
else if ( type = = typeof ( Single ) )
return _store . ReadSingle ( ) ;
else if ( type = = typeof ( Double ) )
return _store . ReadDouble ( ) ;
else if ( type = = typeof ( DateTime ) ) {
// Ideally we should use DateTime's ToBinary & FromBinary,
// but we can't for compatibility reasons.
return new DateTime ( _store . ReadInt64 ( ) ) ;
}
else if ( type = = typeof ( TimeSpan ) )
return new TimeSpan ( _store . ReadInt64 ( ) ) ;
else if ( type = = typeof ( Decimal ) ) {
int [ ] bits = new int [ 4 ] ;
for ( int i = 0 ; i < bits . Length ; i + + )
bits [ i ] = _store . ReadInt32 ( ) ;
return new Decimal ( bits ) ;
}
else {
#if FEATURE_SERIALIZATION
return DeserializeObject ( typeIndex ) ;
#else
throw new NotSupportedException ( Environment . GetResourceString ( "NotSupported_ResourceObjectSerialization" ) ) ;
#endif // FEATURE_SERIALIZATION
}
}
internal Object LoadObjectV2 ( int pos , out ResourceTypeCode typeCode )
{
Contract . Assert ( _store ! = null , "ResourceReader is closed!" ) ;
Contract . Assert ( _version > = 2 , ".resources file was not a V2 (or higher) .resources file!" ) ;
try {
// mega try-catch performs exceptionally bad on x64; factored out body into
// _LoadObjectV2 and wrap here.
return _LoadObjectV2 ( pos , out typeCode ) ;
}
catch ( EndOfStreamException eof ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_TypeMismatch" ) , eof ) ;
}
catch ( ArgumentOutOfRangeException e ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_TypeMismatch" ) , e ) ;
}
}
[System.Security.SecuritySafeCritical] // auto-generated
private Object _LoadObjectV2 ( int pos , out ResourceTypeCode typeCode ) {
_store . BaseStream . Seek ( _dataSectionOffset + pos , SeekOrigin . Begin ) ;
typeCode = ( ResourceTypeCode ) _store . Read7BitEncodedInt ( ) ;
BCLDebug . Log ( "RESMGRFILEFORMAT" , "LoadObjectV2 type: " + typeCode + " pos: 0x" + _store . BaseStream . Position . ToString ( "x" , CultureInfo . InvariantCulture ) ) ;
switch ( typeCode ) {
case ResourceTypeCode . Null :
return null ;
case ResourceTypeCode . String :
return _store . ReadString ( ) ;
case ResourceTypeCode . Boolean :
return _store . ReadBoolean ( ) ;
case ResourceTypeCode . Char :
return ( char ) _store . ReadUInt16 ( ) ;
case ResourceTypeCode . Byte :
return _store . ReadByte ( ) ;
case ResourceTypeCode . SByte :
return _store . ReadSByte ( ) ;
case ResourceTypeCode . Int16 :
return _store . ReadInt16 ( ) ;
case ResourceTypeCode . UInt16 :
return _store . ReadUInt16 ( ) ;
case ResourceTypeCode . Int32 :
return _store . ReadInt32 ( ) ;
case ResourceTypeCode . UInt32 :
return _store . ReadUInt32 ( ) ;
case ResourceTypeCode . Int64 :
return _store . ReadInt64 ( ) ;
case ResourceTypeCode . UInt64 :
return _store . ReadUInt64 ( ) ;
case ResourceTypeCode . Single :
return _store . ReadSingle ( ) ;
case ResourceTypeCode . Double :
return _store . ReadDouble ( ) ;
case ResourceTypeCode . Decimal :
return _store . ReadDecimal ( ) ;
case ResourceTypeCode . DateTime :
// Use DateTime's ToBinary & FromBinary.
Int64 data = _store . ReadInt64 ( ) ;
return DateTime . FromBinary ( data ) ;
case ResourceTypeCode . TimeSpan :
Int64 ticks = _store . ReadInt64 ( ) ;
return new TimeSpan ( ticks ) ;
// Special types
case ResourceTypeCode . ByteArray : {
int len = _store . ReadInt32 ( ) ;
if ( len < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourceDataLengthInvalid" , len ) ) ;
}
if ( _ums = = null ) {
if ( len > _store . BaseStream . Length ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourceDataLengthInvalid" , len ) ) ;
}
return _store . ReadBytes ( len ) ;
}
if ( len > _ums . Length - _ums . Position ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourceDataLengthInvalid" , len ) ) ;
}
byte [ ] bytes = new byte [ len ] ;
int r = _ums . Read ( bytes , 0 , len ) ;
Contract . Assert ( r = = len , "ResourceReader needs to use a blocking read here. (Call _store.ReadBytes(len)?)" ) ;
return bytes ;
}
case ResourceTypeCode . Stream : {
int len = _store . ReadInt32 ( ) ;
if ( len < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourceDataLengthInvalid" , len ) ) ;
}
if ( _ums = = null ) {
byte [ ] bytes = _store . ReadBytes ( len ) ;
// Lifetime of memory == lifetime of this stream.
return new PinnedBufferMemoryStream ( bytes ) ;
}
// make sure we don't create an UnmanagedMemoryStream that is longer than the resource stream.
if ( len > _ums . Length - _ums . Position ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourceDataLengthInvalid" , len ) ) ;
}
// For the case that we've memory mapped in the .resources
// file, just return a Stream pointing to that block of memory.
unsafe {
return new UnmanagedMemoryStream ( _ums . PositionPointer , len , len , FileAccess . Read , true ) ;
}
}
default :
if ( typeCode < ResourceTypeCode . StartOfUserTypes ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_TypeMismatch" ) ) ;
}
break ;
}
// Normal serialized objects
#if FEATURE_SERIALIZATION
int typeIndex = typeCode - ResourceTypeCode . StartOfUserTypes ;
return DeserializeObject ( typeIndex ) ;
#else
throw new NotSupportedException ( Environment . GetResourceString ( "NotSupported_ResourceObjectSerialization" ) ) ;
#endif // FEATURE_SERIALIZATION
}
#if FEATURE_SERIALIZATION
// Helper method to safely deserialize a type, using a type-limiting
// deserialization binder to simulate a type-limiting deserializer.
// This method handles types that are safe to deserialize, as well as
// ensuring we only get back what we expect.
[System.Security.SecurityCritical] // auto-generated
private Object DeserializeObject ( int typeIndex )
{
RuntimeType type = FindType ( typeIndex ) ;
#if ! FEATURE_PAL
// Initialize deserialization permission array, if needed
if ( _safeToDeserialize = = null )
InitSafeToDeserializeArray ( ) ;
#endif // !FEATURE_PAL
// Ensure that the object we deserialized is exactly the same
// type of object we thought we should be deserializing. This
// will help prevent hacked .resources files from using our
// serialization permission assert to deserialize anything
// via a hacked type ID.
Object graph ;
#if FEATURE_PAL
graph = _objFormatter . Deserialize ( _store . BaseStream ) ;
#else
if ( _safeToDeserialize [ typeIndex ] ) {
// Don't assert serialization permission - just ask the binary
// formatter to avoid a permission check. This ensures that any
// types which do demand serialization permission in their
// deserialization .cctors will fail.
// Also, use a serialization binder to limit bind requests to
// our allowed list of [....] types.
_objFormatter . Binder = _typeLimitingBinder ;
_typeLimitingBinder . ExpectingToDeserialize ( type ) ;
graph = _objFormatter . UnsafeDeserialize ( _store . BaseStream , null ) ;
}
else {
_objFormatter . Binder = null ;
graph = _objFormatter . Deserialize ( _store . BaseStream ) ;
}
#endif // FEATURE_PAL
// This check is about correctness, not security at this point.
if ( graph . GetType ( ) ! = type )
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResType&SerBlobMismatch" , type . FullName , graph . GetType ( ) . FullName ) ) ;
return graph ;
}
#endif // FEATURE_SERIALIZATION
// Reads in the header information for a .resources file. Verifies some
// of the assumptions about this resource set, and builds the class table
// for the default resource file format.
[System.Security.SecurityCritical] // auto-generated
private void ReadResources ( )
{
Contract . Assert ( _store ! = null , "ResourceReader is closed!" ) ;
#if FEATURE_SERIALIZATION
BinaryFormatter bf = new BinaryFormatter ( null , new StreamingContext ( StreamingContextStates . File | StreamingContextStates . Persistence ) ) ;
#if ! FEATURE_PAL
_typeLimitingBinder = new TypeLimitingDeserializationBinder ( ) ;
bf . Binder = _typeLimitingBinder ;
#endif
_objFormatter = bf ;
#endif // FEATURE_SERIALIZATION
try {
// mega try-catch performs exceptionally bad on x64; factored out body into
// _ReadResources and wrap here.
_ReadResources ( ) ;
}
catch ( EndOfStreamException eof ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) , eof ) ;
}
catch ( IndexOutOfRangeException e ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) , e ) ;
}
}
[System.Security.SecurityCritical] // auto-generated
private void _ReadResources ( )
{
// Read ResourceManager header
// Check for magic number
int magicNum = _store . ReadInt32 ( ) ;
if ( magicNum ! = ResourceManager . MagicNumber )
throw new ArgumentException ( Environment . GetResourceString ( "Resources_StreamNotValid" ) ) ;
// Assuming this is ResourceManager header V1 or greater, hopefully
// after the version number there is a number of bytes to skip
// to bypass the rest of the ResMgr header. For V2 or greater, we
// use this to skip to the end of the header
int resMgrHeaderVersion = _store . ReadInt32 ( ) ;
int numBytesToSkip = _store . ReadInt32 ( ) ;
if ( numBytesToSkip < 0 | | resMgrHeaderVersion < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) ) ;
}
if ( resMgrHeaderVersion > 1 ) {
BCLDebug . Log ( "RESMGRFILEFORMAT" , LogLevel . Status , "ReadResources: Unexpected ResMgr header version: {0} Skipping ahead {1} bytes." , resMgrHeaderVersion , numBytesToSkip ) ;
_store . BaseStream . Seek ( numBytesToSkip , SeekOrigin . Current ) ;
}
else {
BCLDebug . Log ( "RESMGRFILEFORMAT" , "ReadResources: Parsing ResMgr header v1." ) ;
// We don't care about numBytesToSkip; read the rest of the header
// Read in type name for a suitable ResourceReader
// Note ResourceWriter & InternalResGen use different Strings.
String readerType = _store . ReadString ( ) ;
AssemblyName mscorlib = new AssemblyName ( ResourceManager . MscorlibName ) ;
if ( ! ResourceManager . CompareNames ( readerType , ResourceManager . ResReaderTypeName , mscorlib ) )
throw new NotSupportedException ( Environment . GetResourceString ( "NotSupported_WrongResourceReader_Type" , readerType ) ) ;
// Skip over type name for a suitable ResourceSet
SkipString ( ) ;
}
// Read RuntimeResourceSet header
// Do file version check
int version = _store . ReadInt32 ( ) ;
if ( version ! = RuntimeResourceSet . Version & & version ! = 1 )
throw new ArgumentException ( Environment . GetResourceString ( "Arg_ResourceFileUnsupportedVersion" , RuntimeResourceSet . Version , version ) ) ;
_version = version ;
#if RESOURCE_FILE_FORMAT_DEBUG
// Look for ***DEBUG*** to see if this is a debuggable file.
long oldPos = _store . BaseStream . Position ;
_debug = false ;
try {
String debugString = _store . ReadString ( ) ;
_debug = String . Equals ( "***DEBUG***" , debugString ) ;
}
catch ( IOException ) {
}
catch ( OutOfMemoryException ) {
}
if ( _debug ) {
Console . WriteLine ( "ResourceReader is looking at a debuggable .resources file, version {0}" , _version ) ;
}
else {
_store . BaseStream . Position = oldPos ;
}
#endif
_numResources = _store . ReadInt32 ( ) ;
if ( _numResources < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) ) ;
}
BCLDebug . Log ( "RESMGRFILEFORMAT" , "ReadResources: Expecting " + _numResources + " resources." ) ;
#if _DEBUG
if ( ResourceManager . DEBUG > = 4 )
Console . WriteLine ( "ResourceReader::ReadResources - Reading in " + _numResources + " resources" ) ;
#endif
// Read type positions into type positions array.
// But delay initialize the type table.
int numTypes = _store . ReadInt32 ( ) ;
if ( numTypes < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) ) ;
}
_typeTable = new RuntimeType [ numTypes ] ;
_typeNamePositions = new int [ numTypes ] ;
for ( int i = 0 ; i < numTypes ; i + + ) {
_typeNamePositions [ i ] = ( int ) _store . BaseStream . Position ;
// Skip over the Strings in the file. Don't create types.
SkipString ( ) ;
}
#if _DEBUG
if ( ResourceManager . DEBUG > = 5 )
Console . WriteLine ( "ResourceReader::ReadResources - Reading in " + numTypes + " type table entries" ) ;
#endif
// Prepare to read in the array of name hashes
// Note that the name hashes array is aligned to 8 bytes so
// we can use pointers into it on 64 bit machines. (4 bytes
// may be sufficient, but let's plan for the future)
// Skip over alignment stuff. All public .resources files
// should be aligned No need to verify the byte values.
long pos = _store . BaseStream . Position ;
int alignBytes = ( ( int ) pos ) & 7 ;
if ( alignBytes ! = 0 ) {
for ( int i = 0 ; i < 8 - alignBytes ; i + + ) {
_store . ReadByte ( ) ;
}
}
// Read in the array of name hashes
#if RESOURCE_FILE_FORMAT_DEBUG
// Skip over "HASHES->"
if ( _debug ) {
_store . BaseStream . Position + = 8 ;
}
#endif
if ( _ums = = null ) {
_nameHashes = new int [ _numResources ] ;
for ( int i = 0 ; i < _numResources ; i + + ) {
_nameHashes [ i ] = _store . ReadInt32 ( ) ;
}
}
else {
int seekPos = unchecked ( 4 * _numResources ) ;
if ( seekPos < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) ) ;
}
unsafe {
_nameHashesPtr = ( int * ) _ums . PositionPointer ;
// Skip over the array of nameHashes.
_ums . Seek ( seekPos , SeekOrigin . Current ) ;
// get the position pointer once more to check that the whole table is within the stream
byte * junk = _ums . PositionPointer ;
}
}
// Read in the array of relative positions for all the names.
#if RESOURCE_FILE_FORMAT_DEBUG
// Skip over "POS---->"
if ( _debug ) {
_store . BaseStream . Position + = 8 ;
}
#endif
if ( _ums = = null ) {
_namePositions = new int [ _numResources ] ;
for ( int i = 0 ; i < _numResources ; i + + ) {
int namePosition = _store . ReadInt32 ( ) ;
if ( namePosition < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) ) ;
}
_namePositions [ i ] = namePosition ;
}
}
else {
int seekPos = unchecked ( 4 * _numResources ) ;
if ( seekPos < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) ) ;
}
unsafe {
_namePositionsPtr = ( int * ) _ums . PositionPointer ;
// Skip over the array of namePositions.
_ums . Seek ( seekPos , SeekOrigin . Current ) ;
// get the position pointer once more to check that the whole table is within the stream
byte * junk = _ums . PositionPointer ;
}
}
// Read location of data section.
_dataSectionOffset = _store . ReadInt32 ( ) ;
if ( _dataSectionOffset < 0 ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) ) ;
}
// Store current location as start of name section
_nameSectionOffset = _store . BaseStream . Position ;
// _nameSectionOffset should be <= _dataSectionOffset; if not, it's corrupt
if ( _dataSectionOffset < _nameSectionOffset ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesHeaderCorrupted" ) ) ;
}
BCLDebug . Log ( "RESMGRFILEFORMAT" , String . Format ( CultureInfo . InvariantCulture , "ReadResources: _nameOffset = 0x{0:x} _dataOffset = 0x{1:x}" , _nameSectionOffset , _dataSectionOffset ) ) ;
}
// This allows us to delay-initialize the Type[]. This might be a
// good startup time savings, since we might have to load assemblies
// and initialize Reflection.
private RuntimeType FindType ( int typeIndex )
{
if ( typeIndex < 0 | | typeIndex > = _typeTable . Length ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_InvalidType" ) ) ;
}
if ( _typeTable [ typeIndex ] = = null ) {
long oldPos = _store . BaseStream . Position ;
try {
_store . BaseStream . Position = _typeNamePositions [ typeIndex ] ;
String typeName = _store . ReadString ( ) ;
_typeTable [ typeIndex ] = ( RuntimeType ) Type . GetType ( typeName , true ) ;
}
#if ! FEATURE_SERIALIZATION
// If serialization isn't supported, we convert FileNotFoundException to
// NotSupportedException for consistency with v2. This is a corner-case, but the
// idea is that we want to give the user a more accurate error message. Even if
// the dependency were found, we know it will require serialization since it
// can't be one of the types we special case. So if the dependency were found,
// it would go down the serialization code path, resulting in NotSupported for
// SKUs without serialization.
//
// We don't want to regress the expected case by checking the type info before
// getting to Type.GetType -- this is costly with v1 resource formats.
catch ( FileNotFoundException )
{
throw new NotSupportedException ( Environment . GetResourceString ( "NotSupported_ResourceObjectSerialization" ) ) ;
}
#endif // FEATURE_SERIALIZATION
finally {
_store . BaseStream . Position = oldPos ;
}
}
Contract . Assert ( _typeTable [ typeIndex ] ! = null , "Should have found a type!" ) ;
return _typeTable [ typeIndex ] ;
}
#if ! FEATURE_PAL & & FEATURE_SERIALIZATION
[System.Security.SecurityCritical] // auto-generated
private void InitSafeToDeserializeArray ( )
{
_safeToDeserialize = new bool [ _typeTable . Length ] ;
for ( int i = 0 ; i < _typeTable . Length ; i + + ) {
long oldPos = _store . BaseStream . Position ;
String typeName ;
try {
_store . BaseStream . Position = _typeNamePositions [ i ] ;
typeName = _store . ReadString ( ) ;
}
finally {
_store . BaseStream . Position = oldPos ;
}
AssemblyName an ;
String typePart ;
RuntimeType resourceType = ( RuntimeType ) Type . GetType ( typeName , false ) ;
if ( resourceType = = null ) {
an = null ;
typePart = typeName ;
}
else {
// Enums should be safe to deserialize, and this helps out
// partially trusted, localized [....] apps.
if ( resourceType . BaseType = = typeof ( Enum ) ) {
_safeToDeserialize [ i ] = true ;
continue ;
}
// For most types, check our TypesSafeForDeserialization.
typePart = resourceType . FullName ;
an = new AssemblyName ( ) ;
// resourceType is retrieved via Type.GetType and must be a RuntimeType
RuntimeAssembly a = ( RuntimeAssembly ) resourceType . Assembly ;
an . Init ( a . GetSimpleName ( ) ,
a . GetPublicKey ( ) ,
null , // public key token
null , // version
a . GetLocale ( ) ,
AssemblyHashAlgorithm . None ,
AssemblyVersionCompatibility . SameMachine ,
null , // codebase
AssemblyNameFlags . PublicKey ,
null ) ; // strong name key pair
}
foreach ( String safeType in TypesSafeForDeserialization ) {
if ( ResourceManager . CompareNames ( safeType , typePart , an ) ) {
#if _DEBUG
if ( ResourceManager . DEBUG > = 7 )
Console . WriteLine ( "ResReader: Found a type-safe type to deserialize! Type name: {0}" , typeName ) ;
#endif
_safeToDeserialize [ i ] = true ;
continue ;
}
}
#if _DEBUG
if ( ResourceManager . DEBUG > = 7 )
if ( ! _safeToDeserialize [ i ] )
Console . WriteLine ( "ResReader: Found a type that wasn't safe to deserialize: {0}" , typeName ) ;
#endif
}
}
#endif // !FEATURE_PAL && FEATURE_SERIALIZATION
public void GetResourceData ( String resourceName , out String resourceType , out byte [ ] resourceData )
{
if ( resourceName = = null )
throw new ArgumentNullException ( "resourceName" ) ;
Contract . EndContractBlock ( ) ;
if ( _resCache = = null )
throw new InvalidOperationException ( Environment . GetResourceString ( "ResourceReaderIsClosed" ) ) ;
// Get the type information from the data section. Also,
// sort all of the data section's indexes to compute length of
// the serialized data for this type (making sure to subtract
// off the length of the type code).
int [ ] sortedDataPositions = new int [ _numResources ] ;
int dataPos = FindPosForResource ( resourceName ) ;
if ( dataPos = = - 1 ) {
throw new ArgumentException ( Environment . GetResourceString ( "Arg_ResourceNameNotExist" , resourceName ) ) ;
}
lock ( this ) {
// Read all the positions of data within the data section.
for ( int i = 0 ; i < _numResources ; i + + ) {
_store . BaseStream . Position = _nameSectionOffset + GetNamePosition ( i ) ;
// Skip over name of resource
int numBytesToSkip = _store . Read7BitEncodedInt ( ) ;
if ( numBytesToSkip < 0 ) {
throw new FormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesNameInvalidOffset" , numBytesToSkip ) ) ;
}
_store . BaseStream . Position + = numBytesToSkip ;
int dPos = _store . ReadInt32 ( ) ;
if ( dPos < 0 | | dPos > = _store . BaseStream . Length - _dataSectionOffset ) {
throw new FormatException ( Environment . GetResourceString ( "BadImageFormat_ResourcesDataInvalidOffset" , dPos ) ) ;
}
sortedDataPositions [ i ] = dPos ;
}
Array . Sort ( sortedDataPositions ) ;
int index = Array . BinarySearch ( sortedDataPositions , dataPos ) ;
Contract . Assert ( index > = 0 & & index < _numResources , "Couldn't find data position within sorted data positions array!" ) ;
long nextData = ( index < _numResources - 1 ) ? sortedDataPositions [ index + 1 ] + _dataSectionOffset : _store . BaseStream . Length ;
int len = ( int ) ( nextData - ( dataPos + _dataSectionOffset ) ) ;
Contract . Assert ( len > = 0 & & len < = ( int ) _store . BaseStream . Length - dataPos + _dataSectionOffset , "Length was negative or outside the bounds of the file!" ) ;
// Read type code then byte[]
_store . BaseStream . Position = _dataSectionOffset + dataPos ;
ResourceTypeCode typeCode = ( ResourceTypeCode ) _store . Read7BitEncodedInt ( ) ;
if ( typeCode < 0 | | typeCode > = ResourceTypeCode . StartOfUserTypes + _typeTable . Length ) {
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_InvalidType" ) ) ;
}
resourceType = TypeNameFromTypeCode ( typeCode ) ;
// The length must be adjusted to subtract off the number
// of bytes in the 7 bit encoded type code.
len - = ( int ) ( _store . BaseStream . Position - ( _dataSectionOffset + dataPos ) ) ;
byte [ ] bytes = _store . ReadBytes ( len ) ;
if ( bytes . Length ! = len )
throw new FormatException ( Environment . GetResourceString ( "BadImageFormat_ResourceNameCorrupted" ) ) ;
resourceData = bytes ;
}
}
private String TypeNameFromTypeCode ( ResourceTypeCode typeCode )
{
Contract . Requires ( typeCode > = 0 , "can't be negative" ) ;
if ( typeCode < ResourceTypeCode . StartOfUserTypes ) {
Contract . Assert ( ! String . Equals ( typeCode . ToString ( ) , "LastPrimitive" ) , "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers." ) ;
return "ResourceTypeCode." + typeCode . ToString ( ) ;
}
else {
int typeIndex = typeCode - ResourceTypeCode . StartOfUserTypes ;
Contract . Assert ( typeIndex > = 0 & & typeIndex < _typeTable . Length , "TypeCode is broken or corrupted!" ) ;
long oldPos = _store . BaseStream . Position ;
try {
_store . BaseStream . Position = _typeNamePositions [ typeIndex ] ;
return _store . ReadString ( ) ;
}
finally {
_store . BaseStream . Position = oldPos ;
}
}
}
#if ! FEATURE_PAL & & FEATURE_SERIALIZATION
// We need to build a type-limiting deserializer. We know exactly which
// type we want to deserialize, and if someone tells us we have type X
// (which might be safe to deserialize) and they give us a serialized
// form of type Y, we don't want to run the deserialization constructor
// because we've asserted serialization formatter permission. Instead,
// limit the binary formatter's type binding to precisely the type we
// expect. If they ever don't match, that's a corrupt .resources file.
// We also must check the complete object graph to ensure all of the
// graph contains safe objects.
// Note this is tightly coupled to the BinaryFormatter, since we use
// its internal ObjectReader::FastBindToType method, and we had to
// change the ObjectReader to register itself with this type.
internal sealed class TypeLimitingDeserializationBinder : SerializationBinder
{
private RuntimeType _typeToDeserialize ;
// This is tightly coupled with the binary formatter, because we
// want to use exactly the same code found in the ObjectReader
// to do the lookup, then just give a thumbs up or down based on
// a type equality comparison. In the future, we could consider
// some better refactoring of this code.
private ObjectReader _objectReader ;
internal ObjectReader ObjectReader {
get { return _objectReader ; }
set { _objectReader = value ; }
}
internal void ExpectingToDeserialize ( RuntimeType type )
{
_typeToDeserialize = type ;
}
[System.Security.SecuritySafeCritical] // overrides transparent public member
public override Type BindToType ( string assemblyName , string typeName )
{
// BinaryObjectReader::Bind tries us first, then its own code.
// Returning null means let the default binding rules happen.
AssemblyName an = new AssemblyName ( assemblyName ) ;
bool safe = false ;
foreach ( String safeType in TypesSafeForDeserialization ) {
if ( ResourceManager . CompareNames ( safeType , typeName , an ) ) {
safe = true ;
break ;
}
}
// [....] types may internally use some enums that aren't
// on our safe to deserialize list, like Font using FontStyle.
Type t = ObjectReader . FastBindToType ( assemblyName , typeName ) ;
if ( t . IsEnum )
safe = true ;
if ( safe )
return null ;
// Throw instead of returning null.
// If you're looking at this in a debugger, you've either
// got a hacked .resources file on your hands, or [....]
// types have taken a new dependency on another type. Check
// whether assemblyName & typeName refer to a trustworthy type,
// & consider adding it to the TypesSafeToDeserialize list.
throw new BadImageFormatException ( Environment . GetResourceString ( "BadImageFormat_ResType&SerBlobMismatch" , _typeToDeserialize . FullName , typeName ) ) ;
}
}
#endif // !FEATURE_PAL && FEATURE_SERIALIZATION
internal sealed class ResourceEnumerator : IDictionaryEnumerator
{
private const int ENUM_DONE = Int32 . MinValue ;
private const int ENUM_NOT_STARTED = - 1 ;
private ResourceReader _reader ;
private bool _currentIsValid ;
private int _currentName ;
private int _dataPosition ; // cached for case-insensitive table
internal ResourceEnumerator ( ResourceReader reader )
{
_currentName = ENUM_NOT_STARTED ;
_reader = reader ;
_dataPosition = - 2 ;
}
public bool MoveNext ( )
{
if ( _currentName = = _reader . _numResources - 1 | | _currentName = = ENUM_DONE ) {
_currentIsValid = false ;
_currentName = ENUM_DONE ;
return false ;
}
_currentIsValid = true ;
_currentName + + ;
return true ;
}
public Object Key {
[System.Security.SecuritySafeCritical] // auto-generated
get {
if ( _currentName = = ENUM_DONE ) throw new InvalidOperationException ( Environment . GetResourceString ( ResId . InvalidOperation_EnumEnded ) ) ;
if ( ! _currentIsValid ) throw new InvalidOperationException ( Environment . GetResourceString ( ResId . InvalidOperation_EnumNotStarted ) ) ;
if ( _reader . _resCache = = null ) throw new InvalidOperationException ( Environment . GetResourceString ( "ResourceReaderIsClosed" ) ) ;
return _reader . AllocateStringForNameIndex ( _currentName , out _dataPosition ) ;
}
}
public Object Current {
get {
return Entry ;
}
}
// Warning: This requires that you call the Key or Entry property FIRST before calling it!
internal int DataPosition {
get {
return _dataPosition ;
}
}
public DictionaryEntry Entry {
[System.Security.SecuritySafeCritical] // auto-generated
get {
if ( _currentName = = ENUM_DONE ) throw new InvalidOperationException ( Environment . GetResourceString ( ResId . InvalidOperation_EnumEnded ) ) ;
if ( ! _currentIsValid ) throw new InvalidOperationException ( Environment . GetResourceString ( ResId . InvalidOperation_EnumNotStarted ) ) ;
if ( _reader . _resCache = = null ) throw new InvalidOperationException ( Environment . GetResourceString ( "ResourceReaderIsClosed" ) ) ;
String key ;
Object value = null ;
lock ( _reader . _resCache ) {
key = _reader . AllocateStringForNameIndex ( _currentName , out _dataPosition ) ;
ResourceLocator locator ;
if ( _reader . _resCache . TryGetValue ( key , out locator ) ) {
value = locator . Value ;
}
if ( value = = null ) {
if ( _dataPosition = = - 1 )
value = _reader . GetValueForNameIndex ( _currentName ) ;
else
value = _reader . LoadObject ( _dataPosition ) ;
// If enumeration and subsequent lookups happen very
// frequently in the same process, add a ResourceLocator
// to _resCache here. But [....] enumerates and
// just about everyone else does lookups. So caching
// here may bloat working set.
}
}
return new DictionaryEntry ( key , value ) ;
}
}
public Object Value {
get {
if ( _currentName = = ENUM_DONE ) throw new InvalidOperationException ( Environment . GetResourceString ( ResId . InvalidOperation_EnumEnded ) ) ;
if ( ! _currentIsValid ) throw new InvalidOperationException ( Environment . GetResourceString ( ResId . InvalidOperation_EnumNotStarted ) ) ;
if ( _reader . _resCache = = null ) throw new InvalidOperationException ( Environment . GetResourceString ( "ResourceReaderIsClosed" ) ) ;
// Consider using _resCache here, eventually, if
// this proves to be an interesting perf scenario.
// But mixing lookups and enumerators shouldn't be
// particularly compelling.
return _reader . GetValueForNameIndex ( _currentName ) ;
}
}
public void Reset ( )
{
if ( _reader . _resCache = = null ) throw new InvalidOperationException ( Environment . GetResourceString ( "ResourceReaderIsClosed" ) ) ;
_currentIsValid = false ;
_currentName = ENUM_NOT_STARTED ;
}
}
}
}