We need you! We're working hard on the next version of Developer Fusion - Let us know what you think we should be up to!

The Labs \ Source Viewer \ SSCLI \ System.Resources \ PrecannedResource

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*============================================================
  16. **
  17. ** Class:  ResourceWriter
  18. **
  19. **
  20. ** Purpose: Default way to write strings to a CLR resource
  21. ** file.
  22. **
  23. **
  24. ===========================================================*/
  25. namespace System.Resources
  26. {
  27.     using System;
  28.     using System.IO;
  29.     using System.Text;
  30.     using System.Collections;
  31.     using System.Collections.Generic;
  32.     using System.Runtime.Serialization;
  33.     using System.Runtime.Serialization.Formatters.Binary;
  34.     using System.Globalization;
  35.     using System.Runtime.Versioning;
  36.    
  37.     // Generates a binary .resources file in the system default format
  38.     // from name and value pairs. Create one with a unique file name,
  39.     // call AddResource() at least once, then call Generate() to write
  40.     // the .resources file to disk, then call Close() to close the file.
  41.     //
  42.     // The resources generally aren't written out in the same order
  43.     // they were added.
  44.     //
  45.     // See the RuntimeResourceSet overview for details on the system
  46.     // default file format.
  47.     //
  48.     [System.Runtime.InteropServices.ComVisible(true)]
  49.     public sealed class ResourceWriter : IResourceWriter
  50.     {
  51.         // For cases where users can't create an instance of the deserialized
  52.         // type in memory, and need to pass us serialized blobs instead.
  53.         // LocStudio's managed code parser will do this in some cases.
  54.         private class PrecannedResource
  55.         {
  56.             internal string TypeName;
  57.             internal byte[] Data;
  58.            
  59.             internal PrecannedResource(string typeName, byte[] data)
  60.             {
  61.                 TypeName = typeName;
  62.                 Data = data;
  63.             }
  64.         }
  65.        
  66.         // An initial size for our internal sorted list, to avoid extra resizes.
  67.         private const int _ExpectedNumberOfResources = 1000;
  68.         private const int AverageNameSize = 20 * 2;
  69.         // chars in little endian Unicode
  70.         private const int AverageValueSize = 40;
  71.        
  72.         private Hashtable _resourceList;
  73.         // Uses FastResourceComparer
  74.         private Stream _output;
  75.         private Hashtable _caseInsensitiveDups;
  76.         // To avoid names varying by case
  77.         private Hashtable _preserializedData;
  78.         // For AddResourceData
  79.         [ResourceExposure(ResourceScope.Machine)]
  80.         [ResourceConsumption(ResourceScope.Machine)]
  81.         public ResourceWriter(string fileName)
  82.         {
  83.             if (fileName == null)
  84.                 throw new ArgumentNullException("fileName");
  85.             _output = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None);
  86.             _resourceList = new Hashtable(_ExpectedNumberOfResources, FastResourceComparer.Default);
  87.             _caseInsensitiveDups = new Hashtable(StringComparer.OrdinalIgnoreCase);
  88.         }
  89.        
  90.         public ResourceWriter(Stream stream)
  91.         {
  92.             if (stream == null)
  93.                 throw new ArgumentNullException("stream");
  94.             if (!stream.CanWrite)
  95.                 throw new ArgumentException(Environment.GetResourceString("Argument_StreamNotWritable"));
  96.             _output = stream;
  97.             _resourceList = new Hashtable(_ExpectedNumberOfResources, FastResourceComparer.Default);
  98.             _caseInsensitiveDups = new Hashtable(StringComparer.OrdinalIgnoreCase);
  99.         }
  100.        
  101.        
  102.         // Adds a string resource to the list of resources to be written to a file.
  103.         // They aren't written until Generate() is called.
  104.         //
  105.         public void AddResource(string name, string value)
  106.         {
  107.             if (name == null)
  108.                 throw new ArgumentNullException("name");
  109.             if (_resourceList == null)
  110.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
  111.            
  112.             // Check for duplicate resources whose names vary only by case.
  113.             _caseInsensitiveDups.Add(name, null);
  114.             _resourceList.Add(name, value);
  115.         }
  116.        
  117.         // Adds a resource of type Object to the list of resources to be
  118.         // written to a file. They aren't written until Generate() is called.
  119.         //
  120.         public void AddResource(string name, object value)
  121.         {
  122.             if (name == null)
  123.                 throw new ArgumentNullException("name");
  124.             if (_resourceList == null)
  125.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
  126.            
  127.             // Check for duplicate resources whose names vary only by case.
  128.             _caseInsensitiveDups.Add(name, null);
  129.             _resourceList.Add(name, value);
  130.         }
  131.        
  132.         // Adds a named byte array as a resource to the list of resources to
  133.         // be written to a file. They aren't written until Generate() is called.
  134.         //
  135.         public void AddResource(string name, byte[] value)
  136.         {
  137.             if (name == null)
  138.                 throw new ArgumentNullException("name");
  139.             if (_resourceList == null)
  140.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
  141.            
  142.             // Check for duplicate resources whose names vary only by case.
  143.             _caseInsensitiveDups.Add(name, null);
  144.             _resourceList.Add(name, value);
  145.         }
  146.        
  147.         public void AddResourceData(string name, string typeName, byte[] serializedData)
  148.         {
  149.             if (name == null)
  150.                 throw new ArgumentNullException("name");
  151.             if (typeName == null)
  152.                 throw new ArgumentNullException("typeName");
  153.             if (serializedData == null)
  154.                 throw new ArgumentNullException("serializedData");
  155.             if (_resourceList == null)
  156.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
  157.            
  158.             // Check for duplicate resources whose names vary only by case.
  159.             _caseInsensitiveDups.Add(name, null);
  160.             if (_preserializedData == null)
  161.                 _preserializedData = new Hashtable(FastResourceComparer.Default);
  162.            
  163.             _preserializedData.Add(name, new PrecannedResource(typeName, serializedData));
  164.         }
  165.        
  166.        
  167.         // Closes the output stream.
  168.         public void Close()
  169.         {
  170.             Dispose(true);
  171.         }
  172.        
  173.         private void Dispose(bool disposing)
  174.         {
  175.             if (disposing) {
  176.                 if (_resourceList != null) {
  177.                     Generate();
  178.                 }
  179.                 if (_output != null) {
  180.                     _output.Close();
  181.                 }
  182.             }
  183.             _output = null;
  184.             _caseInsensitiveDups = null;
  185.             // _resourceList is set to null by Generate.
  186.         }
  187.        
  188.         public void Dispose()
  189.         {
  190.             Dispose(true);
  191.         }
  192.        
  193.         // After calling AddResource, Generate() writes out all resources to the
  194.         // output stream in the system default format.
  195.         // If an exception occurs during object serialization or during IO,
  196.         // the .resources file is closed and deleted, since it is most likely
  197.         // invalid.
  198.         public void Generate()
  199.         {
  200.             if (_resourceList == null)
  201.                 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ResourceWriterSaved"));
  202.            
  203.             BinaryWriter bw = new BinaryWriter(_output, Encoding.UTF8);
  204.             List<string> typeNames = new List<string>();
  205.            
  206.             // Write out the ResourceManager header
  207.             // Write out magic number
  208.             bw.Write(ResourceManager.MagicNumber);
  209.            
  210.             // Write out ResourceManager header version number
  211.             bw.Write(ResourceManager.HeaderVersionNumber);
  212.            
  213.             MemoryStream resMgrHeaderBlob = new MemoryStream(240);
  214.             BinaryWriter resMgrHeaderPart = new BinaryWriter(resMgrHeaderBlob);
  215.            
  216.             // Write out class name of IResourceReader capable of handling
  217.             // this file.
  218.             resMgrHeaderPart.Write(typeof(ResourceReader).AssemblyQualifiedName);
  219.            
  220.             // Write out class name of the ResourceSet class best suited to
  221.             // handling this file.
  222.             resMgrHeaderPart.Write(ResourceManager.ResSetTypeName);
  223.             resMgrHeaderPart.Flush();
  224.            
  225.             // Write number of bytes to skip over to get past ResMgr header
  226.             bw.Write((int)resMgrHeaderBlob.Length);
  227.            
  228.             // Write the rest of the ResMgr header
  229.             bw.Write(resMgrHeaderBlob.GetBuffer(), 0, (int)resMgrHeaderBlob.Length);
  230.             // End ResourceManager header
  231.            
  232.            
  233.             // Write out the RuntimeResourceSet header
  234.             // Version number
  235.             bw.Write(RuntimeResourceSet.Version);
  236.             #if RESOURCE_FILE_FORMAT_DEBUG
  237.             // Write out a tag so we know whether to enable or disable
  238.             // debugging support when reading the file.
  239.             bw.Write("***DEBUG***");
  240.             #endif
  241.            
  242.             // number of resources
  243.             int numResources = _resourceList.Count;
  244.             if (_preserializedData != null)
  245.                 numResources += _preserializedData.Count;
  246.             bw.Write(numResources);
  247.            
  248.             // Store values in temporary streams to write at end of file.
  249.             int[] nameHashes = new int[numResources];
  250.             int[] namePositions = new int[numResources];
  251.             int curNameNumber = 0;
  252.             MemoryStream nameSection = new MemoryStream(numResources * AverageNameSize);
  253.             BinaryWriter names = new BinaryWriter(nameSection, Encoding.Unicode);
  254.             MemoryStream dataSection = new MemoryStream(numResources * AverageValueSize);
  255.             BinaryWriter data = new BinaryWriter(dataSection, Encoding.UTF8);
  256.             IFormatter objFormatter = new BinaryFormatter(null, new StreamingContext(StreamingContextStates.File | StreamingContextStates.Persistence));
  257.            
  258.             #if RESOURCE_FILE_FORMAT_DEBUG
  259.             // Write NAMES right before the names section.
  260.             names.Write(new byte[] {(byte)'N', (byte)'A', (byte)'M', (byte)'E', (byte)'S', (byte)'-', (byte)'-', (byte)'>'});
  261.            
  262.             // Write DATA at the end of the name table section.
  263.             data.Write(new byte[] {(byte)'D', (byte)'A', (byte)'T', (byte)'A', (byte)'-', (byte)'-', (byte)'-', (byte)'>'});
  264.             #endif
  265.            
  266.             // We've stored our resources internally in a Hashtable, which
  267.             // makes no guarantees about the ordering while enumerating.
  268.             // While we do our own sorting of the resource names based on their
  269.             // hash values, that's only sorting the nameHashes and namePositions
  270.             // arrays. That's all that is strictly required for correctness,
  271.             // but for ease of generating a patch in the future that
  272.             // modifies just .resources files, we should re-sort them.
  273.             SortedList sortedResources = new SortedList(_resourceList, FastResourceComparer.Default);
  274.             if (_preserializedData != null) {
  275.                 foreach (DictionaryEntry entry in _preserializedData)
  276.                     sortedResources.Add(entry.Key, entry.Value);
  277.             }
  278.            
  279.            
  280.             IDictionaryEnumerator items = sortedResources.GetEnumerator();
  281.             // Write resource name and position to the file, and the value
  282.             // to our temporary buffer. Save Type as well.
  283.             while (items.MoveNext()) {
  284.                 nameHashes[curNameNumber] = FastResourceComparer.HashFunction((string)items.Key);
  285.                 namePositions[curNameNumber++] = (int)names.Seek(0, SeekOrigin.Current);
  286.                 names.Write((string)items.Key);
  287.                 // key
  288.                 names.Write((int)data.Seek(0, SeekOrigin.Current));
  289.                 // virtual offset of value.
  290.                 #if RESOURCE_FILE_FORMAT_DEBUG
  291.                 names.Write((byte)'*');
  292.                 #endif
  293.                 object value = items.Value;
  294.                 ResourceTypeCode typeCode = FindTypeCode(value, typeNames);
  295.                
  296.                 // Write out type code
  297.                 Write7BitEncodedInt(data, (int)typeCode);
  298.                
  299.                 // Write out value
  300.                 PrecannedResource userProvidedResource = value as PrecannedResource;
  301.                 if (userProvidedResource != null) {
  302.                     data.Write(userProvidedResource.Data);
  303.                 }
  304.                 else {
  305.                     WriteValue(typeCode, value, data, objFormatter);
  306.                 }
  307.                
  308.                 #if RESOURCE_FILE_FORMAT_DEBUG
  309.                 data.Write(new byte[] {(byte)'S', (byte)'T', (byte)'O', (byte)'P'});
  310.                 #endif
  311.             }
  312.            
  313.             // At this point, the ResourceManager header has been written.
  314.             // Finish RuntimeResourceSet header
  315.             // Write size & contents of class table
  316.             bw.Write(typeNames.Count);
  317.             for (int i = 0; i < typeNames.Count; i++)
  318.                 bw.Write(typeNames[i]);
  319.            
  320.             // Write out the name-related items for lookup.
  321.             // Note that the hash array and the namePositions array must
  322.             // be sorted in parallel.
  323.             Array.Sort(nameHashes, namePositions);
  324.            
  325.             // Prepare to write sorted name hashes (alignment fixup)
  326.             // Note: For 64-bit machines, these MUST be aligned on 8 byte
  327.             // boundaries! Pointers on IA64 must be aligned! And we'll
  328.             // run faster on X86 machines too.
  329.             bw.Flush();
  330.             int alignBytes = ((int)bw.BaseStream.Position) & 7;
  331.             if (alignBytes > 0) {
  332.                 for (int i = 0; i < 8 - alignBytes; i++)
  333.                     bw.Write("PAD"[i % 3]);
  334.             }
  335.            
  336.             // Write out sorted name hashes.
  337.             // Align to 8 bytes.
  338.             BCLDebug.Assert((bw.BaseStream.Position & 7) == 0, "ResourceWriter: Name hashes array won't be 8 byte aligned! Ack!");
  339.             #if RESOURCE_FILE_FORMAT_DEBUG
  340.             bw.Write(new byte[] {(byte)'H', (byte)'A', (byte)'S', (byte)'H', (byte)'E', (byte)'S', (byte)'-', (byte)'>'});
  341.             #endif
  342.             foreach (int hash in nameHashes)
  343.                 bw.Write(hash);
  344.             #if RESOURCE_FILE_FORMAT_DEBUG
  345.             Console.Write("Name hashes: ");
  346.             foreach (int hash in nameHashes)
  347.                 Console.Write(hash.ToString("x") + " ");
  348.             Console.WriteLine();
  349.             #endif
  350.            
  351.             // Write relative positions of all the names in the file.
  352.             // Note: this data is 4 byte aligned, occuring immediately
  353.             // after the 8 byte aligned name hashes (whose length may
  354.             // potentially be odd).
  355.             BCLDebug.Assert((bw.BaseStream.Position & 3) == 0, "ResourceWriter: Name positions array won't be 4 byte aligned! Ack!");
  356.             #if RESOURCE_FILE_FORMAT_DEBUG
  357.             bw.Write(new byte[] {(byte)'P', (byte)'O', (byte)'S', (byte)'-', (byte)'-', (byte)'-', (byte)'-', (byte)'>'});
  358.             #endif
  359.             foreach (int pos in namePositions)
  360.                 bw.Write(pos);
  361.             #if RESOURCE_FILE_FORMAT_DEBUG
  362.             Console.Write("Name positions: ");
  363.             foreach (int pos in namePositions)
  364.                 Console.Write(pos.ToString("x") + " ");
  365.             Console.WriteLine();
  366.             #endif
  367.            
  368.             // Flush all BinaryWriters to MemoryStreams.
  369.             bw.Flush();
  370.             names.Flush();
  371.             data.Flush();
  372.            
  373.             // Write offset to data section
  374.             int startOfDataSection = (int)(bw.Seek(0, SeekOrigin.Current) + nameSection.Length);
  375.             startOfDataSection += 4;
  376.             // We're writing an int to store this data, adding more bytes to the header
  377.             BCLDebug.Log("RESMGRFILEFORMAT", "Generate: start of DataSection: 0x" + startOfDataSection.ToString("x", CultureInfo.InvariantCulture) + " nameSection length: " + nameSection.Length);
  378.             bw.Write(startOfDataSection);
  379.            
  380.             // Write name section.
  381.             bw.Write(nameSection.GetBuffer(), 0, (int)nameSection.Length);
  382.             names.Close();
  383.            
  384.             // Write data section.
  385.             BCLDebug.Assert(startOfDataSection == bw.Seek(0, SeekOrigin.Current), "ResourceWriter::Generate - start of data section is wrong!");
  386.             bw.Write(dataSection.GetBuffer(), 0, (int)dataSection.Length);
  387.             data.Close();
  388.             bw.Flush();
  389.            
  390.             // Indicate we've called Generate
  391.             _resourceList = null;
  392.         }
  393.        
  394.         // Finds the ResourceTypeCode for a type, or adds this type to the
  395.         // types list.
  396.         private ResourceTypeCode FindTypeCode(object value, List<string> types)
  397.         {
  398.             if (value == null)
  399.                 return ResourceTypeCode.Null;
  400.            
  401.             Type type = value.GetType();
  402.             if (type == typeof(string))
  403.                 return ResourceTypeCode.String;
  404.             else if (type == typeof(Int32))
  405.                 return ResourceTypeCode.Int32;
  406.             else if (type == typeof(bool))
  407.                 return ResourceTypeCode.Boolean;
  408.             else if (type == typeof(char))
  409.                 return ResourceTypeCode.Char;
  410.             else if (type == typeof(byte))
  411.                 return ResourceTypeCode.Byte;
  412.             else if (type == typeof(sbyte))
  413.                 return ResourceTypeCode.SByte;
  414.             else if (type == typeof(Int16))
  415.                 return ResourceTypeCode.Int16;
  416.             else if (type == typeof(Int64))
  417.                 return ResourceTypeCode.Int64;
  418.             else if (type == typeof(UInt16))
  419.                 return ResourceTypeCode.UInt16;
  420.             else if (type == typeof(UInt32))
  421.                 return ResourceTypeCode.UInt32;
  422.             else if (type == typeof(UInt64))
  423.                 return ResourceTypeCode.UInt64;
  424.             else if (type == typeof(float))
  425.                 return ResourceTypeCode.Single;
  426.             else if (type == typeof(double))
  427.                 return ResourceTypeCode.Double;
  428.             else if (type == typeof(decimal))
  429.                 return ResourceTypeCode.Decimal;
  430.             else if (type == typeof(DateTime))
  431.                 return ResourceTypeCode.DateTime;
  432.             else if (type == typeof(TimeSpan))
  433.                 return ResourceTypeCode.TimeSpan;
  434.             else if (type == typeof(byte[]))
  435.                 return ResourceTypeCode.ByteArray;
  436.             else if (type == typeof(MemoryStream))
  437.                 return ResourceTypeCode.Stream;
  438.            
  439.            
  440.             // This is a user type, or a precanned resource. Find type
  441.             // table index. If not there, add new element.
  442.             string typeName;
  443.             if (type == typeof(PrecannedResource)) {
  444.                 typeName = ((PrecannedResource)value).TypeName;
  445.                 if (typeName.StartsWith("ResourceTypeCode.", StringComparison.Ordinal)) {
  446.                     typeName = typeName.Substring(17);
  447.                     // Remove through '.'
  448.                     ResourceTypeCode typeCode = (ResourceTypeCode)Enum.Parse(typeof(ResourceTypeCode), typeName);
  449.                     return typeCode;
  450.                 }
  451.             }
  452.             else
  453.                 typeName = type.AssemblyQualifiedName;
  454.            
  455.             int typeIndex = types.IndexOf(typeName);
  456.             if (typeIndex == -1) {
  457.                 typeIndex = types.Count;
  458.                 types.Add(typeName);
  459.             }
  460.            
  461.             return (ResourceTypeCode)(typeIndex + ResourceTypeCode.StartOfUserTypes);
  462.         }
  463.        
  464.        
  465.         // WriteValue takes a value and writes it to stream. It
  466.         // can take some specific action based on the type, such as write out a compact
  467.         // version of the object if it's a type recognized by this ResourceWriter, or
  468.         // use Serialization to write out the object using the objFormatter.
  469.         // For instance, the default implementation recognizes primitives such as Int32
  470.         // as special types and calls WriteInt32(int) with the value of the object. This
  471.         // can be much more compact than serializing the same object.
  472.         //
  473. /*
  474.         private void WriteValueV1(Type type, Object value, BinaryWriter writer, IFormatter objFormatter)
  475.         {
  476.             // For efficiency reasons, most of our primitive types will be explicitly
  477.             // recognized here.  Some value classes are also special cased here.
  478.             if (type == typeof(String))
  479.                 writer.Write((String) value);
  480.             else if (type == typeof(Int32))
  481.                 writer.Write((int)value);
  482.             else if (type == typeof(Byte))
  483.                 writer.Write((byte)value);
  484.             else if (type == typeof(SByte))
  485.                 writer.Write((sbyte)value);
  486.             else if (type == typeof(Int16))
  487.                 writer.Write((short)value);
  488.             else if (type == typeof(Int64))
  489.                 writer.Write((long)value);
  490.             else if (type == typeof(UInt16))
  491.                 writer.Write((ushort)value);
  492.             else if (type == typeof(UInt32))
  493.                 writer.Write((uint)value);
  494.             else if (type == typeof(UInt64))
  495.                 writer.Write((ulong)value);
  496.             else if (type == typeof(Single))
  497.                 writer.Write((float)value);
  498.             else if (type == typeof(Double))
  499.                 writer.Write((double)value);
  500.             else if (type == typeof(DateTime)) {
  501.                 // Ideally we should use DateTime's ToBinary & FromBinary,
  502.                 // but we can't for compatibility reasons.
  503.                 writer.Write(((DateTime)value).Ticks);
  504.             }
  505.             else if (type == typeof(TimeSpan))
  506.                 writer.Write(((TimeSpan)value).Ticks);
  507.             else if (type == typeof(Decimal)) {
  508.                 int[] bits = Decimal.GetBits((Decimal)value);
  509.                 BCLDebug.Assert(bits.Length == 4, "ResourceReader::LoadObject assumes Decimal's GetBits method returns an array of 4 ints");
  510.                 for(int i=0; i<bits.Length; i++)
  511.                     writer.Write(bits[i]);
  512.             }
  513.             else if (type == null) {
  514.                 BCLDebug.Assert(value == null, "Type is null iff value is null");
  515. #if RESOURCE_FILE_FORMAT_DEBUG
  516.                 writer.Write("<null value>");
  517. #endif
  518.             }
  519.             else
  520.                 objFormatter.Serialize(writer.BaseStream, value);
  521.         }
  522.         */       
  523.        
  524.         private void WriteValue(ResourceTypeCode typeCode, object value, BinaryWriter writer, IFormatter objFormatter)
  525.         {
  526.             switch (typeCode) {
  527.                 case ResourceTypeCode.Null:
  528.                     break;
  529.                 case ResourceTypeCode.String:
  530.                    
  531.                     writer.Write((string)value);
  532.                     break;
  533.                 case ResourceTypeCode.Boolean:
  534.                    
  535.                     writer.Write((bool)value);
  536.                     break;
  537.                 case ResourceTypeCode.Char:
  538.                    
  539.                     writer.Write((UInt16)(char)value);
  540.                     break;
  541.                 case ResourceTypeCode.Byte:
  542.                    
  543.                     writer.Write((byte)value);
  544.                     break;
  545.                 case ResourceTypeCode.SByte:
  546.                    
  547.                     writer.Write((sbyte)value);
  548.                     break;
  549.                 case ResourceTypeCode.Int16:
  550.                    
  551.                     writer.Write((Int16)value);
  552.                     break;
  553.                 case ResourceTypeCode.UInt16:
  554.                    
  555.                     writer.Write((UInt16)value);
  556.                     break;
  557.                 case ResourceTypeCode.Int32:
  558.                    
  559.                     writer.Write((Int32)value);
  560.                     break;
  561.                 case ResourceTypeCode.UInt32:
  562.                    
  563.                     writer.Write((UInt32)value);
  564.                     break;
  565.                 case ResourceTypeCode.Int64:
  566.                    
  567.                     writer.Write((Int64)value);
  568.                     break;
  569.                 case ResourceTypeCode.UInt64:
  570.                    
  571.                     writer.Write((UInt64)value);
  572.                     break;
  573.                 case ResourceTypeCode.Single:
  574.                    
  575.                     writer.Write((float)value);
  576.                     break;
  577.                 case ResourceTypeCode.Double:
  578.                    
  579.                     writer.Write((double)value);
  580.                     break;
  581.                 case ResourceTypeCode.Decimal:
  582.                    
  583.                     writer.Write((decimal)value);
  584.                     break;
  585.                 case ResourceTypeCode.DateTime:
  586.                    
  587.                     // Use DateTime's ToBinary & FromBinary.
  588.                     Int64 data = ((DateTime)value).ToBinary();
  589.                     writer.Write(data);
  590.                     break;
  591.                 case ResourceTypeCode.TimeSpan:
  592.                    
  593.                     writer.Write(((TimeSpan)value).Ticks);
  594.                     break;
  595.                 case ResourceTypeCode.ByteArray:
  596.                    
  597.                     // Special Types
  598.                    
  599.                     {
  600.                         byte[] bytes = (byte[])value;
  601.                         writer.Write(bytes.Length);
  602.                         writer.Write(bytes, 0, bytes.Length);
  603.                         break;
  604.                     }
  605.                     break;
  606.                 case ResourceTypeCode.Stream:
  607.                    
  608.                    
  609.                     {
  610.                         MemoryStream ms = (MemoryStream)value;
  611.                         if (ms.Length > Int32.MaxValue)
  612.                             throw new ArgumentException(Environment.GetResourceString("ArgumentOutOfRange_MemStreamLength"));
  613.                         int offset;
  614.                         int len;
  615.                         ms.InternalGetOriginAndLength(out offset, out len);
  616.                         byte[] bytes = ms.InternalGetBuffer();
  617.                         writer.Write(len);
  618.                         writer.Write(bytes, offset, len);
  619.                         break;
  620.                     }
  621.                     break;
  622.                 default:
  623.                    
  624.                     BCLDebug.Assert(typeCode >= ResourceTypeCode.StartOfUserTypes, String.Format(CultureInfo.InvariantCulture, "ResourceReader: Unsupported ResourceTypeCode in .resources file! {0}", typeCode));
  625.                     objFormatter.Serialize(writer.BaseStream, value);
  626.                     break;
  627.             }
  628.         }
  629.        
  630.         private static void Write7BitEncodedInt(BinaryWriter store, int value)
  631.         {
  632.             // Write out an int 7 bits at a time. The high bit of the byte,
  633.             // when on, tells reader to continue reading more bytes.
  634.             uint v = (uint)value;
  635.             // support negative numbers
  636.             while (v >= 128) {
  637.                 store.Write((byte)(v | 128));
  638.                 v >>= 7;
  639.             }
  640.             store.Write((byte)v);
  641.         }
  642.     }
  643. }

Developer Fusion