Updates referencesource to .NET 4.7
[mono-project.git] / mcs / class / referencesource / mscorlib / system / resources / resourcereader.cs
blob03bebb8e07e613dc027ea8ae3e4da684492707c9
1 // ==++==
2 //
3 // Copyright (c) Microsoft Corporation. All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Class: ResourceReader
9 **
10 ** <OWNER>Microsoft</OWNER>
13 ** Purpose: Default way to read streams of resources on
14 ** demand.
16 ** Version 2 support on October 6, 2003
17 **
18 ===========================================================*/
19 namespace System.Resources {
20 using System;
21 using System.IO;
22 using System.Text;
23 using System.Collections;
24 using System.Collections.Generic;
25 #if FEATURE_SERIALIZATION
26 using System.Runtime.Serialization;
27 using System.Runtime.Serialization.Formatters;
28 using System.Runtime.Serialization.Formatters.Binary;
29 #endif // FEATURE_SERIALIZATION
30 using System.Reflection;
31 using System.Security.Permissions;
32 using System.Security;
33 using System.Globalization;
34 using System.Configuration.Assemblies;
35 using System.Runtime.Versioning;
36 using System.Diagnostics.Contracts;
38 // Provides the default implementation of IResourceReader, reading
39 // .resources file from the system default binary format. This class
40 // can be treated as an enumerator once.
41 //
42 // See the RuntimeResourceSet overview for details on the system
43 // default file format.
44 //
46 internal struct ResourceLocator
48 internal Object _value; // Can be null. Consider WeakReference instead?
49 internal int _dataPos;
51 internal ResourceLocator(int dataPos, Object value)
53 _dataPos = dataPos;
54 _value = value;
57 internal int DataPosition {
58 get { return _dataPos; }
59 //set { _dataPos = value; }
62 // Allows adding in profiling data in a future version, or a special
63 // resource profiling build. We could also use WeakReference.
64 internal Object Value {
65 get { return _value; }
66 set { _value = value; }
69 internal static bool CanCache(ResourceTypeCode value)
71 Contract.Assert(value >= 0, "negative ResourceTypeCode. What?");
72 return value <= ResourceTypeCode.LastPrimitive;
77 [System.Runtime.InteropServices.ComVisible(true)]
78 public sealed class ResourceReader : IResourceReader
80 // A reasonable default buffer size for reading from files, especially
81 // when we will likely be seeking frequently. Could be smaller, but does
82 // it make sense to use anything less than one page?
83 private const int DefaultFileStreamBufferSize = 4096;
85 private BinaryReader _store; // backing store we're reading from.
86 // Used by RuntimeResourceSet and this class's enumerator. Maps
87 // resource name to a value, a ResourceLocator, or a
88 // LooselyLinkedManifestResource.
89 internal Dictionary<String, ResourceLocator> _resCache;
90 private long _nameSectionOffset; // Offset to name section of file.
91 private long _dataSectionOffset; // Offset to Data section of file.
93 // Note this class is tightly coupled with UnmanagedMemoryStream.
94 // At runtime when getting an embedded resource from an assembly,
95 // we're given an UnmanagedMemoryStream referring to the mmap'ed portion
96 // of the assembly. The pointers here are pointers into that block of
97 // memory controlled by the OS's loader.
98 private int[] _nameHashes; // hash values for all names.
99 [SecurityCritical]
100 private unsafe int* _nameHashesPtr; // In case we're using UnmanagedMemoryStream
101 private int[] _namePositions; // relative locations of names
102 [SecurityCritical]
103 private unsafe int* _namePositionsPtr; // If we're using UnmanagedMemoryStream
104 private RuntimeType[] _typeTable; // Lazy array of Types for resource values.
105 private int[] _typeNamePositions; // To delay initialize type table
106 #if FEATURE_SERIALIZATION
107 private BinaryFormatter _objFormatter; // Deserialization stuff.
108 #endif // FEATURE_SERIALIZATION
109 private int _numResources; // Num of resources files, in case arrays aren't allocated.
111 // We'll include a separate code path that uses UnmanagedMemoryStream to
112 // avoid allocating String objects and the like.
113 private UnmanagedMemoryStream _ums;
115 // Version number of .resources file, for compatibility
116 private int _version;
118 #if RESOURCE_FILE_FORMAT_DEBUG
119 private bool _debug; // Whether this file has debugging stuff in it.
120 #endif
122 #if !FEATURE_PAL && FEATURE_SERIALIZATION
123 private bool[] _safeToDeserialize; // Whether to assert serialization permission
124 private TypeLimitingDeserializationBinder _typeLimitingBinder;
126 // One of our goals is to make sure localizable Windows Forms apps
127 // work in semi-trusted scenarios (ie, without serialization permission).
128 // Unfortunate we're serializing out some complex types that currently
129 // require a security check on deserialization. We may fix this
130 // in a next version, but for now just hard-code a list.
131 // Hard-code in the assembly name (minus version) so people can't spoof us.
132 private static readonly String[] TypesSafeForDeserialization = {
133 "System.String[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
134 "System.DateTime[], mscorlib, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
135 "System.Drawing.Bitmap, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
136 "System.Drawing.Imaging.Metafile, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
137 "System.Drawing.Point, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
138 "System.Drawing.PointF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
139 "System.Drawing.Size, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
140 "System.Drawing.SizeF, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
141 "System.Drawing.Font, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
142 "System.Drawing.Icon, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
143 "System.Drawing.Color, System.Drawing, Culture=neutral, PublicKeyToken=" + AssemblyRef.MicrosoftPublicKeyToken,
144 "System.Windows.Forms.Cursor, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
145 "System.Windows.Forms.Padding, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
146 "System.Windows.Forms.LinkArea, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
147 "System.Windows.Forms.ImageListStreamer, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
148 "System.Windows.Forms.ListViewGroup, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
149 "System.Windows.Forms.ListViewItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
150 "System.Windows.Forms.ListViewItem+ListViewSubItem, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
151 "System.Windows.Forms.ListViewItem+ListViewSubItem+SubItemStyle, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
152 "System.Windows.Forms.OwnerDrawPropertyBag, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken,
153 "System.Windows.Forms.TreeNode, System.Windows.Forms, Culture=neutral, PublicKeyToken=" + AssemblyRef.EcmaPublicKeyToken
155 #endif // !FEATURE_PAL && FEATURE_SERIALIZATION
157 #if FEATURE_CORECLR
158 [System.Security.SecurityCritical] // auto-generated
159 #else
160 [System.Security.SecuritySafeCritical]
161 #endif
162 [ResourceExposure(ResourceScope.Machine)]
163 [ResourceConsumption(ResourceScope.Machine)]
164 public ResourceReader(String fileName)
166 _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
167 _store = new BinaryReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, DefaultFileStreamBufferSize, FileOptions.RandomAccess, Path.GetFileName(fileName), false), Encoding.UTF8);
168 BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(String). UnmanagedMemoryStream: "+(_ums!=null));
170 try {
171 ReadResources();
173 catch {
174 _store.Close(); // If we threw an exception, close the file.
175 throw;
179 [System.Security.SecurityCritical] // auto-generated_required
180 public ResourceReader(Stream stream)
182 if (stream==null)
183 throw new ArgumentNullException("stream");
184 if (!stream.CanRead)
185 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotReadable"));
186 Contract.EndContractBlock();
188 _resCache = new Dictionary<String, ResourceLocator>(FastResourceComparer.Default);
189 _store = new BinaryReader(stream, Encoding.UTF8);
190 // We have a faster code path for reading resource files from an assembly.
191 _ums = stream as UnmanagedMemoryStream;
193 BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream). UnmanagedMemoryStream: "+(_ums!=null));
194 ReadResources();
197 // This is the constructor the RuntimeResourceSet calls,
198 // passing in the stream to read from and the RuntimeResourceSet's
199 // internal hash table (hash table of names with file offsets
200 // and values, coupled to this ResourceReader).
201 [System.Security.SecurityCritical] // auto-generated
202 internal ResourceReader(Stream stream, Dictionary<String, ResourceLocator> resCache)
204 Contract.Requires(stream != null, "Need a stream!");
205 Contract.Requires(stream.CanRead, "Stream should be readable!");
206 Contract.Requires(resCache != null, "Need a Dictionary!");
208 _resCache = resCache;
209 _store = new BinaryReader(stream, Encoding.UTF8);
211 _ums = stream as UnmanagedMemoryStream;
213 BCLDebug.Log("RESMGRFILEFORMAT", "ResourceReader .ctor(Stream, Hashtable). UnmanagedMemoryStream: "+(_ums!=null));
214 ReadResources();
218 public void Close()
220 Dispose(true);
223 public void Dispose()
225 Close();
228 [System.Security.SecuritySafeCritical] // auto-generated
229 private unsafe void Dispose(bool disposing)
231 if (_store != null) {
232 _resCache = null;
233 if (disposing) {
234 // Close the stream in a thread-safe way. This fix means
235 // that we may call Close n times, but that's safe.
236 BinaryReader copyOfStore = _store;
237 _store = null;
238 if (copyOfStore != null)
239 copyOfStore.Close();
241 _store = null;
242 _namePositions = null;
243 _nameHashes = null;
244 _ums = null;
245 _namePositionsPtr = null;
246 _nameHashesPtr = null;
250 [System.Security.SecurityCritical] // auto-generated
251 internal static unsafe int ReadUnalignedI4(int* p)
253 byte* buffer = (byte*)p;
254 // Unaligned, little endian format
255 return buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24);
258 private void SkipInt32() {
259 _store.BaseStream.Seek(4, SeekOrigin.Current);
263 private void SkipString() {
264 int stringLength = _store.Read7BitEncodedInt();
265 if (stringLength < 0) {
266 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
268 _store.BaseStream.Seek(stringLength, SeekOrigin.Current);
271 [System.Security.SecuritySafeCritical] // auto-generated
272 private unsafe int GetNameHash(int index)
274 Contract.Assert(index >=0 && index < _numResources, "Bad index into hash array. index: "+index);
275 Contract.Assert((_ums == null && _nameHashes != null && _nameHashesPtr == null) ||
276 (_ums != null && _nameHashes == null && _nameHashesPtr != null), "Internal state mangled.");
277 if (_ums == null)
278 return _nameHashes[index];
279 else
280 return ReadUnalignedI4(&_nameHashesPtr[index]);
283 [System.Security.SecuritySafeCritical] // auto-generated
284 private unsafe int GetNamePosition(int index)
286 Contract.Assert(index >=0 && index < _numResources, "Bad index into name position array. index: "+index);
287 Contract.Assert((_ums == null && _namePositions != null && _namePositionsPtr == null) ||
288 (_ums != null && _namePositions == null && _namePositionsPtr != null), "Internal state mangled.");
289 int r;
290 if (_ums == null)
291 r = _namePositions[index];
292 else
293 r = ReadUnalignedI4(&_namePositionsPtr[index]);
294 if (r < 0 || r > _dataSectionOffset - _nameSectionOffset) {
295 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameInvalidOffset", r));
297 return r;
300 IEnumerator IEnumerable.GetEnumerator()
302 return GetEnumerator();
305 public IDictionaryEnumerator GetEnumerator()
307 if (_resCache == null)
308 throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
309 return new ResourceEnumerator(this);
312 internal ResourceEnumerator GetEnumeratorInternal()
314 return new ResourceEnumerator(this);
317 // From a name, finds the associated virtual offset for the data.
318 // To read the data, seek to _dataSectionOffset + dataPos, then
319 // read the resource type & data.
320 // This does a binary search through the names.
321 internal int FindPosForResource(String name)
323 Contract.Assert(_store != null, "ResourceReader is closed!");
324 int hash = FastResourceComparer.HashFunction(name);
325 BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+" hash: "+hash.ToString("x", CultureInfo.InvariantCulture));
326 // Binary search over the hashes. Use the _namePositions array to
327 // determine where they exist in the underlying stream.
328 int lo = 0;
329 int hi = _numResources - 1;
330 int index = -1;
331 bool success = false;
332 while (lo <= hi) {
333 index = (lo + hi) >> 1;
334 // Do NOT use subtraction here, since it will wrap for large
335 // negative numbers.
336 int currentHash = GetNameHash(index);
337 int c;
338 if (currentHash == hash)
339 c = 0;
340 else if (currentHash < hash)
341 c = -1;
342 else
343 c = 1;
344 //BCLDebug.Log("RESMGRFILEFORMAT", " Probing index "+index+" lo: "+lo+" hi: "+hi+" c: "+c);
345 if (c == 0) {
346 success = true;
347 break;
349 if (c < 0)
350 lo = index + 1;
351 else
352 hi = index - 1;
354 if (!success) {
355 #if RESOURCE_FILE_FORMAT_DEBUG
356 String lastReadString;
357 lock(this) {
358 _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(index), SeekOrigin.Begin);
359 lastReadString = _store.ReadString();
361 BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "FindPosForResource for ", name, " failed. i: ", index, " lo: ", lo, " hi: ", hi, " last read string: \"", lastReadString, '\'');
362 #endif
363 return -1;
366 // index is the location in our hash array that corresponds with a
367 // value in the namePositions array.
368 // There could be collisions in our hash function. Check on both sides
369 // of index to find the range of hash values that are equal to the
370 // target hash value.
371 if (lo != index) {
372 lo = index;
373 while (lo > 0 && GetNameHash(lo - 1) == hash)
374 lo--;
376 if (hi != index) {
377 hi = index;
378 while (hi < _numResources - 1 && GetNameHash(hi + 1) == hash)
379 hi++;
382 lock(this) {
383 for(int i = lo; i<=hi; i++) {
384 _store.BaseStream.Seek(_nameSectionOffset + GetNamePosition(i), SeekOrigin.Begin);
385 if (CompareStringEqualsName(name)) {
386 int dataPos = _store.ReadInt32();
387 if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset) {
388 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataPos));
390 return dataPos;
394 BCLDebug.Log("RESMGRFILEFORMAT", "FindPosForResource for "+name+": Found a hash collision, HOWEVER, neither of these collided values equaled the given string.");
395 return -1;
398 // This compares the String in the .resources file at the current position
399 // with the string you pass in.
400 // Whoever calls this method should make sure that they take a lock
401 // so no one else can cause us to seek in the stream.
402 [System.Security.SecuritySafeCritical] // auto-generated
403 private unsafe bool CompareStringEqualsName(String name)
405 Contract.Assert(_store != null, "ResourceReader is closed!");
406 int byteLen = _store.Read7BitEncodedInt();
407 if (byteLen < 0) {
408 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
410 if (_ums != null) {
411 byte* bytes = _ums.PositionPointer;
412 // Skip over the data in the Stream, positioning ourselves right after it.
413 _ums.Seek(byteLen, SeekOrigin.Current);
414 if (_ums.Position > _ums.Length) {
415 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameTooLong"));
418 // On 64-bit machines, these char*'s may be misaligned. Use a
419 // byte-by-byte comparison instead.
420 //return FastResourceComparer.CompareOrdinal((char*)bytes, byteLen/2, name) == 0;
421 return FastResourceComparer.CompareOrdinal(bytes, byteLen, name) == 0;
423 else {
424 // This code needs to be fast
425 byte[] bytes = new byte[byteLen];
426 int numBytesToRead = byteLen;
427 while(numBytesToRead > 0) {
428 int n = _store.Read(bytes, byteLen - numBytesToRead, numBytesToRead);
429 if (n == 0)
430 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
431 numBytesToRead -= n;
433 return FastResourceComparer.CompareOrdinal(bytes, byteLen/2, name) == 0;
437 // This is used in the enumerator. The enumerator iterates from 0 to n
438 // of our resources and this returns the resource name for a particular
439 // index. The parameter is NOT a virtual offset.
440 [System.Security.SecurityCritical] // auto-generated
441 private unsafe String AllocateStringForNameIndex(int index, out int dataOffset)
443 Contract.Assert(_store != null, "ResourceReader is closed!");
444 byte[] bytes;
445 int byteLen;
446 long nameVA = GetNamePosition(index);
447 lock (this) {
448 _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
449 // Can't use _store.ReadString, since it's using UTF-8!
450 byteLen = _store.Read7BitEncodedInt();
451 if (byteLen < 0) {
452 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_NegativeStringLength"));
455 if (_ums != null) {
456 if (_ums.Position > _ums.Length - byteLen)
457 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesIndexTooLong", index));
459 String s = null;
460 char* charPtr = (char*)_ums.PositionPointer;
461 #if IA64
462 if (((int)charPtr & 1) != 0) {
463 char[] destArray = new char[byteLen/2];
464 fixed(char* pDest = destArray) {
465 Buffer.Memcpy((byte*)pDest, (byte*)charPtr, byteLen);
467 s = new String(destArray);
469 else {
470 #endif //IA64
471 if (!BitConverter.IsLittleEndian) {
472 byte* bytePtr = (byte*) charPtr;
473 var dest = new byte[byteLen];
474 for (int i = 0; i < byteLen; i += 2) {
475 dest[i] = *(bytePtr+i+1);
476 dest[i+1] = *(bytePtr+i);
479 fixed(byte *pDest = dest) {
480 s = new String((char *)pDest, 0, byteLen/2);
482 } else
483 s = new String(charPtr, 0, byteLen/2);
484 #if IA64
486 #endif //IA64
487 _ums.Position += byteLen;
488 dataOffset = _store.ReadInt32();
489 if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset) {
490 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataOffset));
492 return s;
495 bytes = new byte[byteLen];
496 // We must read byteLen bytes, or we have a corrupted file.
497 // Use a blocking read in case the stream doesn't give us back
498 // everything immediately.
499 int count = byteLen;
500 while(count > 0) {
501 int n = _store.Read(bytes, byteLen - count, count);
502 if (n == 0)
503 throw new EndOfStreamException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted_NameIndex", index));
504 count -= n;
506 dataOffset = _store.ReadInt32();
507 if (dataOffset < 0 || dataOffset >= _store.BaseStream.Length - _dataSectionOffset) {
508 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataOffset));
511 return Encoding.Unicode.GetString(bytes, 0, byteLen);
514 // This is used in the enumerator. The enumerator iterates from 0 to n
515 // of our resources and this returns the resource value for a particular
516 // index. The parameter is NOT a virtual offset.
517 private Object GetValueForNameIndex(int index)
519 Contract.Assert(_store != null, "ResourceReader is closed!");
520 long nameVA = GetNamePosition(index);
521 lock(this) {
522 _store.BaseStream.Seek(nameVA + _nameSectionOffset, SeekOrigin.Begin);
523 SkipString();
524 //BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex for index: "+index+" skip (name length): "+skip);
525 int dataPos = _store.ReadInt32();
526 if (dataPos < 0 || dataPos >= _store.BaseStream.Length - _dataSectionOffset) {
527 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dataPos));
529 BCLDebug.Log("RESMGRFILEFORMAT", "GetValueForNameIndex: dataPos: "+dataPos);
530 ResourceTypeCode junk;
531 if (_version == 1)
532 return LoadObjectV1(dataPos);
533 else
534 return LoadObjectV2(dataPos, out junk);
538 // This takes a virtual offset into the data section and reads a String
539 // from that location.
540 // Anyone who calls LoadObject should make sure they take a lock so
541 // no one can cause us to do a seek in here.
542 internal String LoadString(int pos)
544 Contract.Assert(_store != null, "ResourceReader is closed!");
545 _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
546 String s = null;
547 int typeIndex = _store.Read7BitEncodedInt();
548 if (_version == 1) {
549 if (typeIndex == -1)
550 return null;
551 if (FindType(typeIndex) != typeof(String))
552 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", FindType(typeIndex).FullName));
553 s = _store.ReadString();
555 else {
556 ResourceTypeCode typeCode = (ResourceTypeCode) typeIndex;
557 if (typeCode != ResourceTypeCode.String && typeCode != ResourceTypeCode.Null) {
558 String typeString;
559 if (typeCode < ResourceTypeCode.StartOfUserTypes)
560 typeString = typeCode.ToString();
561 else
562 typeString = FindType(typeCode - ResourceTypeCode.StartOfUserTypes).FullName;
563 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceNotString_Type", typeString));
565 if (typeCode == ResourceTypeCode.String) // ignore Null
566 s = _store.ReadString();
568 BCLDebug.Log("RESMGRFILEFORMAT", "LoadString("+pos.ToString("x", CultureInfo.InvariantCulture)+" returned "+(s==null ? "[a null string]" : s));
569 return s;
572 // Called from RuntimeResourceSet
573 internal Object LoadObject(int pos)
575 if (_version == 1)
576 return LoadObjectV1(pos);
577 ResourceTypeCode typeCode;
578 return LoadObjectV2(pos, out typeCode);
581 internal Object LoadObject(int pos, out ResourceTypeCode typeCode)
583 if (_version == 1) {
584 Object o = LoadObjectV1(pos);
585 typeCode = (o is String) ? ResourceTypeCode.String : ResourceTypeCode.StartOfUserTypes;
586 return o;
588 return LoadObjectV2(pos, out typeCode);
591 // This takes a virtual offset into the data section and reads an Object
592 // from that location.
593 // Anyone who calls LoadObject should make sure they take a lock so
594 // no one can cause us to do a seek in here.
595 internal Object LoadObjectV1(int pos)
597 Contract.Assert(_store != null, "ResourceReader is closed!");
598 Contract.Assert(_version == 1, ".resources file was not a V1 .resources file!");
600 try {
601 // mega try-catch performs exceptionally bad on x64; factored out body into
602 // _LoadObjectV1 and wrap here.
603 return _LoadObjectV1(pos);
605 catch (EndOfStreamException eof) {
606 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), eof);
608 catch (ArgumentOutOfRangeException e) {
609 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), e);
613 #if FEATURE_SERIALIZATION
614 [SecuritySafeCritical]
615 #endif
616 private Object _LoadObjectV1(int pos) {
617 _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
618 int typeIndex = _store.Read7BitEncodedInt();
619 if (typeIndex == -1)
620 return null;
621 RuntimeType type = FindType(typeIndex);
622 BCLDebug.Log("RESMGRFILEFORMAT", "LoadObject type: "+type.Name+" pos: 0x"+_store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
623 // Consider putting in logic to see if this type is a
624 // primitive or a value type first, so we can reach the
625 // deserialization code faster for arbitrary objects.
627 if (type == typeof(String))
628 return _store.ReadString();
629 else if (type == typeof(Int32))
630 return _store.ReadInt32();
631 else if (type == typeof(Byte))
632 return _store.ReadByte();
633 else if (type == typeof(SByte))
634 return _store.ReadSByte();
635 else if (type == typeof(Int16))
636 return _store.ReadInt16();
637 else if (type == typeof(Int64))
638 return _store.ReadInt64();
639 else if (type == typeof(UInt16))
640 return _store.ReadUInt16();
641 else if (type == typeof(UInt32))
642 return _store.ReadUInt32();
643 else if (type == typeof(UInt64))
644 return _store.ReadUInt64();
645 else if (type == typeof(Single))
646 return _store.ReadSingle();
647 else if (type == typeof(Double))
648 return _store.ReadDouble();
649 else if (type == typeof(DateTime)) {
650 // Ideally we should use DateTime's ToBinary & FromBinary,
651 // but we can't for compatibility reasons.
652 return new DateTime(_store.ReadInt64());
654 else if (type == typeof(TimeSpan))
655 return new TimeSpan(_store.ReadInt64());
656 else if (type == typeof(Decimal)) {
657 int[] bits = new int[4];
658 for(int i=0; i<bits.Length; i++)
659 bits[i] = _store.ReadInt32();
660 return new Decimal(bits);
662 else {
663 #if FEATURE_SERIALIZATION
664 return DeserializeObject(typeIndex);
665 #else
666 throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
667 #endif // FEATURE_SERIALIZATION
671 internal Object LoadObjectV2(int pos, out ResourceTypeCode typeCode)
673 Contract.Assert(_store != null, "ResourceReader is closed!");
674 Contract.Assert(_version >= 2, ".resources file was not a V2 (or higher) .resources file!");
676 try {
677 // mega try-catch performs exceptionally bad on x64; factored out body into
678 // _LoadObjectV2 and wrap here.
679 return _LoadObjectV2(pos, out typeCode);
681 catch (EndOfStreamException eof) {
682 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), eof);
684 catch (ArgumentOutOfRangeException e) {
685 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"), e);
689 [System.Security.SecuritySafeCritical] // auto-generated
690 private Object _LoadObjectV2(int pos, out ResourceTypeCode typeCode) {
691 _store.BaseStream.Seek(_dataSectionOffset+pos, SeekOrigin.Begin);
692 typeCode = (ResourceTypeCode) _store.Read7BitEncodedInt();
694 BCLDebug.Log("RESMGRFILEFORMAT", "LoadObjectV2 type: "+typeCode+" pos: 0x"+_store.BaseStream.Position.ToString("x", CultureInfo.InvariantCulture));
696 switch(typeCode) {
697 case ResourceTypeCode.Null:
698 return null;
700 case ResourceTypeCode.String:
701 return _store.ReadString();
703 case ResourceTypeCode.Boolean:
704 return _store.ReadBoolean();
706 case ResourceTypeCode.Char:
707 return (char) _store.ReadUInt16();
709 case ResourceTypeCode.Byte:
710 return _store.ReadByte();
712 case ResourceTypeCode.SByte:
713 return _store.ReadSByte();
715 case ResourceTypeCode.Int16:
716 return _store.ReadInt16();
718 case ResourceTypeCode.UInt16:
719 return _store.ReadUInt16();
721 case ResourceTypeCode.Int32:
722 return _store.ReadInt32();
724 case ResourceTypeCode.UInt32:
725 return _store.ReadUInt32();
727 case ResourceTypeCode.Int64:
728 return _store.ReadInt64();
730 case ResourceTypeCode.UInt64:
731 return _store.ReadUInt64();
733 case ResourceTypeCode.Single:
734 return _store.ReadSingle();
736 case ResourceTypeCode.Double:
737 return _store.ReadDouble();
739 case ResourceTypeCode.Decimal:
740 return _store.ReadDecimal();
742 case ResourceTypeCode.DateTime:
743 // Use DateTime's ToBinary & FromBinary.
744 Int64 data = _store.ReadInt64();
745 return DateTime.FromBinary(data);
747 case ResourceTypeCode.TimeSpan:
748 Int64 ticks = _store.ReadInt64();
749 return new TimeSpan(ticks);
751 // Special types
752 case ResourceTypeCode.ByteArray: {
753 int len = _store.ReadInt32();
754 if (len < 0) {
755 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
758 if (_ums == null) {
759 if (len > _store.BaseStream.Length) {
760 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
762 return _store.ReadBytes(len);
765 if (len > _ums.Length - _ums.Position) {
766 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
769 byte[] bytes = new byte[len];
770 int r = _ums.Read(bytes, 0, len);
771 Contract.Assert(r == len, "ResourceReader needs to use a blocking read here. (Call _store.ReadBytes(len)?)");
772 return bytes;
775 case ResourceTypeCode.Stream: {
776 int len = _store.ReadInt32();
777 if (len < 0) {
778 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
780 if (_ums == null) {
781 byte[] bytes = _store.ReadBytes(len);
782 // Lifetime of memory == lifetime of this stream.
783 return new PinnedBufferMemoryStream(bytes);
786 // make sure we don't create an UnmanagedMemoryStream that is longer than the resource stream.
787 if (len > _ums.Length - _ums.Position) {
788 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourceDataLengthInvalid", len));
791 // For the case that we've memory mapped in the .resources
792 // file, just return a Stream pointing to that block of memory.
793 unsafe {
794 return new UnmanagedMemoryStream(_ums.PositionPointer, len, len, FileAccess.Read, true);
798 default:
799 if (typeCode < ResourceTypeCode.StartOfUserTypes) {
800 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_TypeMismatch"));
802 break;
805 // Normal serialized objects
806 #if FEATURE_SERIALIZATION
807 int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
808 return DeserializeObject(typeIndex);
809 #else
810 throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
811 #endif // FEATURE_SERIALIZATION
815 #if FEATURE_SERIALIZATION
816 // Helper method to safely deserialize a type, using a type-limiting
817 // deserialization binder to simulate a type-limiting deserializer.
818 // This method handles types that are safe to deserialize, as well as
819 // ensuring we only get back what we expect.
820 [System.Security.SecurityCritical] // auto-generated
821 private Object DeserializeObject(int typeIndex)
823 RuntimeType type = FindType(typeIndex);
824 #if !FEATURE_PAL
825 // Initialize deserialization permission array, if needed
826 if (_safeToDeserialize == null)
827 InitSafeToDeserializeArray();
828 #endif // !FEATURE_PAL
830 // Ensure that the object we deserialized is exactly the same
831 // type of object we thought we should be deserializing. This
832 // will help prevent hacked .resources files from using our
833 // serialization permission assert to deserialize anything
834 // via a hacked type ID.
836 Object graph;
837 #if FEATURE_PAL
838 graph = _objFormatter.Deserialize(_store.BaseStream);
839 #else
840 if (_safeToDeserialize[typeIndex]) {
841 // Don't assert serialization permission - just ask the binary
842 // formatter to avoid a permission check. This ensures that any
843 // types which do demand serialization permission in their
844 // deserialization .cctors will fail.
845 // Also, use a serialization binder to limit bind requests to
846 // our allowed list of Microsoft types.
847 _objFormatter.Binder = _typeLimitingBinder;
848 _typeLimitingBinder.ExpectingToDeserialize(type);
849 graph = _objFormatter.UnsafeDeserialize(_store.BaseStream, null);
851 else {
852 _objFormatter.Binder = null;
853 graph = _objFormatter.Deserialize(_store.BaseStream);
855 #endif // FEATURE_PAL
857 // This check is about correctness, not security at this point.
858 if (graph.GetType() != type)
859 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", type.FullName, graph.GetType().FullName));
861 return graph;
863 #endif // FEATURE_SERIALIZATION
865 // Reads in the header information for a .resources file. Verifies some
866 // of the assumptions about this resource set, and builds the class table
867 // for the default resource file format.
868 [System.Security.SecurityCritical] // auto-generated
869 private void ReadResources()
871 Contract.Assert(_store != null, "ResourceReader is closed!");
872 #if FEATURE_SERIALIZATION
873 BinaryFormatter bf = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence));
874 #if !FEATURE_PAL
875 _typeLimitingBinder = new TypeLimitingDeserializationBinder();
876 bf.Binder = _typeLimitingBinder;
877 #endif
878 _objFormatter = bf;
879 #endif // FEATURE_SERIALIZATION
881 try {
882 // mega try-catch performs exceptionally bad on x64; factored out body into
883 // _ReadResources and wrap here.
884 _ReadResources();
886 catch (EndOfStreamException eof) {
887 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"), eof);
889 catch (IndexOutOfRangeException e) {
890 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"), e);
894 [System.Security.SecurityCritical] // auto-generated
895 private void _ReadResources()
897 // Read ResourceManager header
898 // Check for magic number
899 int magicNum = _store.ReadInt32();
900 if (magicNum != ResourceManager.MagicNumber)
901 throw new ArgumentException(Environment.GetResourceString("Resources_StreamNotValid"));
902 // Assuming this is ResourceManager header V1 or greater, hopefully
903 // after the version number there is a number of bytes to skip
904 // to bypass the rest of the ResMgr header. For V2 or greater, we
905 // use this to skip to the end of the header
906 int resMgrHeaderVersion = _store.ReadInt32();
907 int numBytesToSkip = _store.ReadInt32();
908 if (numBytesToSkip < 0 || resMgrHeaderVersion < 0) {
909 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
911 if (resMgrHeaderVersion > 1) {
912 BCLDebug.Log("RESMGRFILEFORMAT", LogLevel.Status, "ReadResources: Unexpected ResMgr header version: {0} Skipping ahead {1} bytes.", resMgrHeaderVersion, numBytesToSkip);
913 _store.BaseStream.Seek(numBytesToSkip, SeekOrigin.Current);
915 else {
916 BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Parsing ResMgr header v1.");
917 // We don't care about numBytesToSkip; read the rest of the header
919 // Read in type name for a suitable ResourceReader
920 // Note ResourceWriter & InternalResGen use different Strings.
921 String readerType = _store.ReadString();
922 AssemblyName mscorlib = new AssemblyName(ResourceManager.MscorlibName);
924 if (!ResourceManager.CompareNames(readerType, ResourceManager.ResReaderTypeName, mscorlib))
925 throw new NotSupportedException(Environment.GetResourceString("NotSupported_WrongResourceReader_Type", readerType));
927 // Skip over type name for a suitable ResourceSet
928 SkipString();
931 // Read RuntimeResourceSet header
932 // Do file version check
933 int version = _store.ReadInt32();
934 if (version != RuntimeResourceSet.Version && version != 1)
935 throw new ArgumentException(Environment.GetResourceString("Arg_ResourceFileUnsupportedVersion", RuntimeResourceSet.Version, version));
936 _version = version;
938 #if RESOURCE_FILE_FORMAT_DEBUG
939 // Look for ***DEBUG*** to see if this is a debuggable file.
940 long oldPos = _store.BaseStream.Position;
941 _debug = false;
942 try {
943 String debugString = _store.ReadString();
944 _debug = String.Equals("***DEBUG***", debugString);
946 catch(IOException) {
948 catch(OutOfMemoryException) {
950 if (_debug) {
951 Console.WriteLine("ResourceReader is looking at a debuggable .resources file, version {0}", _version);
953 else {
954 _store.BaseStream.Position = oldPos;
956 #endif
958 _numResources = _store.ReadInt32();
959 if (_numResources < 0) {
960 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
962 BCLDebug.Log("RESMGRFILEFORMAT", "ReadResources: Expecting " + _numResources + " resources.");
963 #if _DEBUG
964 if (ResourceManager.DEBUG >= 4)
965 Console.WriteLine("ResourceReader::ReadResources - Reading in "+_numResources+" resources");
966 #endif
968 // Read type positions into type positions array.
969 // But delay initialize the type table.
970 int numTypes = _store.ReadInt32();
971 if (numTypes < 0) {
972 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
974 _typeTable = new RuntimeType[numTypes];
975 _typeNamePositions = new int[numTypes];
976 for (int i=0; i<numTypes; i++) {
977 _typeNamePositions[i] = (int) _store.BaseStream.Position;
979 // Skip over the Strings in the file. Don't create types.
980 SkipString();
983 #if _DEBUG
984 if (ResourceManager.DEBUG >= 5)
985 Console.WriteLine("ResourceReader::ReadResources - Reading in "+numTypes+" type table entries");
986 #endif
988 // Prepare to read in the array of name hashes
989 // Note that the name hashes array is aligned to 8 bytes so
990 // we can use pointers into it on 64 bit machines. (4 bytes
991 // may be sufficient, but let's plan for the future)
992 // Skip over alignment stuff. All public .resources files
993 // should be aligned No need to verify the byte values.
994 long pos = _store.BaseStream.Position;
995 int alignBytes = ((int)pos) & 7;
996 if (alignBytes != 0) {
997 for (int i = 0; i < 8 - alignBytes; i++) {
998 _store.ReadByte();
1002 // Read in the array of name hashes
1003 #if RESOURCE_FILE_FORMAT_DEBUG
1004 // Skip over "HASHES->"
1005 if (_debug) {
1006 _store.BaseStream.Position += 8;
1008 #endif
1010 if (_ums == null) {
1011 _nameHashes = new int[_numResources];
1012 for (int i = 0; i < _numResources; i++) {
1013 _nameHashes[i] = _store.ReadInt32();
1016 else {
1017 // The hexadecimal E translates to binary 1110
1018 // So, with this & condition we are checking that none of the highest 3 bits are
1019 // set before multiplying, as that would cause an overflow.
1020 if ((_numResources & 0xE0000000) != 0) {
1021 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1024 int seekPos = unchecked(4 * _numResources);
1025 unsafe {
1026 _nameHashesPtr = (int*)_ums.PositionPointer;
1027 // Skip over the array of nameHashes.
1028 _ums.Seek(seekPos, SeekOrigin.Current);
1029 // get the position pointer once more to check that the whole table is within the stream
1030 byte* junk = _ums.PositionPointer;
1034 // Read in the array of relative positions for all the names.
1035 #if RESOURCE_FILE_FORMAT_DEBUG
1036 // Skip over "POS---->"
1037 if (_debug) {
1038 _store.BaseStream.Position += 8;
1040 #endif
1041 if (_ums == null) {
1042 _namePositions = new int[_numResources];
1043 for (int i = 0; i < _numResources; i++) {
1044 int namePosition = _store.ReadInt32();
1045 if (namePosition < 0) {
1046 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1049 _namePositions[i] = namePosition;
1052 else {
1053 // The hexadecimal E translates to binary 1110
1054 // So, with this & condition we are checking that none of the highest 3 bits are
1055 // set before multiplying, as that would cause an overflow.
1056 if ((_numResources & 0xE0000000) != 0) {
1057 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1060 int seekPos = unchecked(4 * _numResources);
1061 unsafe {
1062 _namePositionsPtr = (int*)_ums.PositionPointer;
1063 // Skip over the array of namePositions.
1064 _ums.Seek(seekPos, SeekOrigin.Current);
1065 // get the position pointer once more to check that the whole table is within the stream
1066 byte* junk = _ums.PositionPointer;
1070 // Read location of data section.
1071 _dataSectionOffset = _store.ReadInt32();
1072 if (_dataSectionOffset < 0) {
1073 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1076 // Store current location as start of name section
1077 _nameSectionOffset = _store.BaseStream.Position;
1079 // _nameSectionOffset should be <= _dataSectionOffset; if not, it's corrupt
1080 if (_dataSectionOffset < _nameSectionOffset) {
1081 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResourcesHeaderCorrupted"));
1084 BCLDebug.Log("RESMGRFILEFORMAT", String.Format(CultureInfo.InvariantCulture, "ReadResources: _nameOffset = 0x{0:x} _dataOffset = 0x{1:x}", _nameSectionOffset, _dataSectionOffset));
1087 // This allows us to delay-initialize the Type[]. This might be a
1088 // good startup time savings, since we might have to load assemblies
1089 // and initialize Reflection.
1090 private RuntimeType FindType(int typeIndex)
1092 if (typeIndex < 0 || typeIndex >= _typeTable.Length) {
1093 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_InvalidType"));
1095 if (_typeTable[typeIndex] == null) {
1096 long oldPos = _store.BaseStream.Position;
1097 try {
1098 _store.BaseStream.Position = _typeNamePositions[typeIndex];
1099 String typeName = _store.ReadString();
1100 _typeTable[typeIndex] = (RuntimeType)Type.GetType(typeName, true);
1102 #if !FEATURE_SERIALIZATION
1103 // If serialization isn't supported, we convert FileNotFoundException to
1104 // NotSupportedException for consistency with v2. This is a corner-case, but the
1105 // idea is that we want to give the user a more accurate error message. Even if
1106 // the dependency were found, we know it will require serialization since it
1107 // can't be one of the types we special case. So if the dependency were found,
1108 // it would go down the serialization code path, resulting in NotSupported for
1109 // SKUs without serialization.
1111 // We don't want to regress the expected case by checking the type info before
1112 // getting to Type.GetType -- this is costly with v1 resource formats.
1113 catch (FileNotFoundException)
1115 throw new NotSupportedException(Environment.GetResourceString("NotSupported_ResourceObjectSerialization"));
1117 #endif // FEATURE_SERIALIZATION
1118 finally {
1119 _store.BaseStream.Position = oldPos;
1122 Contract.Assert(_typeTable[typeIndex] != null, "Should have found a type!");
1123 return _typeTable[typeIndex];
1126 #if !FEATURE_PAL && FEATURE_SERIALIZATION
1127 [System.Security.SecurityCritical] // auto-generated
1128 private void InitSafeToDeserializeArray()
1130 _safeToDeserialize = new bool[_typeTable.Length];
1131 for(int i=0; i<_typeTable.Length; i++) {
1132 long oldPos = _store.BaseStream.Position;
1133 String typeName;
1134 try {
1135 _store.BaseStream.Position = _typeNamePositions[i];
1136 typeName = _store.ReadString();
1138 finally {
1139 _store.BaseStream.Position = oldPos;
1142 AssemblyName an;
1143 String typePart;
1144 RuntimeType resourceType = (RuntimeType)Type.GetType(typeName, false);
1145 if (resourceType == null) {
1146 an = null;
1147 typePart = typeName;
1149 else {
1150 // Enums should be safe to deserialize, and this helps out
1151 // partially trusted, localized Microsoft apps.
1152 if (resourceType.BaseType == typeof(Enum)) {
1153 _safeToDeserialize[i] = true;
1154 continue;
1157 // For most types, check our TypesSafeForDeserialization.
1158 typePart = resourceType.FullName;
1160 an = new AssemblyName();
1162 // resourceType is retrieved via Type.GetType and must be a RuntimeType
1163 RuntimeAssembly a = (RuntimeAssembly)resourceType.Assembly;
1164 an.Init(a.GetSimpleName(),
1165 a.GetPublicKey(),
1166 null, // public key token
1167 null, // version
1168 a.GetLocale(),
1169 AssemblyHashAlgorithm.None,
1170 AssemblyVersionCompatibility.SameMachine,
1171 null, // codebase
1172 AssemblyNameFlags.PublicKey,
1173 null); // strong name key pair
1176 foreach(String safeType in TypesSafeForDeserialization) {
1177 if (ResourceManager.CompareNames(safeType, typePart, an)) {
1178 #if _DEBUG
1179 if (ResourceManager.DEBUG >= 7)
1180 Console.WriteLine("ResReader: Found a type-safe type to deserialize! Type name: {0}", typeName);
1181 #endif
1182 _safeToDeserialize[i] = true;
1183 continue;
1187 #if _DEBUG
1188 if (ResourceManager.DEBUG >= 7)
1189 if (!_safeToDeserialize[i])
1190 Console.WriteLine("ResReader: Found a type that wasn't safe to deserialize: {0}", typeName);
1191 #endif
1194 #endif // !FEATURE_PAL && FEATURE_SERIALIZATION
1196 public void GetResourceData(String resourceName, out String resourceType, out byte[] resourceData)
1198 if (resourceName == null)
1199 throw new ArgumentNullException("resourceName");
1200 Contract.EndContractBlock();
1201 if (_resCache == null)
1202 throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1204 // Get the type information from the data section. Also,
1205 // sort all of the data section's indexes to compute length of
1206 // the serialized data for this type (making sure to subtract
1207 // off the length of the type code).
1208 int[] sortedDataPositions = new int[_numResources];
1209 int dataPos = FindPosForResource(resourceName);
1210 if( dataPos == -1) {
1211 throw new ArgumentException(Environment.GetResourceString("Arg_ResourceNameNotExist", resourceName));
1214 lock(this) {
1215 // Read all the positions of data within the data section.
1216 for(int i=0; i<_numResources; i++) {
1217 _store.BaseStream.Position = _nameSectionOffset + GetNamePosition(i);
1218 // Skip over name of resource
1219 int numBytesToSkip = _store.Read7BitEncodedInt();
1220 if (numBytesToSkip < 0) {
1221 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesNameInvalidOffset", numBytesToSkip));
1223 _store.BaseStream.Position += numBytesToSkip;
1225 int dPos = _store.ReadInt32();
1226 if (dPos < 0 || dPos >= _store.BaseStream.Length - _dataSectionOffset) {
1227 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourcesDataInvalidOffset", dPos));
1229 sortedDataPositions[i] = dPos;
1231 Array.Sort(sortedDataPositions);
1233 int index = Array.BinarySearch(sortedDataPositions, dataPos);
1234 Contract.Assert(index >= 0 && index < _numResources, "Couldn't find data position within sorted data positions array!");
1235 long nextData = (index < _numResources - 1) ? sortedDataPositions[index + 1] + _dataSectionOffset : _store.BaseStream.Length;
1236 int len = (int) (nextData - (dataPos + _dataSectionOffset));
1237 Contract.Assert(len >= 0 && len <= (int) _store.BaseStream.Length - dataPos + _dataSectionOffset, "Length was negative or outside the bounds of the file!");
1239 // Read type code then byte[]
1240 _store.BaseStream.Position = _dataSectionOffset + dataPos;
1241 ResourceTypeCode typeCode = (ResourceTypeCode) _store.Read7BitEncodedInt();
1242 if (typeCode < 0 || typeCode >= ResourceTypeCode.StartOfUserTypes + _typeTable.Length) {
1243 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_InvalidType"));
1245 resourceType = TypeNameFromTypeCode(typeCode);
1247 // The length must be adjusted to subtract off the number
1248 // of bytes in the 7 bit encoded type code.
1249 len -= (int) (_store.BaseStream.Position - (_dataSectionOffset + dataPos));
1250 byte[] bytes = _store.ReadBytes(len);
1251 if (bytes.Length != len)
1252 throw new FormatException(Environment.GetResourceString("BadImageFormat_ResourceNameCorrupted"));
1253 resourceData = bytes;
1257 private String TypeNameFromTypeCode(ResourceTypeCode typeCode)
1259 Contract.Requires(typeCode >= 0, "can't be negative");
1260 if (typeCode < ResourceTypeCode.StartOfUserTypes) {
1261 Contract.Assert(!String.Equals(typeCode.ToString(), "LastPrimitive"), "Change ResourceTypeCode metadata order so LastPrimitive isn't what Enum.ToString prefers.");
1262 return "ResourceTypeCode." + typeCode.ToString();
1264 else {
1265 int typeIndex = typeCode - ResourceTypeCode.StartOfUserTypes;
1266 Contract.Assert(typeIndex >= 0 && typeIndex < _typeTable.Length, "TypeCode is broken or corrupted!");
1267 long oldPos = _store.BaseStream.Position;
1268 try {
1269 _store.BaseStream.Position = _typeNamePositions[typeIndex];
1270 return _store.ReadString();
1272 finally {
1273 _store.BaseStream.Position = oldPos;
1278 #if !FEATURE_PAL && FEATURE_SERIALIZATION
1279 // We need to build a type-limiting deserializer. We know exactly which
1280 // type we want to deserialize, and if someone tells us we have type X
1281 // (which might be safe to deserialize) and they give us a serialized
1282 // form of type Y, we don't want to run the deserialization constructor
1283 // because we've asserted serialization formatter permission. Instead,
1284 // limit the binary formatter's type binding to precisely the type we
1285 // expect. If they ever don't match, that's a corrupt .resources file.
1286 // We also must check the complete object graph to ensure all of the
1287 // graph contains safe objects.
1288 // Note this is tightly coupled to the BinaryFormatter, since we use
1289 // its internal ObjectReader::FastBindToType method, and we had to
1290 // change the ObjectReader to register itself with this type.
1291 internal sealed class TypeLimitingDeserializationBinder : SerializationBinder
1293 private RuntimeType _typeToDeserialize;
1294 // This is tightly coupled with the binary formatter, because we
1295 // want to use exactly the same code found in the ObjectReader
1296 // to do the lookup, then just give a thumbs up or down based on
1297 // a type equality comparison. In the future, we could consider
1298 // some better refactoring of this code.
1299 private ObjectReader _objectReader;
1301 internal ObjectReader ObjectReader {
1302 get { return _objectReader; }
1303 set { _objectReader = value; }
1306 internal void ExpectingToDeserialize(RuntimeType type)
1308 _typeToDeserialize = type;
1311 [System.Security.SecuritySafeCritical] // overrides transparent public member
1312 public override Type BindToType(string assemblyName, string typeName)
1314 // BinaryObjectReader::Bind tries us first, then its own code.
1315 // Returning null means let the default binding rules happen.
1316 AssemblyName an = new AssemblyName(assemblyName);
1318 bool safe = false;
1319 foreach(String safeType in TypesSafeForDeserialization) {
1320 if (ResourceManager.CompareNames(safeType, typeName, an)) {
1321 safe = true;
1322 break;
1326 // Microsoft types may internally use some enums that aren't
1327 // on our safe to deserialize list, like Font using FontStyle.
1328 Type t = ObjectReader.FastBindToType(assemblyName, typeName);
1329 if (t.IsEnum)
1330 safe = true;
1332 if (safe)
1333 return null;
1335 // Throw instead of returning null.
1336 // If you're looking at this in a debugger, you've either
1337 // got a hacked .resources file on your hands, or Microsoft
1338 // types have taken a new dependency on another type. Check
1339 // whether assemblyName & typeName refer to a trustworthy type,
1340 // & consider adding it to the TypesSafeToDeserialize list.
1341 throw new BadImageFormatException(Environment.GetResourceString("BadImageFormat_ResType&SerBlobMismatch", _typeToDeserialize.FullName, typeName));
1344 #endif // !FEATURE_PAL && FEATURE_SERIALIZATION
1347 internal sealed class ResourceEnumerator : IDictionaryEnumerator
1349 private const int ENUM_DONE = Int32.MinValue;
1350 private const int ENUM_NOT_STARTED = -1;
1352 private ResourceReader _reader;
1353 private bool _currentIsValid;
1354 private int _currentName;
1355 private int _dataPosition; // cached for case-insensitive table
1357 internal ResourceEnumerator(ResourceReader reader)
1359 _currentName = ENUM_NOT_STARTED;
1360 _reader = reader;
1361 _dataPosition = -2;
1364 public bool MoveNext()
1366 if (_currentName == _reader._numResources - 1 || _currentName == ENUM_DONE) {
1367 _currentIsValid = false;
1368 _currentName = ENUM_DONE;
1369 return false;
1371 _currentIsValid = true;
1372 _currentName++;
1373 return true;
1376 public Object Key {
1377 [System.Security.SecuritySafeCritical] // auto-generated
1378 get {
1379 if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
1380 if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
1381 if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1383 return _reader.AllocateStringForNameIndex(_currentName, out _dataPosition);
1387 public Object Current {
1388 get {
1389 return Entry;
1393 // Warning: This requires that you call the Key or Entry property FIRST before calling it!
1394 internal int DataPosition {
1395 get {
1396 return _dataPosition;
1400 public DictionaryEntry Entry {
1401 [System.Security.SecuritySafeCritical] // auto-generated
1402 get {
1403 if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
1404 if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
1405 if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1407 String key;
1408 Object value = null;
1409 lock (_reader) { // locks should be taken in the same order as in RuntimeResourceSet.GetObject to avoid deadlock
1410 lock (_reader._resCache) {
1411 key = _reader.AllocateStringForNameIndex(_currentName, out _dataPosition); // AllocateStringForNameIndex could lock on _reader
1412 ResourceLocator locator;
1413 if (_reader._resCache.TryGetValue(key, out locator)) {
1414 value = locator.Value;
1416 if (value == null) {
1417 if (_dataPosition == -1)
1418 value = _reader.GetValueForNameIndex(_currentName);
1419 else
1420 value = _reader.LoadObject(_dataPosition);
1421 // If enumeration and subsequent lookups happen very
1422 // frequently in the same process, add a ResourceLocator
1423 // to _resCache here. But Microsoft enumerates and
1424 // just about everyone else does lookups. So caching
1425 // here may bloat working set.
1429 return new DictionaryEntry(key, value);
1433 public Object Value {
1434 get {
1435 if (_currentName == ENUM_DONE) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumEnded));
1436 if (!_currentIsValid) throw new InvalidOperationException(Environment.GetResourceString(ResId.InvalidOperation_EnumNotStarted));
1437 if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1439 // Consider using _resCache here, eventually, if
1440 // this proves to be an interesting perf scenario.
1441 // But mixing lookups and enumerators shouldn't be
1442 // particularly compelling.
1443 return _reader.GetValueForNameIndex(_currentName);
1447 public void Reset()
1449 if (_reader._resCache == null) throw new InvalidOperationException(Environment.GetResourceString("ResourceReaderIsClosed"));
1450 _currentIsValid = false;
1451 _currentName = ENUM_NOT_STARTED;