|
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
/*============================================================
**
** Class: ResourceReader
**
** <OWNER>Microsoft</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
s = new String(charPtr, 0, byteLen/2);
#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 Microsoft 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 {
// The hexadecimal E translates to binary 1110
// So, with this & condition we are checking that none of the highest 3 bits are
// set before multiplying, as that would cause an overflow.
if ((_numResources & 0xE0000000) != 0) {
throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
}
int seekPos = unchecked(4 * _numResources);
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 {
// The hexadecimal E translates to binary 1110
// So, with this & condition we are checking that none of the highest 3 bits are
// set before multiplying, as that would cause an overflow.
if ((_numResources & 0xE0000000) != 0) {
throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
}
int seekPos = unchecked(4 * _numResources);
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 Microsoft 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;
}
}
// Microsoft 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 Microsoft
// 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) { // locks should be taken in the same order as in RuntimeResourceSet.GetObject to avoid deadlock
lock (_reader._resCache) {
key = _reader.AllocateStringForNameIndex(_currentName, out _dataPosition); // AllocateStringForNameIndex could lock on _reader
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 Microsoft 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;
}
}
}
}
|