diff options
Diffstat (limited to 'src/dtf/WixToolset.Dtf.Resources/Resource.cs')
-rw-r--r-- | src/dtf/WixToolset.Dtf.Resources/Resource.cs | 225 |
1 files changed, 225 insertions, 0 deletions
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 | } | ||