aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.Resources
diff options
context:
space:
mode:
Diffstat (limited to 'src/dtf/WixToolset.Dtf.Resources')
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/AssemblyInfo.cs5
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/BitmapResource.cs57
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/FixedFileVersionInfo.cs183
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/GroupIconInfo.cs119
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/GroupIconResource.cs120
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/NativeMethods.cs55
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/Resource.cs225
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs340
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/ResourceType.cs198
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/VersionEnums.cs86
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/VersionInfo.cs270
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/VersionResource.cs415
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/VersionStringTable.cs231
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/WixToolset.Dtf.Resources.csproj17
14 files changed, 2321 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.Resources/AssemblyInfo.cs b/src/dtf/WixToolset.Dtf.Resources/AssemblyInfo.cs
new file mode 100644
index 00000000..77f33f97
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/AssemblyInfo.cs
@@ -0,0 +1,5 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3using System.Diagnostics.CodeAnalysis;
4
5[assembly: SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "WixToolset.Dtf.Resources.ResourceCollection.#System.Collections.Generic.ICollection`1<WixToolset.Dtf.Resources.Resource>.IsReadOnly")]
diff --git a/src/dtf/WixToolset.Dtf.Resources/BitmapResource.cs b/src/dtf/WixToolset.Dtf.Resources/BitmapResource.cs
new file mode 100644
index 00000000..42c886cb
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/BitmapResource.cs
@@ -0,0 +1,57 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7
8 /// <summary>
9 /// A subclass of Resource which provides specific methods for manipulating the resource data.
10 /// </summary>
11 /// <remarks>
12 /// The resource is of type <see cref="ResourceType.Bitmap"/> (RT_GROUPICON).
13 /// </remarks>
14 public sealed class BitmapResource : Resource
15 {
16 private const int SizeOfBitmapFileHeader = 14; // this is the sizeof(BITMAPFILEHEADER)
17
18 /// <summary>
19 /// Creates a new BitmapResource object without any data. The data can be later loaded from a file.
20 /// </summary>
21 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
22 /// <param name="locale">Locale of the resource</param>
23 public BitmapResource(string name, int locale)
24 : this(name, locale, null)
25 {
26 }
27
28 /// <summary>
29 /// Creates a new BitmapResource object with data. The data can be later saved to a file.
30 /// </summary>
31 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
32 /// <param name="locale">Locale of the resource</param>
33 /// <param name="data">Raw resource data</param>
34 public BitmapResource(string name, int locale, byte[] data)
35 : base(ResourceType.Bitmap, name, locale, data)
36 {
37 }
38
39 /// <summary>
40 /// Reads the bitmap from a .bmp file.
41 /// </summary>
42 /// <param name="path">Path to a bitmap file (.bmp).</param>
43 public void ReadFromFile(string path)
44 {
45 using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read))
46 {
47 // Move past the BITMAPFILEHEADER, and copy the rest of the bitmap as the resource data. Resource
48 // functions expect only the BITMAPINFO struct which exists just beyond the BITMAPFILEHEADER
49 // struct in bitmap files.
50 fs.Seek(BitmapResource.SizeOfBitmapFileHeader, SeekOrigin.Begin);
51
52 base.Data = new byte[fs.Length - BitmapResource.SizeOfBitmapFileHeader];
53 fs.Read(base.Data, 0, base.Data.Length);
54 }
55 }
56 }
57}
diff --git a/src/dtf/WixToolset.Dtf.Resources/FixedFileVersionInfo.cs b/src/dtf/WixToolset.Dtf.Resources/FixedFileVersionInfo.cs
new file mode 100644
index 00000000..e0d081db
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/FixedFileVersionInfo.cs
@@ -0,0 +1,183 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Globalization;
11 using System.Diagnostics.CodeAnalysis;
12
13 internal class FixedFileVersionInfo
14 {
15 public FixedFileVersionInfo()
16 {
17 // Set reasonable defaults
18 this.signature = 0xFEEF04BD;
19 this.structVersion = 0x00010000; // v1.0
20 this.FileVersion = new Version(0, 0, 0, 0);
21 this.ProductVersion = new Version(0, 0, 0, 0);
22 this.FileFlagsMask = VersionBuildTypes.Debug | VersionBuildTypes.Prerelease;
23 this.FileFlags = VersionBuildTypes.None;
24 this.FileOS = VersionFileOS.NT_WINDOWS32;
25 this.FileType = VersionFileType.Application;
26 this.FileSubtype = VersionFileSubtype.Unknown;
27 this.Timestamp = DateTime.MinValue;
28 }
29
30 private uint signature;
31 private uint structVersion;
32
33 public Version FileVersion
34 {
35 get
36 {
37 return this.fileVersion;
38 }
39
40 set
41 {
42 if (value == null)
43 {
44 throw new InvalidOperationException();
45 }
46
47 this.fileVersion = value;
48 }
49 }
50 private Version fileVersion;
51
52 public Version ProductVersion
53 {
54 get
55 {
56 return this.productVersion;
57 }
58
59 set
60 {
61 if (value == null)
62 {
63 throw new InvalidOperationException();
64 }
65
66 this.productVersion = value;
67 }
68 }
69 private Version productVersion;
70
71 public VersionBuildTypes FileFlagsMask
72 {
73 get { return this.fileFlagsMask; }
74 set { this.fileFlagsMask = value; }
75 }
76 private VersionBuildTypes fileFlagsMask;
77
78 public VersionBuildTypes FileFlags
79 {
80 get { return this.fileFlags; }
81 set { this.fileFlags = value; }
82 }
83 private VersionBuildTypes fileFlags;
84
85 [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
86 public VersionFileOS FileOS
87 {
88 get { return this.fileOS; }
89 set { this.fileOS = value; }
90 }
91 private VersionFileOS fileOS;
92
93 public VersionFileType FileType
94 {
95 get { return this.fileType; }
96 set { this.fileType = value; }
97 }
98 private VersionFileType fileType;
99
100 public VersionFileSubtype FileSubtype
101 {
102 get { return this.fileSubtype; }
103 set { this.fileSubtype = value; }
104 }
105 private VersionFileSubtype fileSubtype;
106
107 public DateTime Timestamp
108 {
109 get { return this.timestamp; }
110 set { this.timestamp = value; }
111 }
112 private DateTime timestamp;
113
114 public void Read(BinaryReader reader)
115 {
116 this.signature = reader.ReadUInt32();
117 this.structVersion = reader.ReadUInt32();
118 this.fileVersion = UInt64ToVersion(reader.ReadUInt64());
119 this.productVersion = UInt64ToVersion(reader.ReadUInt64());
120 this.fileFlagsMask = (VersionBuildTypes) reader.ReadInt32();
121 this.fileFlags = (VersionBuildTypes) reader.ReadInt32();
122 this.fileOS = (VersionFileOS) reader.ReadInt32();
123 this.fileType = (VersionFileType) reader.ReadInt32();
124 this.fileSubtype = (VersionFileSubtype) reader.ReadInt32();
125 this.timestamp = UInt64ToDateTime(reader.ReadUInt64());
126 }
127
128 public void Write(BinaryWriter writer)
129 {
130 writer.Write(this.signature);
131 writer.Write(this.structVersion);
132 writer.Write(VersionToUInt64(this.fileVersion));
133 writer.Write(VersionToUInt64(this.productVersion));
134 writer.Write((int) this.fileFlagsMask);
135 writer.Write((int) this.fileFlags);
136 writer.Write((int) this.fileOS);
137 writer.Write((int) this.fileType);
138 writer.Write((int) this.fileSubtype);
139 writer.Write(DateTimeToUInt64(this.timestamp));
140 }
141
142 public static explicit operator FixedFileVersionInfo(byte[] bytesValue)
143 {
144 FixedFileVersionInfo ffviValue = new FixedFileVersionInfo();
145 using (BinaryReader reader = new BinaryReader(new MemoryStream(bytesValue, false)))
146 {
147 ffviValue.Read(reader);
148 }
149 return ffviValue;
150 }
151
152 public static explicit operator byte[](FixedFileVersionInfo ffviValue)
153 {
154 const int FFVI_LENGTH = 52;
155
156 byte[] bytesValue = new byte[FFVI_LENGTH];
157 using (BinaryWriter writer = new BinaryWriter(new MemoryStream(bytesValue, true)))
158 {
159 ffviValue.Write(writer);
160 }
161 return bytesValue;
162 }
163
164 private static Version UInt64ToVersion(ulong version)
165 {
166 return new Version((int) ((version >> 16) & 0xFFFF), (int) (version & 0xFFFF), (int) (version >> 48), (int) ((version >> 32) & 0xFFFF));
167 }
168 private static ulong VersionToUInt64(Version version)
169 {
170 return (((ulong) (ushort) version.Major) << 16) | ((ulong) (ushort) version.Minor)
171 | (((ulong) (ushort) version.Build) << 48) | (((ulong) (ushort) version.Revision) << 32);
172 }
173
174 private static DateTime UInt64ToDateTime(ulong dateTime)
175 {
176 return (dateTime == 0 ? DateTime.MinValue : DateTime.FromFileTime((long) dateTime));
177 }
178 private static ulong DateTimeToUInt64(DateTime dateTime)
179 {
180 return (dateTime == DateTime.MinValue ? 0 : (ulong) dateTime.ToFileTime());
181 }
182 }
183}
diff --git a/src/dtf/WixToolset.Dtf.Resources/GroupIconInfo.cs b/src/dtf/WixToolset.Dtf.Resources/GroupIconInfo.cs
new file mode 100644
index 00000000..0fb56223
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/GroupIconInfo.cs
@@ -0,0 +1,119 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Globalization;
11 using System.Diagnostics.CodeAnalysis;
12
13 internal enum GroupIconType
14 {
15 Unknown,
16 Icon,
17 Cursor,
18 }
19
20 internal struct GroupIconDirectoryInfo
21 {
22 public byte width;
23 public byte height;
24 public byte colors;
25 public byte reserved;
26 public ushort planes;
27 public ushort bitsPerPixel;
28 public uint imageSize;
29 public uint imageOffset; // only valid when icon group is read from .ico file.
30 public ushort imageIndex; // only valid when icon group is read from PE resource.
31 }
32
33 internal class GroupIconInfo
34 {
35 private ushort reserved;
36 private GroupIconType type;
37 private GroupIconDirectoryInfo[] images;
38
39 public GroupIconInfo()
40 {
41 this.images = new GroupIconDirectoryInfo[0];
42 }
43
44 public GroupIconDirectoryInfo[] DirectoryInfo { get { return this.images; } }
45
46 public void ReadFromFile(Stream stream)
47 {
48 BinaryReader reader = new BinaryReader(stream);
49 this.Read(reader, true);
50 }
51
52 public void ReadFromResource(byte[] data)
53 {
54 using (BinaryReader reader = new BinaryReader(new MemoryStream(data, false)))
55 {
56 this.Read(reader, false);
57 }
58 }
59
60 public byte[] GetResourceData()
61 {
62 byte[] data = null;
63
64 using (MemoryStream stream = new MemoryStream())
65 {
66 BinaryWriter writer = new BinaryWriter(stream);
67 writer.Write(this.reserved);
68 writer.Write((ushort)this.type);
69 writer.Write((ushort)this.images.Length);
70 for (int i = 0; i < this.images.Length; ++i)
71 {
72 writer.Write(this.images[i].width);
73 writer.Write(this.images[i].height);
74 writer.Write(this.images[i].colors);
75 writer.Write(this.images[i].reserved);
76 writer.Write(this.images[i].planes);
77 writer.Write(this.images[i].bitsPerPixel);
78 writer.Write(this.images[i].imageSize);
79 writer.Write(this.images[i].imageIndex);
80 }
81
82 data = new byte[stream.Length];
83 stream.Seek(0, SeekOrigin.Begin);
84 stream.Read(data, 0, data.Length);
85 }
86
87 return data;
88 }
89
90 private void Read(BinaryReader reader, bool readFromFile)
91 {
92 this.reserved = reader.ReadUInt16();
93 this.type = (GroupIconType)reader.ReadUInt16();
94
95 int imageCount = reader.ReadUInt16();
96 this.images = new GroupIconDirectoryInfo[imageCount];
97 for (int i = 0; i < imageCount; ++i)
98 {
99 this.images[i].width = reader.ReadByte();
100 this.images[i].height = reader.ReadByte();
101 this.images[i].colors = reader.ReadByte();
102 this.images[i].reserved = reader.ReadByte();
103 this.images[i].planes = reader.ReadUInt16();
104 this.images[i].bitsPerPixel = reader.ReadUInt16();
105 this.images[i].imageSize = reader.ReadUInt32();
106 if (readFromFile)
107 {
108 this.images[i].imageOffset = reader.ReadUInt32();
109 this.images[i].imageIndex = (ushort)(i + 1);
110 }
111 else
112 {
113 this.images[i].imageIndex = reader.ReadUInt16();
114 }
115 }
116 }
117
118 }
119}
diff --git a/src/dtf/WixToolset.Dtf.Resources/GroupIconResource.cs b/src/dtf/WixToolset.Dtf.Resources/GroupIconResource.cs
new file mode 100644
index 00000000..8820ce75
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/GroupIconResource.cs
@@ -0,0 +1,120 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Globalization;
12 using System.Diagnostics.CodeAnalysis;
13
14 /// <summary>
15 /// A subclass of Resource which provides specific methods for manipulating the resource data.
16 /// </summary>
17 /// <remarks>
18 /// The resource is of type <see cref="ResourceType.GroupIcon"/> (RT_GROUPICON).
19 /// </remarks>
20 public sealed class GroupIconResource : Resource
21 {
22 internal bool dirty;
23 private GroupIconInfo rawGroupIconInfo;
24 private List<Resource> icons;
25
26 /// <summary>
27 /// Creates a new GroupIconResource object without any data. The data can be later loaded from a file.
28 /// </summary>
29 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
30 /// <param name="locale">Locale of the resource</param>
31 public GroupIconResource(string name, int locale)
32 : this(name, locale, null)
33 {
34 }
35
36 /// <summary>
37 /// Creates a new GroupIconResource object with data. The data can be later saved to a file.
38 /// </summary>
39 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
40 /// <param name="locale">Locale of the resource</param>
41 /// <param name="data">Raw resource data</param>
42 public GroupIconResource(string name, int locale, byte[] data)
43 : base(ResourceType.GroupIcon, name, locale, data)
44 {
45 this.RefreshIconGroupInfo(data);
46 }
47
48 /// <summary>
49 /// Gets or sets the raw data of the resource. The data is in the format of the RT_GROUPICON resource structure.
50 /// </summary>
51 public override byte[] Data
52 {
53 get
54 {
55 if (this.dirty)
56 {
57 base.Data = this.rawGroupIconInfo.GetResourceData();
58 this.dirty = false;
59 }
60
61 return base.Data;
62 }
63 set
64 {
65 this.RefreshIconGroupInfo(value);
66
67 base.Data = value;
68 this.dirty = false;
69 }
70 }
71
72 /// <summary>
73 /// Enumerates the the icons in the icon group.
74 /// </summary>
75 public IEnumerable<Resource> Icons { get { return this.icons; } }
76
77 /// <summary>
78 /// Reads the icon group from a .ico file.
79 /// </summary>
80 /// <param name="path">Path to an icon file (.ico).</param>
81 public void ReadFromFile(string path)
82 {
83 this.rawGroupIconInfo = new GroupIconInfo();
84 this.icons = new List<Resource>();
85 using (FileStream fs = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.Read))
86 {
87 this.rawGroupIconInfo.ReadFromFile(fs);
88
89 // After reading the group icon info header from the file, read all the icons.
90 for (int i = 0; i < this.rawGroupIconInfo.DirectoryInfo.Length; ++i)
91 {
92 ushort index = this.rawGroupIconInfo.DirectoryInfo[i].imageIndex;
93 uint offset = this.rawGroupIconInfo.DirectoryInfo[i].imageOffset;
94 uint size = this.rawGroupIconInfo.DirectoryInfo[i].imageSize;
95 byte[] data = new byte[size];
96
97 fs.Seek(offset, SeekOrigin.Begin);
98 fs.Read(data, 0, data.Length);
99
100 Resource resource = new Resource(ResourceType.Icon, String.Concat("#", index), this.Locale, data);
101 this.icons.Add(resource);
102 }
103 }
104
105 this.dirty = true;
106 }
107
108 private void RefreshIconGroupInfo(byte[] refreshData)
109 {
110 this.rawGroupIconInfo = new GroupIconInfo();
111 this.icons = new List<Resource>();
112 if (refreshData != null)
113 {
114 this.rawGroupIconInfo.ReadFromResource(refreshData);
115 }
116
117 this.dirty = true;
118 }
119 }
120}
diff --git a/src/dtf/WixToolset.Dtf.Resources/NativeMethods.cs b/src/dtf/WixToolset.Dtf.Resources/NativeMethods.cs
new file mode 100644
index 00000000..bf1bd1dc
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/NativeMethods.cs
@@ -0,0 +1,55 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Globalization;
11 using System.Runtime.InteropServices;
12
13 internal static class NativeMethods
14 {
15 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
16 internal static extern IntPtr LoadLibraryEx(string fileName, IntPtr hFile, uint flags);
17 internal const uint LOAD_LIBRARY_AS_DATAFILE = 2;
18 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
19 internal static extern bool FreeLibrary(IntPtr module);
20 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
21 internal static extern bool EnumResourceTypes(IntPtr module, EnumResTypesProc enumFunc, IntPtr param);
22 [return: MarshalAs(UnmanagedType.Bool)]
23 internal delegate bool EnumResTypesProc(IntPtr module, IntPtr type, IntPtr param);
24 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
25 internal static extern bool EnumResourceNames(IntPtr module, IntPtr type, EnumResNamesProc enumFunc, IntPtr param);
26 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
27 internal static extern bool EnumResourceNames(IntPtr module, string type, EnumResNamesProc enumFunc, IntPtr param);
28 [return: MarshalAs(UnmanagedType.Bool)]
29 internal delegate bool EnumResNamesProc(IntPtr module, IntPtr type, IntPtr name, IntPtr param);
30 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
31 internal static extern bool EnumResourceLanguages(IntPtr module, IntPtr type, IntPtr name, EnumResLangsProc enumFunc, IntPtr param);
32 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
33 internal static extern bool EnumResourceLanguages(IntPtr module, string type, string name, EnumResLangsProc enumFunc, IntPtr param);
34 [return: MarshalAs(UnmanagedType.Bool)]
35 internal delegate bool EnumResLangsProc(IntPtr module, IntPtr type, IntPtr name, ushort langId, IntPtr param);
36 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
37 internal static extern IntPtr FindResourceEx(IntPtr module, string type, string name, ushort langId);
38 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
39 internal static extern IntPtr LoadResource(IntPtr module, IntPtr resourceInfo);
40 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
41 internal static extern IntPtr LockResource(IntPtr resourceData);
42 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
43 internal static extern uint SizeofResource(IntPtr module, IntPtr resourceInfo);
44 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
45 internal static extern IntPtr BeginUpdateResource(string fileName, [MarshalAs(UnmanagedType.Bool)] bool deleteExistingResources);
46 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
47 internal static extern bool UpdateResource(IntPtr updateHandle, IntPtr type, IntPtr name, ushort lcid, IntPtr data, uint dataSize);
48 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]
49 internal static extern bool UpdateResource(IntPtr updateHandle, IntPtr type, string name, ushort lcid, IntPtr data, uint dataSize);
50 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
51 internal static extern bool UpdateResource(IntPtr updateHandle, string type, string name, ushort lcid, IntPtr data, uint dataSize);
52 [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)]
53 internal static extern bool EndUpdateResource(IntPtr updateHandle, [MarshalAs(UnmanagedType.Bool)] bool discardChanges);
54 }
55}
diff --git a/src/dtf/WixToolset.Dtf.Resources/Resource.cs b/src/dtf/WixToolset.Dtf.Resources/Resource.cs
new file mode 100644
index 00000000..23b4621f
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/Resource.cs
@@ -0,0 +1,225 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Globalization;
11 using System.Runtime.InteropServices;
12 using System.Diagnostics.CodeAnalysis;
13
14 /// <summary>
15 /// Represents a Win32 resource which can be loaded from and saved to a PE file.
16 /// </summary>
17 public class Resource
18 {
19 private ResourceType type;
20 private string name;
21 private int locale;
22 private byte[] data;
23
24 /// <summary>
25 /// Creates a new Resource object without any data. The data can be later loaded from a file.
26 /// </summary>
27 /// <param name="type">Type of the resource; may be one of the ResourceType constants or a user-defined type.</param>
28 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
29 /// <param name="locale">Locale of the resource</param>
30 public Resource(ResourceType type, string name, int locale)
31 : this(type, name, locale, null)
32 {
33 }
34
35 /// <summary>
36 /// Creates a new Resource object with data. The data can be later saved to a file.
37 /// </summary>
38 /// <param name="type">Type of the resource; may be one of the ResourceType constants or a user-defined type.</param>
39 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
40 /// <param name="locale">Locale of the resource</param>
41 /// <param name="data">Raw resource data</param>
42 public Resource(ResourceType type, string name, int locale, byte[] data)
43 {
44 if (name == null)
45 {
46 throw new ArgumentNullException("name");
47 }
48
49 this.type = type;
50 this.name = name;
51 this.locale = locale;
52 this.data = data;
53 }
54
55 /// <summary>
56 /// Gets or sets the type of the resource. This may be one of the ResourceType constants
57 /// or a user-defined type name.
58 /// </summary>
59 public ResourceType ResourceType
60 {
61 get { return this.type; }
62 set { this.type = value; }
63 }
64
65 /// <summary>
66 /// Gets or sets the name of the resource. For a numeric resource identifier, the decimal number is prefixed with a "#".
67 /// </summary>
68 public string Name
69 {
70 get
71 {
72 return this.name;
73 }
74
75 set
76 {
77 if (value == null)
78 {
79 throw new ArgumentNullException("value");
80 }
81
82 this.name = value;
83 }
84 }
85
86 /// <summary>
87 /// Gets or sets the locale of the resource.
88 /// </summary>
89 public int Locale
90 {
91 get { return this.locale; }
92 set { this.locale = value; }
93 }
94
95 /// <summary>
96 /// Gets or sets the raw data of the resource.
97 /// </summary>
98 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
99 public virtual byte[] Data
100 {
101 get { return this.data; }
102 set { this.data = value; }
103 }
104
105 /// <summary>
106 /// Loads the resource data from a file. The file is searched for a resource with matching type, name, and locale.
107 /// </summary>
108 /// <param name="file">Win32 PE file containing the resource</param>
109 public void Load(string file)
110 {
111 IntPtr module = NativeMethods.LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
112 try
113 {
114 this.Load(module);
115 }
116 finally
117 {
118 NativeMethods.FreeLibrary(module);
119 }
120 }
121
122 internal void Load(IntPtr module)
123 {
124 IntPtr resourceInfo = NativeMethods.FindResourceEx(module, (string) this.ResourceType, this.Name, (ushort) this.Locale);
125 if (resourceInfo != IntPtr.Zero)
126 {
127 uint resourceLength = NativeMethods.SizeofResource(module, resourceInfo);
128 IntPtr resourceData = NativeMethods.LoadResource(module, resourceInfo);
129 IntPtr resourcePtr = NativeMethods.LockResource(resourceData);
130 byte[] resourceBytes = new byte[resourceLength];
131 Marshal.Copy(resourcePtr, resourceBytes, 0, resourceBytes.Length);
132 this.Data = resourceBytes;
133 }
134 else
135 {
136 this.Data = null;
137 }
138 }
139
140 /// <summary>
141 /// Saves the resource to a file. Any existing resource data with matching type, name, and locale is overwritten.
142 /// </summary>
143 /// <param name="file">Win32 PE file to contain the resource</param>
144 public void Save(string file)
145 {
146 IntPtr updateHandle = IntPtr.Zero;
147 try
148 {
149 updateHandle = NativeMethods.BeginUpdateResource(file, false);
150 this.Save(updateHandle);
151 if (!NativeMethods.EndUpdateResource(updateHandle, false))
152 {
153 int err = Marshal.GetLastWin32Error();
154 throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to save resource. Error code: {0}", err));
155 }
156 updateHandle = IntPtr.Zero;
157 }
158 finally
159 {
160 if (updateHandle != IntPtr.Zero)
161 {
162 NativeMethods.EndUpdateResource(updateHandle, true);
163 }
164 }
165 }
166
167 internal void Save(IntPtr updateHandle)
168 {
169 IntPtr dataPtr = IntPtr.Zero;
170 try
171 {
172 int dataLength = 0;
173 if (this.Data != null)
174 {
175 dataLength = this.Data.Length;
176 dataPtr = Marshal.AllocHGlobal(dataLength);
177 Marshal.Copy(this.Data, 0, dataPtr, dataLength);
178 }
179 bool updateSuccess;
180 if (this.Name.StartsWith("#", StringComparison.Ordinal))
181 {
182 // A numeric-named resource must be saved via the integer version of UpdateResource.
183 IntPtr intName = new IntPtr(Int32.Parse(this.Name.Substring(1), CultureInfo.InvariantCulture));
184 updateSuccess = NativeMethods.UpdateResource(updateHandle, new IntPtr(this.ResourceType.IntegerValue), intName, (ushort) this.Locale, dataPtr, (uint) dataLength);
185 }
186 else
187 {
188 updateSuccess = NativeMethods.UpdateResource(updateHandle, (string) this.ResourceType, this.Name, (ushort) this.Locale, dataPtr, (uint) dataLength);
189 }
190 if (!updateSuccess)
191 {
192 throw new IOException("Failed to save resource. Error: " + Marshal.GetLastWin32Error());
193 }
194 }
195 finally
196 {
197 if (dataPtr != IntPtr.Zero)
198 {
199 Marshal.FreeHGlobal(dataPtr);
200 }
201 }
202 }
203
204 /// <summary>
205 /// Tests if type, name, and locale of this Resource object match another Resource object.
206 /// </summary>
207 /// <param name="obj">Resource object to be compared</param>
208 /// <returns>True if the objects represent the same resource; false otherwise.</returns>
209 public override bool Equals(object obj)
210 {
211 Resource res = obj as Resource;
212 if (res == null) return false;
213 return this.ResourceType == res.ResourceType && this.Name == res.Name && this.Locale == res.Locale;
214 }
215
216 /// <summary>
217 /// Gets a hash code for this Resource object.
218 /// </summary>
219 /// <returns>Hash code generated from the resource type, name, and locale.</returns>
220 public override int GetHashCode()
221 {
222 return this.ResourceType.GetHashCode() ^ this.Name.GetHashCode() ^ this.Locale.GetHashCode();
223 }
224 }
225}
diff --git a/src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs b/src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs
new file mode 100644
index 00000000..5d9e5653
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs
@@ -0,0 +1,340 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Globalization;
12 using System.Runtime.InteropServices;
13 using System.Diagnostics.CodeAnalysis;
14
15 /// <summary>
16 /// Allows reading and editing of resource data in a Win32 PE file.
17 /// </summary>
18 /// <remarks>
19 /// To use this class:<list type="number">
20 /// <item>Create a new ResourceCollection</item>
21 /// <item>Locate resources for the collection by calling one of the <see cref="ResourceCollection.Find(string)"/> methods</item>
22 /// <item>Load data of one or more <see cref="Resource"/>s from a file by calling the <see cref="Load"/> method of the
23 /// Resource class, or load them all at once (more efficient) with the <see cref="Load"/> method of the ResourceCollection.</item>
24 /// <item>Read and/or edit data of the individual Resource objects using the methods on that class.</item>
25 /// <item>Save data of one or more <see cref="Resource"/>s to a file by calling the <see cref="Save"/> method of the
26 /// Resource class, or save them all at once (more efficient) with the <see cref="Save"/> method of the ResourceCollection.</item>
27 /// </list>
28 /// </remarks>
29 public class ResourceCollection : ICollection<Resource>
30 {
31 private List<Resource> resources;
32
33 /// <summary>
34 /// Creates a new, empty ResourceCollection.
35 /// </summary>
36 public ResourceCollection()
37 {
38 this.resources = new List<Resource>();
39 }
40
41 /// <summary>
42 /// Locates all resources in a file, including all resource types and languages. For each located resource,
43 /// a <see cref="Resource"/> instance (or subclass) is added to the collection.
44 /// </summary>
45 /// <param name="resFile">The file to be searched for resources.</param>
46 /// <exception cref="IOException">resources could not be read from the file</exception>
47 public void Find(string resFile)
48 {
49 this.Clear();
50
51 IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
52 if (module == IntPtr.Zero)
53 {
54 int err = Marshal.GetLastWin32Error();
55 throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to load resource file. Error code: {0}", err));
56 }
57 try
58 {
59 if (!NativeMethods.EnumResourceTypes(module, new NativeMethods.EnumResTypesProc(this.EnumResTypes), IntPtr.Zero))
60 {
61 int err = Marshal.GetLastWin32Error();
62 throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to enumerate resources. Error code: {0}", err));
63 }
64 }
65 finally
66 {
67 NativeMethods.FreeLibrary(module);
68 }
69 }
70
71 /// <summary>
72 /// Locates all resources in a file of a given type, including all languages. For each located resource,
73 /// a <see cref="Resource"/> instance (or subclass) is added to the collection.
74 /// </summary>
75 /// <param name="resFile">The file to be searched for resources.</param>
76 /// <param name="type">The type of resource to search for; may be one of the ResourceType constants or a user-defined type.</param>
77 /// <exception cref="IOException">resources could not be read from the file</exception>
78 public void Find(string resFile, ResourceType type)
79 {
80 this.Clear();
81
82 IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
83 try
84 {
85 if (!NativeMethods.EnumResourceNames(module, (string) type, new NativeMethods.EnumResNamesProc(this.EnumResNames), IntPtr.Zero))
86 {
87 int err = Marshal.GetLastWin32Error();
88 throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error. Error code: {0}", err));
89 }
90 }
91 finally
92 {
93 NativeMethods.FreeLibrary(module);
94 }
95 }
96
97 /// <summary>
98 /// Locates all resources in a file of a given type and language. For each located resource,
99 /// a <see cref="Resource"/> instance (or subclass) is added to the collection.
100 /// </summary>
101 /// <param name="resFile">The file to be searched for resources.</param>
102 /// <param name="type">The type of resource to search for; may be one of the ResourceType constants or a user-defined type.</param>
103 /// <param name="name">The name of the resource to search for.</param>
104 /// <exception cref="IOException">resources could not be read from the file</exception>
105 public void Find(string resFile, ResourceType type, string name)
106 {
107 this.Clear();
108
109 IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
110 try
111 {
112 if (!NativeMethods.EnumResourceLanguages(module, (string) type, name, new NativeMethods.EnumResLangsProc(this.EnumResLangs), IntPtr.Zero))
113 {
114 int err = Marshal.GetLastWin32Error();
115 throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err));
116 }
117 }
118 finally
119 {
120 NativeMethods.FreeLibrary(module);
121 }
122 }
123
124 private bool EnumResTypes(IntPtr module, IntPtr type, IntPtr param)
125 {
126 if (!NativeMethods.EnumResourceNames(module, type, new NativeMethods.EnumResNamesProc(EnumResNames), IntPtr.Zero))
127 {
128 int err = Marshal.GetLastWin32Error();
129 throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error! Error code: {0}", err));
130 }
131 return true;
132 }
133
134 private bool EnumResNames(IntPtr module, IntPtr type, IntPtr name, IntPtr param)
135 {
136 if (!NativeMethods.EnumResourceLanguages(module, type, name, new NativeMethods.EnumResLangsProc(EnumResLangs), IntPtr.Zero))
137 {
138 int err = Marshal.GetLastWin32Error();
139 throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err));
140 }
141 return true;
142 }
143
144 private bool EnumResLangs(IntPtr module, IntPtr type, IntPtr name, ushort langId, IntPtr param)
145 {
146 Resource res;
147 if (((int) type) == ResourceType.Version.IntegerValue)
148 {
149 res = new VersionResource(ResourceNameToString(name), langId);
150 }
151 else
152 {
153 res = new Resource(ResourceNameToString(type), ResourceNameToString(name), langId);
154 }
155
156 if (!this.Contains(res))
157 {
158 this.Add(res);
159 }
160
161 return true;
162 }
163
164 private static string ResourceNameToString(IntPtr resName)
165 {
166 if ((resName.ToInt64() >> 16) == 0)
167 {
168 return "#" + resName.ToString();
169 }
170 else
171 {
172 return Marshal.PtrToStringAuto(resName);
173 }
174 }
175
176 /// <summary>
177 /// For all resources in the collection, loads their data from a resource file.
178 /// </summary>
179 /// <param name="file">The file from which resources are loaded.</param>
180 public void Load(string file)
181 {
182 IntPtr module = NativeMethods.LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
183 try
184 {
185 foreach (Resource res in this)
186 {
187 res.Load(module);
188 }
189 }
190 finally
191 {
192 NativeMethods.FreeLibrary(module);
193 }
194 }
195
196 /// <summary>
197 /// For all resources in the collection, saves their data to a resource file.
198 /// </summary>
199 /// <param name="file">The file to which resources are saved.</param>
200 public void Save(string file)
201 {
202 IntPtr updateHandle = IntPtr.Zero;
203 try
204 {
205 updateHandle = NativeMethods.BeginUpdateResource(file, false);
206 foreach (Resource res in this)
207 {
208 res.Save(updateHandle);
209 }
210 if (!NativeMethods.EndUpdateResource(updateHandle, false))
211 {
212 int err = Marshal.GetLastWin32Error();
213 throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to save resource. Error {0}", err));
214 }
215 updateHandle = IntPtr.Zero;
216 }
217 finally
218 {
219 if (updateHandle != IntPtr.Zero)
220 {
221 NativeMethods.EndUpdateResource(updateHandle, true);
222 }
223 }
224 }
225
226 /// <summary>
227 /// Gets or sets the element at the specified index.
228 /// </summary>
229 public Resource this[int index]
230 {
231 get
232 {
233 return (Resource) this.resources[index];
234 }
235 set
236 {
237 this.resources[index] = value;
238 }
239 }
240
241 /// <summary>
242 /// Adds a new item to the collection.
243 /// </summary>
244 /// <param name="item">The Resource to add.</param>
245 public void Add(Resource item)
246 {
247 this.resources.Add(item);
248 }
249
250 /// <summary>
251 /// Removes an item to the collection.
252 /// </summary>
253 /// <param name="item">The Resource to remove.</param>
254 public bool Remove(Resource item)
255 {
256 return this.resources.Remove(item);
257 }
258
259 /// <summary>
260 /// Gets the index of an item in the collection.
261 /// </summary>
262 /// <param name="item">The Resource to search for.</param>
263 /// <returns>The index of the item, or -1 if not found.</returns>
264 public int IndexOf(Resource item)
265 {
266 return this.resources.IndexOf(item);
267 }
268
269 /// <summary>
270 /// Inserts a item into the collection.
271 /// </summary>
272 /// <param name="index">The insertion index.</param>
273 /// <param name="item">The Resource to insert.</param>
274 public void Insert(int index, Resource item)
275 {
276 this.resources.Insert(index, item);
277 }
278
279 /// <summary>
280 /// Tests if the collection contains an item.
281 /// </summary>
282 /// <param name="item">The Resource to search for.</param>
283 /// <returns>true if the item is found; false otherwise</returns>
284 public bool Contains(Resource item)
285 {
286 return this.resources.Contains(item);
287 }
288
289 /// <summary>
290 /// Copies the collection into an array.
291 /// </summary>
292 /// <param name="array">The array to copy into.</param>
293 /// <param name="arrayIndex">The starting index in the destination array.</param>
294 public void CopyTo(Resource[] array, int arrayIndex)
295 {
296 this.resources.CopyTo(array, arrayIndex);
297 }
298
299 /// <summary>
300 /// Removes all resources from the collection.
301 /// </summary>
302 public void Clear()
303 {
304 this.resources.Clear();
305 }
306
307 /// <summary>
308 /// Gets the number of resources in the collection.
309 /// </summary>
310 public int Count
311 {
312 get
313 {
314 return this.resources.Count;
315 }
316 }
317
318 /// <summary>
319 /// Gets an enumerator over all resources in the collection.
320 /// </summary>
321 /// <returns></returns>
322 public IEnumerator<Resource> GetEnumerator()
323 {
324 return this.resources.GetEnumerator();
325 }
326
327 IEnumerator IEnumerable.GetEnumerator()
328 {
329 return ((IEnumerable) this.resources).GetEnumerator();
330 }
331
332 bool ICollection<Resource>.IsReadOnly
333 {
334 get
335 {
336 return false;
337 }
338 }
339 }
340}
diff --git a/src/dtf/WixToolset.Dtf.Resources/ResourceType.cs b/src/dtf/WixToolset.Dtf.Resources/ResourceType.cs
new file mode 100644
index 00000000..4b3971ab
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/ResourceType.cs
@@ -0,0 +1,198 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.Diagnostics.CodeAnalysis;
7 using System.Globalization;
8
9 /// <summary>
10 /// Represents either a standard integer resource type or a custom resource type name.
11 /// </summary>
12 public class ResourceType
13 {
14 // Silence warnings about doc-comments
15 #pragma warning disable 1591
16
17 public static ResourceType None { get { return "#0"; } }
18 public static ResourceType Cursor { get { return "#1"; } }
19 public static ResourceType Bitmap { get { return "#2"; } }
20 public static ResourceType Icon { get { return "#3"; } }
21 public static ResourceType Menu { get { return "#4"; } }
22 public static ResourceType Dialog { get { return "#5"; } }
23 public static ResourceType String { get { return "#6"; } }
24 public static ResourceType FontDir { get { return "#7"; } }
25 public static ResourceType Font { get { return "#8"; } }
26 public static ResourceType Accelerator { get { return "#9"; } }
27 public static ResourceType RCData { get { return "#10"; } }
28 public static ResourceType MessageTable { get { return "#11"; } }
29 public static ResourceType GroupCursor { get { return "#12"; } }
30 public static ResourceType GroupIcon { get { return "#14"; } }
31 public static ResourceType Version { get { return "#16"; } }
32 public static ResourceType DialogInclude { get { return "#17"; } }
33 public static ResourceType PlugPlay { get { return "#19"; } }
34 public static ResourceType Vxd { get { return "#20"; } }
35 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ani")]
36 public static ResourceType AniCursor { get { return "#21"; } }
37 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Ani")]
38 public static ResourceType AniIcon { get { return "#22"; } }
39 public static ResourceType Html { get { return "#23"; } }
40 public static ResourceType Manifest { get { return "#24"; } }
41
42 #pragma warning restore 1591
43
44 private string resourceType;
45
46 /// <summary>
47 /// Creates a new resource type from a string resource name.
48 /// </summary>
49 /// <param name="resourceType">String resource name,
50 /// or an integer resource type prefixed by a #.</param>
51 public ResourceType(string resourceType)
52 {
53 if (string.IsNullOrEmpty(resourceType))
54 {
55 throw new ArgumentNullException("resourceType");
56 }
57
58 this.resourceType = resourceType;
59
60 if (this.IsInteger && this.IntegerValue < 0)
61 {
62 throw new ArgumentOutOfRangeException("Invalid integer resource type value.");
63 }
64 }
65
66 /// <summary>
67 /// Creates a new integer resource type.
68 /// </summary>
69 /// <param name="resourceType">Integer value of a well-known resource type.</param>
70 public ResourceType(int resourceType)
71 : this("#" + resourceType)
72 {
73 }
74
75 /// <summary>
76 /// Gets a flag indicating whether the resource type is an integer type.
77 /// </summary>
78 public bool IsInteger
79 {
80 get
81 {
82 return this.resourceType.StartsWith("#", StringComparison.Ordinal);
83 }
84 }
85
86 /// <summary>
87 /// Gets the integer value of the resource type, or -1 if the resource type is not an integer.
88 /// </summary>
89 public int IntegerValue
90 {
91 get
92 {
93 int value;
94 if (!this.IsInteger ||
95 !Int32.TryParse(this.resourceType.Substring(1), out value))
96 {
97 value = -1;
98 }
99
100 return value;
101 }
102 }
103
104 /// <summary>
105 /// Gets a string representation of the resource type.
106 /// </summary>
107 /// <returns>The custom resource name, or the name of a well-known resource type.</returns>
108 public override string ToString()
109 {
110 if (this.IsInteger)
111 {
112 switch (this.IntegerValue)
113 {
114 case 0: return "None";
115 case 1: return "Cursor";
116 case 2: return "Bitmap";
117 case 3: return "Icon";
118 case 4: return "Menu";
119 case 5: return "Dialog";
120 case 6: return "String";
121 case 7: return "FontDir";
122 case 8: return "Font";
123 case 9: return "Accelerator";
124 case 10: return "RCData";
125 case 11: return "MessageTable";
126 case 12: return "GroupCursor";
127 case 14: return "GroupIcon";
128 case 16: return "Version";
129 case 17: return "DialogInclude";
130 case 19: return "PlugPlay";
131 case 20: return "Vxd";
132 case 21: return "AniCursor";
133 case 22: return "AniIcon";
134 case 23: return "Html";
135 case 24: return "Manifest";
136 }
137 }
138
139 return this.resourceType;
140 }
141
142 /// <summary>
143 /// Tests whether one resource type equals another object.
144 /// </summary>
145 /// <param name="obj">Other object.</param>
146 /// <returns>True if equal, else false.</returns>
147 public override bool Equals(object obj)
148 {
149 return this.Equals(obj as ResourceType);
150 }
151
152 /// <summary>
153 /// Tests whether one resource type equals another.
154 /// </summary>
155 /// <param name="otherType">Other resource type.</param>
156 /// <returns>True if equal, else false.</returns>
157 public bool Equals(ResourceType otherType)
158 {
159 return otherType != null && this.resourceType.Equals(otherType.resourceType, StringComparison.Ordinal);
160 }
161
162 /// <summary>
163 /// Gets a hash code suitable for using the resource type as a dictionary key.
164 /// </summary>
165 /// <returns>Hash code based on the resource type string.</returns>
166 public override int GetHashCode()
167 {
168 return this.resourceType.GetHashCode();
169 }
170
171 /// <summary>
172 /// Implicitly converts a string to a ResourceType.
173 /// </summary>
174 /// <param name="resourceType">String resource type to convert.</param>
175 /// <returns>ResourceType object.</returns>
176 public static implicit operator ResourceType(string resourceType)
177 {
178 return new ResourceType(resourceType);
179 }
180
181 /// <summary>
182 /// Explicitly converts a ResourceType to a string.
183 /// </summary>
184 /// <param name="resourceType">ResourceType object to convert.</param>
185 /// <returns>The resource type string.</returns>
186 /// <remarks>
187 /// Unlike <see cref="ToString" />, this conversion does not return
188 /// the common name of well-known integer resource types. Therefore,
189 /// the returned string is suitable for passing directly to Win32
190 /// resource APIs that accept resource type strings.
191 /// </remarks>
192 public static explicit operator string(ResourceType resourceType)
193 {
194 return resourceType.resourceType;
195 }
196 }
197}
198
diff --git a/src/dtf/WixToolset.Dtf.Resources/VersionEnums.cs b/src/dtf/WixToolset.Dtf.Resources/VersionEnums.cs
new file mode 100644
index 00000000..96e54b51
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/VersionEnums.cs
@@ -0,0 +1,86 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 // Silence warnings about doc-comments
6 #pragma warning disable 1591
7
8 using System;
9 using System.Diagnostics.CodeAnalysis;
10
11 /// <summary>
12 /// Identifies build types of a versioned file.
13 /// </summary>
14 [Flags]
15 public enum VersionBuildTypes : int
16 {
17 None = 0x00,
18 Debug = 0x01,
19 Prerelease = 0x02,
20 Patched = 0x04,
21 PrivateBuild = 0x08,
22 InfoInferred = 0x10,
23 SpecialBuild = 0x20,
24 }
25
26 [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
27 internal enum VersionFileOS : int
28 {
29 Unknown = 0x00000000,
30 DOS = 0x00010000,
31 OS216 = 0x00020000,
32 OS232 = 0x00030000,
33 NT = 0x00040000,
34 WINCE = 0x00050000,
35 WINDOWS16 = 0x00000001,
36 PM16 = 0x00000002,
37 PM32 = 0x00000003,
38 WINDOWS32 = 0x00000004,
39 DOS_WINDOWS16 = 0x00010001,
40 DOS_WINDOWS32 = 0x00010004,
41 OS216_PM16 = 0x00020002,
42 OS232_PM32 = 0x00030003,
43 NT_WINDOWS32 = 0x00040004,
44 }
45
46 /// <summary>
47 /// Identifies the type of a versioned file.
48 /// </summary>
49 [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")]
50 public enum VersionFileType : int
51 {
52 Unknown = 0,
53 Application = 1,
54 Dll = 2,
55 Driver = 3,
56 Font = 4,
57 VirtualDevice = 5,
58 StaticLibrary = 7,
59 }
60
61 /// <summary>
62 /// Identifies the sub-type of a versioned file.
63 /// </summary>
64 public enum VersionFileSubtype : int
65 {
66 Unknown = 0,
67 PrinterDriver = 1,
68 KeyboardDriver = 2,
69 LanguageDriver = 3,
70 DisplayDriver = 4,
71 MouseDriver = 5,
72 NetworkDriver = 6,
73 SystemDriver = 7,
74 InstallableDriver = 8,
75 SoundDriver = 9,
76 [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Comm")]
77 CommDriver = 10,
78 InputMethodDriver = 11,
79 VersionedPrinterDriver = 12,
80 RasterFont = 1,
81 VectorFont = 2,
82 TrueTypeFont = 3,
83 }
84
85 #pragma warning restore 1591
86}
diff --git a/src/dtf/WixToolset.Dtf.Resources/VersionInfo.cs b/src/dtf/WixToolset.Dtf.Resources/VersionInfo.cs
new file mode 100644
index 00000000..38224d12
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/VersionInfo.cs
@@ -0,0 +1,270 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Globalization;
12
13 internal class VersionInfo : ICollection<VersionInfo>
14 {
15 private string key;
16 private bool isString;
17 private byte[] data;
18 private List<VersionInfo> children;
19
20 public VersionInfo(string key)
21 : base()
22 {
23 if (key == null)
24 {
25 throw new ArgumentNullException("key");
26 }
27
28 this.key = key;
29 this.children = new List<VersionInfo>();
30 }
31
32 public string Key
33 {
34 get
35 {
36 return this.key;
37 }
38
39 set
40 {
41 if (value == null)
42 {
43 throw new ArgumentNullException("value");
44 }
45
46 this.key = value;
47 }
48 }
49
50 public bool IsString
51 {
52 get
53 {
54 return this.isString;
55 }
56
57 set
58 {
59 this.isString = value;
60 }
61 }
62
63 public byte[] Data
64 {
65 get
66 {
67 return this.data;
68 }
69
70 set
71 {
72 this.data = value;
73 this.isString = false;
74 }
75 }
76
77 public void Read(BinaryReader reader)
78 {
79 long basePosition = reader.BaseStream.Position;
80 int verInfoSize = (int) reader.ReadUInt16();
81 int valueSize = (int) reader.ReadUInt16();
82 bool dataIsString = (reader.ReadUInt16() != 0);
83 StringBuilder keyStringBuilder = new StringBuilder();
84 char c;
85 while ((c = (char) reader.ReadUInt16()) != 0)
86 {
87 keyStringBuilder.Append(c);
88 }
89 this.Key = keyStringBuilder.ToString();
90 Pad(reader, basePosition);
91 if (valueSize == 0)
92 {
93 this.data = null;
94 }
95 else
96 {
97 if (dataIsString) valueSize *= 2; // Count is # of chars instead of bytes
98 this.data = reader.ReadBytes(valueSize);
99 this.isString = dataIsString;
100 Pad(reader, basePosition);
101 }
102
103 while (reader.BaseStream.Position - basePosition < verInfoSize)
104 {
105 Pad(reader, basePosition);
106 VersionInfo childVerInfo = new VersionInfo("");
107 childVerInfo.Read(reader);
108 this.children.Add(childVerInfo);
109 }
110 }
111
112 public void Write(BinaryWriter writer)
113 {
114 long basePosition = writer.BaseStream.Position;
115 writer.Write((ushort) this.Length);
116 byte[] valueBytes = this.data;
117 writer.Write((ushort) ((valueBytes != null ? valueBytes.Length : 0) / (this.IsString ? 2 : 1)));
118 writer.Write((ushort) (this.IsString ? 1 : 0));
119 byte[] keyBytes = new byte[Encoding.Unicode.GetByteCount(this.Key) + 2];
120 Encoding.Unicode.GetBytes(this.Key, 0, this.Key.Length, keyBytes, 0);
121 writer.Write(keyBytes);
122 Pad(writer, basePosition);
123 if (valueBytes != null)
124 {
125 writer.Write(valueBytes);
126 Pad(writer, basePosition);
127 }
128
129 foreach (VersionInfo childVersionInfo in this.children)
130 {
131 Pad(writer, basePosition);
132 childVersionInfo.Write(writer);
133 }
134 }
135
136 private static void Pad(BinaryReader reader, long basePosition)
137 {
138 long position = reader.BaseStream.Position;
139 int diff = (int) (position - basePosition) % 4;
140 if (diff > 0) while (diff++ < 4 && reader.BaseStream.Position < reader.BaseStream.Length) reader.ReadByte();
141 }
142 private static void Pad(BinaryWriter writer, long basePosition)
143 {
144 long position = writer.BaseStream.Position;
145 int diff = (int) (position - basePosition) % 4;
146 if (diff > 0) while (diff++ < 4) writer.Write((byte) 0);
147 }
148
149 private int Length
150 {
151 get
152 {
153 int len = 6 + Encoding.Unicode.GetByteCount(this.Key) + 2;
154 if (len % 4 > 0) len += (4 - len % 4);
155 len += (this.data != null ? this.data.Length : 0);
156 if (len % 4 > 0) len += (4 - len % 4);
157 foreach (VersionInfo childVersionInfo in this.children)
158 {
159 if (len % 4 > 0) len += (4 - len % 4);
160 len += childVersionInfo.Length;
161 }
162 return len;
163 }
164 }
165
166 public static explicit operator VersionInfo(byte[] bytesValue)
167 {
168 VersionInfo viValue = new VersionInfo("");
169 using (BinaryReader reader = new BinaryReader(new MemoryStream(bytesValue, false)))
170 {
171 viValue.Read(reader);
172 }
173 return viValue;
174 }
175
176 public static explicit operator byte[](VersionInfo viValue)
177 {
178 byte[] bytesValue = new byte[viValue.Length];
179 using (BinaryWriter writer = new BinaryWriter(new MemoryStream(bytesValue, true)))
180 {
181 viValue.Write(writer);
182 }
183 return bytesValue;
184 }
185
186 public VersionInfo this[string itemKey]
187 {
188 get
189 {
190 int index = this.IndexOf(itemKey);
191 if (index < 0) return null;
192 return this.children[index];
193 }
194 }
195
196 public void Add(VersionInfo item)
197 {
198 this.children.Add(item);
199 }
200
201 public bool Remove(VersionInfo item)
202 {
203 return this.children.Remove(item);
204 }
205
206 public bool Remove(string itemKey)
207 {
208 int index = this.IndexOf(itemKey);
209 if (index >= 0)
210 {
211 this.children.RemoveAt(index);
212 return true;
213 }
214 else
215 {
216 return false;
217 }
218 }
219
220 private int IndexOf(string itemKey)
221 {
222 for (int i = 0; i < this.children.Count; i++)
223 {
224 if (this.children[i].Key == itemKey) return i;
225 }
226 return -1;
227 }
228
229 public bool Contains(VersionInfo item)
230 {
231 return this.children.Contains(item);
232 }
233
234 public void CopyTo(VersionInfo[] array, int index)
235 {
236 this.children.CopyTo(array, index);
237 }
238
239 public void Clear()
240 {
241 this.children.Clear();
242 }
243
244 public int Count
245 {
246 get
247 {
248 return this.children.Count;
249 }
250 }
251
252 public bool IsReadOnly
253 {
254 get
255 {
256 return false;
257 }
258 }
259
260 public IEnumerator<VersionInfo> GetEnumerator()
261 {
262 return this.children.GetEnumerator();
263 }
264
265 IEnumerator IEnumerable.GetEnumerator()
266 {
267 return this.GetEnumerator();
268 }
269 }
270}
diff --git a/src/dtf/WixToolset.Dtf.Resources/VersionResource.cs b/src/dtf/WixToolset.Dtf.Resources/VersionResource.cs
new file mode 100644
index 00000000..9955fa03
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/VersionResource.cs
@@ -0,0 +1,415 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Globalization;
12 using System.Diagnostics.CodeAnalysis;
13
14 /// <summary>
15 /// A subclass of Resource which provides specific methods for manipulating the resource data.
16 /// </summary>
17 /// <remarks>
18 /// The resource is of type <see cref="ResourceType.Version"/> (RT_VERSION).
19 /// </remarks>
20 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
21 public sealed class VersionResource : Resource, ICollection<VersionStringTable>
22 {
23 internal bool dirty;
24 private VersionInfo rawVersionInfo;
25 private FixedFileVersionInfo rawFileVersionInfo;
26
27 /// <summary>
28 /// Creates a new VersionResource object without any data. The data can be later loaded from a file.
29 /// </summary>
30 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
31 /// <param name="locale">Locale of the resource</param>
32 public VersionResource(string name, int locale)
33 : this(name, locale, null)
34 {
35 }
36
37 /// <summary>
38 /// Creates a new VersionResource object with data. The data can be later saved to a file.
39 /// </summary>
40 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
41 /// <param name="locale">Locale of the resource</param>
42 /// <param name="data">Raw resource data</param>
43 public VersionResource(string name, int locale, byte[] data)
44 : base(ResourceType.Version, name, locale, data)
45 {
46 this.RefreshVersionInfo(data);
47 }
48
49 /// <summary>
50 /// Gets or sets the raw data of the resource. The data is in the format of the VS_VERSIONINFO structure.
51 /// </summary>
52 public override byte[] Data
53 {
54 get
55 {
56 if (this.dirty)
57 {
58 this.rawVersionInfo.Data = (byte[]) this.rawFileVersionInfo;
59 base.Data = (byte[]) this.rawVersionInfo;
60 this.dirty = false;
61 }
62
63 return base.Data;
64 }
65 set
66 {
67 base.Data = value;
68 this.RefreshVersionInfo(value);
69 this.dirty = false;
70 }
71 }
72
73 private void RefreshVersionInfo(byte[] refreshData)
74 {
75 if (refreshData == null)
76 {
77 this.rawVersionInfo = new VersionInfo("VS_VERSION_INFO");
78 this.rawFileVersionInfo = new FixedFileVersionInfo();
79 }
80 else
81 {
82 this.rawVersionInfo = (VersionInfo) refreshData;
83 this.rawFileVersionInfo = (FixedFileVersionInfo) this.rawVersionInfo.Data;
84 }
85 }
86
87 /// <summary>
88 /// Gets or sets the binary locale-independent file version of the version resource.
89 /// </summary>
90 public Version FileVersion
91 {
92 get
93 {
94 return this.rawFileVersionInfo.FileVersion;
95 }
96 set
97 {
98 this.rawFileVersionInfo.FileVersion = value;
99 this.dirty = true;
100 }
101 }
102
103 /// <summary>
104 /// Gets or sets the binary locale-independent product version of the version resource.
105 /// </summary>
106 public Version ProductVersion
107 {
108 get
109 {
110 return this.rawFileVersionInfo.ProductVersion;
111 }
112 set
113 {
114 this.rawFileVersionInfo.ProductVersion = value;
115 this.dirty = true;
116 }
117 }
118
119 /// <summary>
120 /// Gets or sets a bitmask that specifies the build types of the file.
121 /// </summary>
122 public VersionBuildTypes BuildTypes
123 {
124 get
125 {
126 return this.rawFileVersionInfo.FileFlags &
127 this.rawFileVersionInfo.FileFlagsMask;
128 }
129 set
130 {
131 this.rawFileVersionInfo.FileFlags = value;
132 this.rawFileVersionInfo.FileFlagsMask = value;
133 this.dirty = true;
134 }
135 }
136
137 /// <summary>
138 /// Gets or sets the general type of the file.
139 /// </summary>
140 public VersionFileType FileType
141 {
142 get
143 {
144 return this.rawFileVersionInfo.FileType;
145 }
146 set
147 {
148 this.rawFileVersionInfo.FileType = value;
149 this.dirty = true;
150 }
151 }
152
153 /// <summary>
154 /// Gets or sets the specific type of the file.
155 /// </summary>
156 public VersionFileSubtype FileSubtype
157 {
158 get
159 {
160 return this.rawFileVersionInfo.FileSubtype;
161 }
162 set
163 {
164 this.rawFileVersionInfo.FileSubtype = value;
165 this.dirty = true;
166 }
167 }
168
169 /// <summary>
170 /// Gets or sets the binary creation date and time.
171 /// </summary>
172 public DateTime Timestamp
173 {
174 get
175 {
176 return this.rawFileVersionInfo.Timestamp;
177 }
178 set
179 {
180 this.rawFileVersionInfo.Timestamp = value;
181 this.dirty = true;
182 }
183 }
184
185 /// <summary>
186 /// Gets the string table for a specific locale, or null if there is no table for that locale.
187 /// </summary>
188 /// <seealso cref="Add(int)"/>
189 /// <seealso cref="Remove(int)"/>
190 public VersionStringTable this[int locale]
191 {
192 get
193 {
194 VersionInfo svi = this.rawVersionInfo["StringFileInfo"];
195 if (svi != null)
196 {
197 foreach (VersionInfo strings in svi)
198 {
199 int stringsLocale = UInt16.Parse(strings.Key.Substring(0, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
200 if (stringsLocale == locale)
201 {
202 return new VersionStringTable(this, strings);
203 }
204 }
205 }
206 return null;
207 }
208 }
209
210 /// <summary>
211 /// Adds a new version string table for a locale.
212 /// </summary>
213 /// <param name="locale">Locale of the table</param>
214 /// <returns>The new string table, or the existing table if the locale already existed.</returns>
215 public VersionStringTable Add(int locale)
216 {
217 VersionInfo svi = this.rawVersionInfo["StringFileInfo"];
218 if (svi == null)
219 {
220 svi = new VersionInfo("StringFileInfo");
221 this.rawVersionInfo.Add(svi);
222 }
223 foreach (VersionInfo strings in svi)
224 {
225 int stringsLocale = UInt16.Parse(strings.Key.Substring(0, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
226 if (stringsLocale == locale)
227 {
228 return new VersionStringTable(this, strings);
229 }
230 }
231
232 VersionInfo newStrings = new VersionInfo(
233 ((ushort) locale).ToString("x4", CultureInfo.InvariantCulture) + ((ushort) 1200).ToString("x4", CultureInfo.InvariantCulture));
234 svi.Add(newStrings);
235 this.dirty = true;
236
237 VersionInfo vvi = this.rawVersionInfo["VarFileInfo"];
238 if (vvi == null)
239 {
240 vvi = new VersionInfo("VarFileInfo");
241 vvi.Add(new VersionInfo("Translation"));
242 this.rawVersionInfo.Add(vvi);
243 }
244 VersionInfo tVerInfo = vvi["Translation"];
245 if (tVerInfo != null)
246 {
247 byte[] oldValue = tVerInfo.Data;
248 if (oldValue == null) oldValue = new byte[0];
249 tVerInfo.Data = new byte[oldValue.Length + 4];
250 Array.Copy(oldValue, tVerInfo.Data, oldValue.Length);
251 using (BinaryWriter bw = new BinaryWriter(new MemoryStream(tVerInfo.Data, oldValue.Length, 4, true)))
252 {
253 bw.Write((ushort) locale);
254 bw.Write((ushort) 1200);
255 }
256 }
257
258 return new VersionStringTable(this, newStrings);
259 }
260
261 /// <summary>
262 /// Removes a version string table for a locale.
263 /// </summary>
264 /// <param name="locale">Locale of the table</param>
265 public void Remove(int locale)
266 {
267 VersionInfo svi = this.rawVersionInfo["StringFileInfo"];
268 if (svi != null)
269 {
270 foreach (VersionInfo strings in svi)
271 {
272 int stringsLocale = UInt16.Parse(strings.Key.Substring(0, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
273 if (stringsLocale == locale)
274 {
275 svi.Remove(strings);
276 this.dirty = true;
277 break;
278 }
279 }
280
281 }
282
283 VersionInfo vvi = this.rawVersionInfo["VarFileInfo"];
284 if (vvi != null)
285 {
286 VersionInfo tVerInfo = vvi["Translation"];
287 if (tVerInfo != null)
288 {
289 byte[] newValue = new byte[tVerInfo.Data.Length];
290 int j = 0;
291 using (BinaryWriter bw = new BinaryWriter(new MemoryStream(newValue, 0, newValue.Length, true)))
292 {
293 using (BinaryReader br = new BinaryReader(new MemoryStream(tVerInfo.Data)))
294 {
295 for (int i = tVerInfo.Data.Length / 4; i > 0; i--)
296 {
297 ushort tLocale = br.ReadUInt16();
298 ushort cp = br.ReadUInt16();
299 if (tLocale != locale)
300 {
301 bw.Write((ushort) tLocale);
302 bw.Write((ushort) cp);
303 j++;
304 }
305 }
306 }
307 }
308 tVerInfo.Data = new byte[j * 4];
309 Array.Copy(newValue, tVerInfo.Data, tVerInfo.Data.Length);
310 }
311 }
312 }
313
314 /// <summary>
315 /// Checks if a version string table exists for a given locale.
316 /// </summary>
317 /// <param name="locale">Locale to search for</param>
318 /// <returns>True if a string table was found for the locale; false otherwise.</returns>
319 public bool Contains(int locale)
320 {
321 return this[locale] != null;
322 }
323
324 /// <summary>
325 /// Gets the number string tables in the version resource.
326 /// </summary>
327 public int Count
328 {
329 get
330 {
331 VersionInfo svi = this.rawVersionInfo["StringFileInfo"];
332 return svi != null ? svi.Count : 0;
333 }
334 }
335
336 /// <summary>
337 /// Removes all string tables from the version resource.
338 /// </summary>
339 public void Clear()
340 {
341 VersionInfo svi = this.rawVersionInfo["StringFileInfo"];
342 if (svi != null)
343 {
344 svi.Clear();
345 this.dirty = true;
346 }
347 }
348
349 bool ICollection<VersionStringTable>.IsReadOnly
350 {
351 get
352 {
353 return false;
354 }
355 }
356
357 void ICollection<VersionStringTable>.Add(VersionStringTable item)
358 {
359 throw new NotSupportedException();
360 }
361
362 bool ICollection<VersionStringTable>.Remove(VersionStringTable item)
363 {
364 throw new NotSupportedException();
365 }
366
367 bool ICollection<VersionStringTable>.Contains(VersionStringTable item)
368 {
369 throw new NotSupportedException();
370 }
371
372 /// <summary>
373 /// Copies the version string tables to an array, starting at a particular array index.
374 /// </summary>
375 /// <param name="array">The one-dimensional Array that is the destination of the elements copied
376 /// from the collection. The Array must have zero-based indexing.</param>
377 /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
378 public void CopyTo(VersionStringTable[] array, int arrayIndex)
379 {
380 VersionInfo svi = this.rawVersionInfo["StringFileInfo"];
381 if (svi != null)
382 {
383 foreach (VersionInfo strings in svi)
384 {
385 array[arrayIndex++] = new VersionStringTable(this, strings);
386 }
387 }
388 }
389
390 /// <summary>
391 /// Gets an enumerator that can iterate over the version string tables in the collection.
392 /// </summary>
393 /// <returns>An enumerator that returns <see cref="VersionStringTable"/> objects.</returns>
394 public IEnumerator<VersionStringTable> GetEnumerator()
395 {
396 VersionInfo svi = this.rawVersionInfo["StringFileInfo"];
397 if (svi != null)
398 {
399 foreach (VersionInfo strings in svi)
400 {
401 yield return new VersionStringTable(this, strings);
402 }
403 }
404 }
405
406 /// <summary>
407 /// Gets an enumerator that can iterate over the version string tables in the collection.
408 /// </summary>
409 /// <returns>An enumerator that returns <see cref="VersionStringTable"/> objects.</returns>
410 IEnumerator IEnumerable.GetEnumerator()
411 {
412 return this.GetEnumerator();
413 }
414 }
415}
diff --git a/src/dtf/WixToolset.Dtf.Resources/VersionStringTable.cs b/src/dtf/WixToolset.Dtf.Resources/VersionStringTable.cs
new file mode 100644
index 00000000..6aad68c6
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/VersionStringTable.cs
@@ -0,0 +1,231 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Collections.Generic;
11 using System.Globalization;
12 using System.Diagnostics.CodeAnalysis;
13
14 /// <summary>
15 /// Represents a string table of a file version resource.
16 /// </summary>
17 [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
18 public sealed class VersionStringTable : IDictionary<string, string>
19 {
20 private VersionResource parent;
21 private VersionInfo rawStringVersionInfo;
22
23 internal VersionStringTable(VersionResource parent, VersionInfo rawStringVersionInfo)
24 {
25 this.parent = parent;
26 this.rawStringVersionInfo = rawStringVersionInfo;
27 }
28
29 /// <summary>
30 /// Gets the locale (LCID) of the string table.
31 /// </summary>
32 public int Locale
33 {
34 get
35 {
36 return UInt16.Parse(rawStringVersionInfo.Key.Substring(0, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
37 }
38 set
39 {
40 rawStringVersionInfo.Key = ((ushort) value).ToString("x4", CultureInfo.InvariantCulture) + rawStringVersionInfo.Key.Substring(4, 4);
41 this.parent.dirty = true;
42 }
43 }
44
45 /// <summary>
46 /// Gets or sets a string value.
47 /// </summary>
48 /// <param name="key">Name of the string.</param>
49 public string this[string key]
50 {
51 get
52 {
53 VersionInfo verValue = this.rawStringVersionInfo[key];
54 if (verValue == null)
55 {
56 return null;
57 }
58 else
59 {
60 return Encoding.Unicode.GetString(verValue.Data, 0, verValue.Data.Length - 2);
61 }
62 }
63 set
64 {
65 if (value == null)
66 {
67 rawStringVersionInfo.Remove(key);
68 }
69 else
70 {
71 VersionInfo verValue = rawStringVersionInfo[key];
72 if (verValue == null)
73 {
74 verValue = new VersionInfo(key);
75 verValue.IsString = true;
76 rawStringVersionInfo.Add(verValue);
77 }
78 verValue.Data = new byte[Encoding.Unicode.GetByteCount(value) + 2];
79 Encoding.Unicode.GetBytes(value, 0, value.Length, verValue.Data, 0);
80 }
81 this.parent.dirty = true;
82 }
83 }
84
85 bool ICollection<KeyValuePair<string, string>>.IsReadOnly
86 {
87 get
88 {
89 return false;
90 }
91 }
92
93 bool IDictionary<string, string>.TryGetValue(string key, out string value)
94 {
95 value = this[key];
96 return value != null;
97 }
98
99
100 void ICollection<KeyValuePair<string, string>>.Add(KeyValuePair<string, string> item)
101 {
102 this[item.Key] = item.Value;
103 }
104
105 bool ICollection<KeyValuePair<string, string>>.Remove(KeyValuePair<string, string> item)
106 {
107 string value = this[item.Key];
108 if (value == item.Value)
109 {
110 this[item.Key] = null;
111 return true;
112 }
113 else
114 {
115 return false;
116 }
117 }
118
119 bool ICollection<KeyValuePair<string, string>>.Contains(KeyValuePair<string, string> item)
120 {
121 string value = this[item.Key];
122 if (value == item.Value)
123 {
124 return true;
125 }
126 else
127 {
128 return false;
129 }
130 }
131
132 bool IDictionary<string, string>.ContainsKey(string key)
133 {
134 return this[key] != null;
135 }
136
137 void IDictionary<string, string>.Add(string key, string value)
138 {
139 this[key] = value;
140 }
141
142 bool IDictionary<string, string>.Remove(string key)
143 {
144 if (this[key] != null)
145 {
146 this[key] = null;
147 return true;
148 }
149 else
150 {
151 return false;
152 }
153 }
154
155 /// <summary>
156 /// Removes all strings from the string table.
157 /// </summary>
158 public void Clear()
159 {
160 this.rawStringVersionInfo.Clear();
161 }
162
163 /// <summary>
164 /// Gets a collection of all the names of the strings in the table.
165 /// </summary>
166 public ICollection<string> Keys
167 {
168 get
169 {
170 List<string> keys = new List<string>(this.rawStringVersionInfo.Count);
171 foreach (VersionInfo verValue in this.rawStringVersionInfo)
172 {
173 keys.Add(verValue.Key);
174 }
175 return keys;
176 }
177 }
178
179 /// <summary>
180 /// Gets a collection of all the values in the table.
181 /// </summary>
182 public ICollection<string> Values
183 {
184 get
185 {
186 List<string> values = new List<string>(this.rawStringVersionInfo.Count);
187 foreach (VersionInfo verValue in this.rawStringVersionInfo)
188 {
189 values.Add(Encoding.Unicode.GetString(verValue.Data, 0, verValue.Data.Length - 2));
190 }
191 return values;
192 }
193 }
194
195 /// <summary>
196 /// Gets the number of strings in the table.
197 /// </summary>
198 public int Count
199 {
200 get
201 {
202 return this.rawStringVersionInfo.Count;
203 }
204 }
205
206 void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int index)
207 {
208 foreach (VersionInfo verValue in this.rawStringVersionInfo)
209 {
210 array[index++] = new KeyValuePair<string, string>(verValue.Key, Encoding.Unicode.GetString(verValue.Data, 0, verValue.Data.Length - 2));
211 }
212 }
213
214 /// <summary>
215 /// Gets an enumeration over all strings in the table.
216 /// </summary>
217 /// <returns>Enumeration of string name and value pairs</returns>
218 public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
219 {
220 foreach (VersionInfo verValue in this.rawStringVersionInfo)
221 {
222 yield return new KeyValuePair<string, string>(verValue.Key, Encoding.Unicode.GetString(verValue.Data, 0, verValue.Data.Length - 2));
223 }
224 }
225
226 IEnumerator IEnumerable.GetEnumerator()
227 {
228 return this.GetEnumerator();
229 }
230 }
231}
diff --git a/src/dtf/WixToolset.Dtf.Resources/WixToolset.Dtf.Resources.csproj b/src/dtf/WixToolset.Dtf.Resources/WixToolset.Dtf.Resources.csproj
new file mode 100644
index 00000000..b5f5e9a2
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.Resources/WixToolset.Dtf.Resources.csproj
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="utf-8"?>
2<!-- Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. -->
3
4<Project Sdk="Microsoft.NET.Sdk">
5 <PropertyGroup>
6 <RootNamespace>WixToolset.Dtf.Resources</RootNamespace>
7 <AssemblyName>WixToolset.Dtf.Resources</AssemblyName>
8 <TargetFrameworks>netstandard2.0;net20</TargetFrameworks>
9 <Description>Classes for reading and writing resource data in executable files</Description>
10 <CreateDocumentationFile>true</CreateDocumentationFile>
11 </PropertyGroup>
12
13 <ItemGroup>
14 <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="All" />
15 <PackageReference Include="Nerdbank.GitVersioning" Version="3.3.37" PrivateAssets="All" />
16 </ItemGroup>
17</Project>