Browse Source

Fix #3001: Support new resources format in ResourcesFile/ResXResourceWriter

pull/3298/head
Siegfried Pammer 9 months ago
parent
commit
f9ae51b12a
  1. 2
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler/Util/ResXResourceWriter.cs
  3. 36
      ICSharpCode.Decompiler/Util/ResourcesFile.cs
  4. BIN
      ILSpy.Tests/BitmapContainer.resources
  5. 13
      ILSpy.Tests/ILSpy.Tests.csproj
  6. BIN
      ILSpy.Tests/Icon.ico
  7. 124
      ILSpy.Tests/IconContainer.resx
  8. 99
      ILSpy.Tests/ResourceReaderWriterTests.cs
  9. 0
      ILSpy.Tests/Test.resources

2
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -130,7 +130,6 @@
<Compile Include="ProjectDecompiler\TargetFrameworkTests.cs" /> <Compile Include="ProjectDecompiler\TargetFrameworkTests.cs" />
<Compile Include="TestAssemblyResolver.cs" /> <Compile Include="TestAssemblyResolver.cs" />
<Compile Include="TestCases\ILPretty\MonoFixed.cs" /> <Compile Include="TestCases\ILPretty\MonoFixed.cs" />
<Compile Include="Util\ResourceReaderWriterTests.cs" />
<None Include="TestCases\VBPretty\VBAutomaticEvents.vb" /> <None Include="TestCases\VBPretty\VBAutomaticEvents.vb" />
<Compile Include="TestCases\VBPretty\VBAutomaticEvents.cs" /> <Compile Include="TestCases\VBPretty\VBAutomaticEvents.cs" />
<Compile Include="TestCases\VBPretty\VBNonGenericForEach.cs" /> <Compile Include="TestCases\VBPretty\VBNonGenericForEach.cs" />
@ -356,7 +355,6 @@
<None Include="TestCases\VBPretty\VBCompoundAssign.vb" /> <None Include="TestCases\VBPretty\VBCompoundAssign.vb" />
<None Include="TestCases\VBPretty\Async.vb" /> <None Include="TestCases\VBPretty\Async.vb" />
<None Include="TestCases\VBPretty\VBPropertiesTest.vb" /> <None Include="TestCases\VBPretty\VBPropertiesTest.vb" />
<EmbeddedResource Include="Util\Test.resources" />
</ItemGroup> </ItemGroup>
</Project> </Project>

6
ICSharpCode.Decompiler/Util/ResXResourceWriter.cs

@ -36,6 +36,8 @@ using System.IO;
using System.Text; using System.Text;
using System.Xml; using System.Xml;
using ICSharpCode.Decompiler.Metadata;
namespace ICSharpCode.Decompiler.Util namespace ICSharpCode.Decompiler.Util
{ {
#if INSIDE_SYSTEM_WEB #if INSIDE_SYSTEM_WEB
@ -150,7 +152,7 @@ namespace ICSharpCode.Decompiler.Util
{ {
writer.WriteAttributeString("mimetype", BinSerializedObjectMimeType); writer.WriteAttributeString("mimetype", BinSerializedObjectMimeType);
writer.WriteStartElement("value"); writer.WriteStartElement("value");
writer.WriteBase64(value, offset, length); WriteNiceBase64(value, offset, length);
} }
writer.WriteEndElement(); writer.WriteEndElement();
@ -274,7 +276,7 @@ namespace ICSharpCode.Decompiler.Util
break; break;
case ResourceSerializedObject rso: case ResourceSerializedObject rso:
var bytes = rso.GetBytes(); var bytes = rso.GetBytes();
WriteBytes(name, null, bytes, 0, bytes.Length, comment); WriteBytes(name, rso.TypeName, bytes, 0, bytes.Length, comment);
break; break;
default: default:
throw new NotSupportedException($"Value '{value}' of type {value.GetType().FullName} is not supported by this version of ResXResourceWriter. Use byte arrays or streams instead."); throw new NotSupportedException($"Value '{value}' of type {value.GetType().FullName} is not supported by this version of ResXResourceWriter. Use byte arrays or streams instead.");

36
ICSharpCode.Decompiler/Util/ResourcesFile.cs

@ -76,12 +76,21 @@ namespace ICSharpCode.Decompiler.Util
StartOfUserTypes = 0x40 StartOfUserTypes = 0x40
} }
enum SerializationFormat
{
BinaryFormatter = 1,
TypeConverterByteArray = 2,
TypeConverterString = 3,
ActivatorStream = 4
}
/// <summary>Holds the number used to identify resource files.</summary> /// <summary>Holds the number used to identify resource files.</summary>
public const int MagicNumber = unchecked((int)0xBEEFCACE); public const int MagicNumber = unchecked((int)0xBEEFCACE);
const int ResourceSetVersion = 2; const int ResourceSetVersion = 2;
readonly MyBinaryReader reader; readonly MyBinaryReader reader;
readonly int version; readonly int version;
readonly bool usesSerializationFormat;
readonly int numResources; readonly int numResources;
readonly string[] typeTable; readonly string[] typeTable;
readonly int[] namePositions; readonly int[] namePositions;
@ -94,7 +103,7 @@ namespace ICSharpCode.Decompiler.Util
/// Creates a new ResourcesFile. /// Creates a new ResourcesFile.
/// </summary> /// </summary>
/// <param name="stream">Input stream.</param> /// <param name="stream">Input stream.</param>
/// <param name="leaveOpen">Whether the stream should be help open when the ResourcesFile is disposed.</param> /// <param name="leaveOpen">Whether the stream should be held open when the ResourcesFile is disposed.</param>
/// <remarks> /// <remarks>
/// The stream is must be held open while the ResourcesFile is in use. /// The stream is must be held open while the ResourcesFile is in use.
/// The stream must be seekable; any operation using the ResourcesFile will end up seeking the stream. /// The stream must be seekable; any operation using the ResourcesFile will end up seeking the stream.
@ -130,7 +139,8 @@ namespace ICSharpCode.Decompiler.Util
// We don't care about numBytesToSkip; read the rest of the header // We don't care about numBytesToSkip; read the rest of the header
// readerType: // readerType:
reader.ReadString(); string readerType = reader.ReadString();
usesSerializationFormat = readerType == "System.Resources.Extensions.DeserializingResourceReader, System.Resources.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51";
// resourceSetType: // resourceSetType:
reader.ReadString(); reader.ReadString();
} }
@ -369,7 +379,7 @@ namespace ICSharpCode.Decompiler.Util
bits[i] = reader.ReadInt32(); bits[i] = reader.ReadInt32();
return new decimal(bits); return new decimal(bits);
default: default:
return new ResourceSerializedObject(FindType(typeIndex), this, reader.BaseStream.Position); return new ResourceSerializedObject(FindType(typeIndex), this, reader.BaseStream.Position, usesSerializationFormat);
} }
} }
@ -461,7 +471,7 @@ namespace ICSharpCode.Decompiler.Util
{ {
throw new BadImageFormatException("Invalid typeCode"); throw new BadImageFormatException("Invalid typeCode");
} }
return new ResourceSerializedObject(FindType(typeCode - ResourceTypeCode.StartOfUserTypes), this, reader.BaseStream.Position); return new ResourceSerializedObject(FindType(typeCode - ResourceTypeCode.StartOfUserTypes), this, reader.BaseStream.Position, usesSerializationFormat);
} }
} }
@ -509,7 +519,7 @@ namespace ICSharpCode.Decompiler.Util
} }
} }
internal byte[] GetBytesForSerializedObject(long pos) internal byte[] GetBytesForSerializedObject(long pos, bool usesSerializationFormat)
{ {
long[] positions = GetStartPositions(); long[] positions = GetStartPositions();
int i = Array.BinarySearch(positions, pos); int i = Array.BinarySearch(positions, pos);
@ -535,6 +545,12 @@ namespace ICSharpCode.Decompiler.Util
} }
int len = (int)(endPos - pos); int len = (int)(endPos - pos);
reader.Seek(pos, SeekOrigin.Begin); reader.Seek(pos, SeekOrigin.Begin);
if (usesSerializationFormat)
{
int kind = reader.Read7BitEncodedInt();
Debug.Assert(Enum.IsDefined(typeof(SerializationFormat), kind));
len = reader.Read7BitEncodedInt();
}
return reader.ReadBytes(len); return reader.ReadBytes(len);
} }
} }
@ -545,12 +561,14 @@ namespace ICSharpCode.Decompiler.Util
public string? TypeName { get; } public string? TypeName { get; }
readonly ResourcesFile file; readonly ResourcesFile file;
readonly long position; readonly long position;
readonly bool usesSerializationFormat;
internal ResourceSerializedObject(string? typeName, ResourcesFile file, long position) internal ResourceSerializedObject(string? typeName, ResourcesFile file, long position, bool usesSerializationFormat)
{ {
this.TypeName = typeName; this.TypeName = usesSerializationFormat ? typeName : null;
this.file = file; this.file = file;
this.position = position; this.position = position;
this.usesSerializationFormat = usesSerializationFormat;
} }
/// <summary> /// <summary>
@ -558,7 +576,7 @@ namespace ICSharpCode.Decompiler.Util
/// </summary> /// </summary>
public Stream GetStream() public Stream GetStream()
{ {
return new MemoryStream(file.GetBytesForSerializedObject(position), writable: false); return new MemoryStream(file.GetBytesForSerializedObject(position, usesSerializationFormat), writable: false);
} }
/// <summary> /// <summary>
@ -566,7 +584,7 @@ namespace ICSharpCode.Decompiler.Util
/// </summary> /// </summary>
public byte[] GetBytes() public byte[] GetBytes()
{ {
return file.GetBytesForSerializedObject(position); return file.GetBytesForSerializedObject(position, usesSerializationFormat);
} }
} }
} }

BIN
ILSpy.Tests/BitmapContainer.resources

Binary file not shown.

13
ILSpy.Tests/ILSpy.Tests.csproj

@ -41,6 +41,19 @@
<Compile Include="Analyzers\TestCases\MainAssembly.cs" /> <Compile Include="Analyzers\TestCases\MainAssembly.cs" />
<Compile Include="Analyzers\TypeUsedByAnalyzerTests.cs" /> <Compile Include="Analyzers\TypeUsedByAnalyzerTests.cs" />
<Compile Include="CommandLineArgumentsTests.cs" /> <Compile Include="CommandLineArgumentsTests.cs" />
<Compile Include="ResourceReaderWriterTests.cs" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Icon.ico" />
<EmbeddedResource Include="Test.resources" />
<EmbeddedResource Include="IconContainer.resx">
<Generator>ResXCodeGenerator</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="BitmapContainer.resources" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

BIN
ILSpy.Tests/Icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

124
ILSpy.Tests/IconContainer.resx

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="Icon" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>Icon.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

99
ICSharpCode.Decompiler.Tests/Util/ResourceReaderWriterTests.cs → ILSpy.Tests/ResourceReaderWriterTests.cs

@ -17,11 +17,10 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Drawing;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Xml.Linq; using System.Xml.Linq;
using System.Xml.XPath; using System.Xml.XPath;
@ -30,13 +29,16 @@ using ICSharpCode.Decompiler.Util;
using NUnit.Framework; using NUnit.Framework;
using NUnit.Framework.Internal; using NUnit.Framework.Internal;
namespace ICSharpCode.Decompiler.Tests.Util using TomsToolbox.Essentials;
namespace ICSharpCode.ILSpy.Tests
{ {
[TestFixture] [TestFixture]
public class ResourceReaderWriterTests public class ResourceReaderWriterTests
{ {
const string WinFormsAssemblyName = ", System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; const string winFormsAssemblyName = ", System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
const string MSCorLibAssemblyName = ", mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"; const string msCorLibAssemblyName = ", mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
const string drawingAssemblyName = ", System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a";
[Serializable] [Serializable]
public class SerializableClass public class SerializableClass
@ -45,22 +47,27 @@ namespace ICSharpCode.Decompiler.Tests.Util
public int Age { get; set; } public int Age { get; set; }
} }
static readonly object[][] TestWriteCases = { static readonly object[][] testWriteCases = {
new object[] { "Decimal", 1.0m, "1.0", "System.Decimal" + MSCorLibAssemblyName }, new object[] { "Decimal", 1.0m, "1.0", "System.Decimal" + msCorLibAssemblyName },
new object[] { "TimeSpan", TimeSpan.FromSeconds(42), "00:00:42", "System.TimeSpan" + MSCorLibAssemblyName }, new object[] { "TimeSpan", TimeSpan.FromSeconds(42), "00:00:42", "System.TimeSpan" + msCorLibAssemblyName },
new object[] { "DateTime", DateTime.Parse("06/18/2023 21:36:30", CultureInfo.InvariantCulture), "06/18/2023 21:36:30", "System.DateTime" + MSCorLibAssemblyName }, new object[] { "DateTime", DateTime.Parse("06/18/2023 21:36:30", CultureInfo.InvariantCulture), "06/18/2023 21:36:30", "System.DateTime" + msCorLibAssemblyName },
}; };
static readonly object[][] TestReadCases = { static readonly object[][] testReadCases = {
new object[] { "Decimal", 1.0m }, new object[] { "Decimal", 1.0m },
new object[] { "TimeSpan", TimeSpan.FromSeconds(42) }, new object[] { "TimeSpan", TimeSpan.FromSeconds(42) },
new object[] { "DateTime", DateTime.Parse("06/18/2023 21:36:30", CultureInfo.InvariantCulture) }, new object[] { "DateTime", DateTime.Parse("06/18/2023 21:36:30", CultureInfo.InvariantCulture) },
}; };
static Stream GetResource(string fileName)
{
return typeof(ResourceReaderWriterTests).Assembly.GetManifestResourceStream(typeof(ResourceReaderWriterTests).Namespace + "." + fileName);
}
static MemoryStream ProduceResourcesTestFile<T>(string name, T value) static MemoryStream ProduceResourcesTestFile<T>(string name, T value)
{ {
var ms = new MemoryStream(); var ms = new MemoryStream();
var writer = new ResourceWriter(ms); var writer = new System.Resources.ResourceWriter(ms);
writer.AddResource(name, value); writer.AddResource(name, value);
writer.Generate(); writer.Generate();
ms.Position = 0; ms.Position = 0;
@ -70,7 +77,7 @@ namespace ICSharpCode.Decompiler.Tests.Util
static XElement ProduceResXTest<T>(string name, T value) static XElement ProduceResXTest<T>(string name, T value)
{ {
using var ms = new MemoryStream(); using var ms = new MemoryStream();
var writer = new ResXResourceWriter(ms); var writer = new Decompiler.Util.ResXResourceWriter(ms);
writer.AddResource(name, value); writer.AddResource(name, value);
writer.Generate(); writer.Generate();
ms.Position = 0; ms.Position = 0;
@ -94,7 +101,7 @@ namespace ICSharpCode.Decompiler.Tests.Util
[TestCase("Single", 1.0f)] [TestCase("Single", 1.0f)]
[TestCase("Double", 1.0d)] [TestCase("Double", 1.0d)]
[TestCase("Bytes", new byte[] { 42, 43, 44 })] [TestCase("Bytes", new byte[] { 42, 43, 44 })]
[TestCaseSource(nameof(TestReadCases))] [TestCaseSource(nameof(testReadCases))]
public void Read(string name, object value) public void Read(string name, object value)
{ {
using var testFile = ProduceResourcesTestFile(name, value); using var testFile = ProduceResourcesTestFile(name, value);
@ -105,22 +112,22 @@ namespace ICSharpCode.Decompiler.Tests.Util
Assert.That(items[0].Value, Is.EqualTo(value)); Assert.That(items[0].Value, Is.EqualTo(value));
} }
[TestCase("Null", null, null, "System.Resources.ResXNullRef" + WinFormsAssemblyName)] [TestCase("Null", null, null, "System.Resources.ResXNullRef" + winFormsAssemblyName)]
[TestCase("String", "Hello World!", "Hello World!", null)] [TestCase("String", "Hello World!", "Hello World!", null)]
[TestCase("Bool", true, "True", "System.Boolean" + MSCorLibAssemblyName)] [TestCase("Bool", true, "True", "System.Boolean" + msCorLibAssemblyName)]
[TestCase("Bool", false, "False", "System.Boolean" + MSCorLibAssemblyName)] [TestCase("Bool", false, "False", "System.Boolean" + msCorLibAssemblyName)]
[TestCase("Char", 'A', "A", "System.Char" + MSCorLibAssemblyName)] [TestCase("Char", 'A', "A", "System.Char" + msCorLibAssemblyName)]
[TestCase("Byte", (byte)1, "1", "System.Byte" + MSCorLibAssemblyName)] [TestCase("Byte", (byte)1, "1", "System.Byte" + msCorLibAssemblyName)]
[TestCase("SByte", (sbyte)-1, "-1", "System.SByte" + MSCorLibAssemblyName)] [TestCase("SByte", (sbyte)-1, "-1", "System.SByte" + msCorLibAssemblyName)]
[TestCase("Int16", (short)1, "1", "System.Int16" + MSCorLibAssemblyName)] [TestCase("Int16", (short)1, "1", "System.Int16" + msCorLibAssemblyName)]
[TestCase("UInt16", (ushort)1, "1", "System.UInt16" + MSCorLibAssemblyName)] [TestCase("UInt16", (ushort)1, "1", "System.UInt16" + msCorLibAssemblyName)]
[TestCase("Int32", 1, "1", "System.Int32" + MSCorLibAssemblyName)] [TestCase("Int32", 1, "1", "System.Int32" + msCorLibAssemblyName)]
[TestCase("UInt32", (uint)1, "1", "System.UInt32" + MSCorLibAssemblyName)] [TestCase("UInt32", (uint)1, "1", "System.UInt32" + msCorLibAssemblyName)]
[TestCase("Int64", (long)1, "1", "System.Int64" + MSCorLibAssemblyName)] [TestCase("Int64", (long)1, "1", "System.Int64" + msCorLibAssemblyName)]
[TestCase("UInt64", (ulong)1, "1", "System.UInt64" + MSCorLibAssemblyName)] [TestCase("UInt64", (ulong)1, "1", "System.UInt64" + msCorLibAssemblyName)]
[TestCase("Single", 1.0f, "1", "System.Single" + MSCorLibAssemblyName)] [TestCase("Single", 1.0f, "1", "System.Single" + msCorLibAssemblyName)]
[TestCase("Double", 1.0d, "1", "System.Double" + MSCorLibAssemblyName)] [TestCase("Double", 1.0d, "1", "System.Double" + msCorLibAssemblyName)]
[TestCaseSource(nameof(TestWriteCases))] [TestCaseSource(nameof(testWriteCases))]
public void Write(string name, object value, string serializedValue, string typeName) public void Write(string name, object value, string serializedValue, string typeName)
{ {
var element = ProduceResXTest(name, value); var element = ProduceResXTest(name, value);
@ -153,6 +160,8 @@ namespace ICSharpCode.Decompiler.Tests.Util
var item = items.FirstOrDefault(i => i.Key == "Bitmap"); var item = items.FirstOrDefault(i => i.Key == "Bitmap");
Assert.That(item.Key, Is.Not.Null); Assert.That(item.Key, Is.Not.Null);
Assert.That(item.Value, Is.InstanceOf<ResourceSerializedObject>()); Assert.That(item.Value, Is.InstanceOf<ResourceSerializedObject>());
var rso = (ResourceSerializedObject)item.Value;
Assert.That(rso.TypeName, Is.Null);
} }
[Test] [Test]
@ -185,5 +194,39 @@ namespace ICSharpCode.Decompiler.Tests.Util
Assert.That(item.Key, Is.Not.Null); Assert.That(item.Key, Is.Not.Null);
Assert.That(item.Value, Is.InstanceOf<MemoryStream>()); Assert.That(item.Value, Is.InstanceOf<MemoryStream>());
} }
[Test]
public void IconDataCanBeDeserializedFromResX()
{
// Uses new serialization format
var resourcesStream = GetResource("IconContainer.resources");
var reader = new ResourcesFile(resourcesStream);
var item = reader.Single();
Assert.That(item.Key, Is.EqualTo("Icon"));
Assert.That(item.Value, Is.InstanceOf<ResourceSerializedObject>());
var rso = (ResourceSerializedObject)item.Value;
var xml = ProduceResXTest("Icon", rso);
Assert.That(xml.GetAttribute("name"), Is.EqualTo("Icon"));
Assert.That(xml.GetAttribute("type"), Is.EqualTo("System.Drawing.Icon" + drawingAssemblyName));
Assert.That(xml.GetAttribute("mimetype"), Is.EqualTo("application/x-microsoft.net.object.bytearray.base64"));
var base64Icon = xml.Element("value").Value;
using var memory = new MemoryStream(Convert.FromBase64String(base64Icon));
new Icon(memory);
}
[Test]
public void BitmapDataCanBeDeserializedFromResX()
{
// Uses old serialization format
var resourcesStream = GetResource("BitmapContainer.resources");
var reader = new ResourcesFile(resourcesStream);
var item = reader.Single(x => x.Key == "Image1");
Assert.That(item.Value, Is.InstanceOf<ResourceSerializedObject>());
var rso = (ResourceSerializedObject)item.Value;
var xml = ProduceResXTest("Image1", rso);
Assert.That(xml.GetAttribute("name"), Is.EqualTo("Image1"));
Assert.That(xml.GetAttribute("type"), Is.Null);
Assert.That(xml.GetAttribute("mimetype"), Is.EqualTo("application/x-microsoft.net.object.binary.base64"));
}
} }
} }

0
ICSharpCode.Decompiler.Tests/Util/Test.resources → ILSpy.Tests/Test.resources

Loading…
Cancel
Save