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