diff options
Diffstat (limited to 'src/dtf/WixToolset.Dtf.Resources')
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/AssemblyInfo.cs | 5 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/BitmapResource.cs | 57 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/FixedFileVersionInfo.cs | 183 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/GroupIconInfo.cs | 119 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/GroupIconResource.cs | 120 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/NativeMethods.cs | 55 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/Resource.cs | 225 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/ResourceCollection.cs | 340 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/ResourceType.cs | 198 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/VersionEnums.cs | 86 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/VersionInfo.cs | 270 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/VersionResource.cs | 415 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/VersionStringTable.cs | 231 | ||||
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/WixToolset.Dtf.Resources.csproj | 17 |
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 | |||
3 | using 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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 | |||
3 | namespace 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> | ||