aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.Resources/Resource.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/dtf/WixToolset.Dtf.Resources/Resource.cs')
-rw-r--r--src/dtf/WixToolset.Dtf.Resources/Resource.cs225
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
3namespace WixToolset.Dtf.Resources
4{
5 using System;
6 using System.IO;
7 using System.Text;
8 using System.Reflection;
9 using System.Collections;
10 using System.Globalization;
11 using System.Runtime.InteropServices;
12 using System.Diagnostics.CodeAnalysis;
13
14 /// <summary>
15 /// Represents a Win32 resource which can be loaded from and saved to a PE file.
16 /// </summary>
17 public class Resource
18 {
19 private ResourceType type;
20 private string name;
21 private int locale;
22 private byte[] data;
23
24 /// <summary>
25 /// Creates a new Resource object without any data. The data can be later loaded from a file.
26 /// </summary>
27 /// <param name="type">Type of the resource; may be one of the ResourceType constants or a user-defined type.</param>
28 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
29 /// <param name="locale">Locale of the resource</param>
30 public Resource(ResourceType type, string name, int locale)
31 : this(type, name, locale, null)
32 {
33 }
34
35 /// <summary>
36 /// Creates a new Resource object with data. The data can be later saved to a file.
37 /// </summary>
38 /// <param name="type">Type of the resource; may be one of the ResourceType constants or a user-defined type.</param>
39 /// <param name="name">Name of the resource. For a numeric resource identifier, prefix the decimal number with a "#".</param>
40 /// <param name="locale">Locale of the resource</param>
41 /// <param name="data">Raw resource data</param>
42 public Resource(ResourceType type, string name, int locale, byte[] data)
43 {
44 if (name == null)
45 {
46 throw new ArgumentNullException("name");
47 }
48
49 this.type = type;
50 this.name = name;
51 this.locale = locale;
52 this.data = data;
53 }
54
55 /// <summary>
56 /// Gets or sets the type of the resource. This may be one of the ResourceType constants
57 /// or a user-defined type name.
58 /// </summary>
59 public ResourceType ResourceType
60 {
61 get { return this.type; }
62 set { this.type = value; }
63 }
64
65 /// <summary>
66 /// Gets or sets the name of the resource. For a numeric resource identifier, the decimal number is prefixed with a "#".
67 /// </summary>
68 public string Name
69 {
70 get
71 {
72 return this.name;
73 }
74
75 set
76 {
77 if (value == null)
78 {
79 throw new ArgumentNullException("value");
80 }
81
82 this.name = value;
83 }
84 }
85
86 /// <summary>
87 /// Gets or sets the locale of the resource.
88 /// </summary>
89 public int Locale
90 {
91 get { return this.locale; }
92 set { this.locale = value; }
93 }
94
95 /// <summary>
96 /// Gets or sets the raw data of the resource.
97 /// </summary>
98 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]
99 public virtual byte[] Data
100 {
101 get { return this.data; }
102 set { this.data = value; }
103 }
104
105 /// <summary>
106 /// Loads the resource data from a file. The file is searched for a resource with matching type, name, and locale.
107 /// </summary>
108 /// <param name="file">Win32 PE file containing the resource</param>
109 public void Load(string file)
110 {
111 IntPtr module = NativeMethods.LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE);
112 try
113 {
114 this.Load(module);
115 }
116 finally
117 {
118 NativeMethods.FreeLibrary(module);
119 }
120 }
121
122 internal void Load(IntPtr module)
123 {
124 IntPtr resourceInfo = NativeMethods.FindResourceEx(module, (string) this.ResourceType, this.Name, (ushort) this.Locale);
125 if (resourceInfo != IntPtr.Zero)
126 {
127 uint resourceLength = NativeMethods.SizeofResource(module, resourceInfo);
128 IntPtr resourceData = NativeMethods.LoadResource(module, resourceInfo);
129 IntPtr resourcePtr = NativeMethods.LockResource(resourceData);
130 byte[] resourceBytes = new byte[resourceLength];
131 Marshal.Copy(resourcePtr, resourceBytes, 0, resourceBytes.Length);
132 this.Data = resourceBytes;
133 }
134 else
135 {
136 this.Data = null;
137 }
138 }
139
140 /// <summary>
141 /// Saves the resource to a file. Any existing resource data with matching type, name, and locale is overwritten.
142 /// </summary>
143 /// <param name="file">Win32 PE file to contain the resource</param>
144 public void Save(string file)
145 {
146 IntPtr updateHandle = IntPtr.Zero;
147 try
148 {
149 updateHandle = NativeMethods.BeginUpdateResource(file, false);
150 this.Save(updateHandle);
151 if (!NativeMethods.EndUpdateResource(updateHandle, false))
152 {
153 int err = Marshal.GetLastWin32Error();
154 throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to save resource. Error code: {0}", err));
155 }
156 updateHandle = IntPtr.Zero;
157 }
158 finally
159 {
160 if (updateHandle != IntPtr.Zero)
161 {
162 NativeMethods.EndUpdateResource(updateHandle, true);
163 }
164 }
165 }
166
167 internal void Save(IntPtr updateHandle)
168 {
169 IntPtr dataPtr = IntPtr.Zero;
170 try
171 {
172 int dataLength = 0;
173 if (this.Data != null)
174 {
175 dataLength = this.Data.Length;
176 dataPtr = Marshal.AllocHGlobal(dataLength);
177 Marshal.Copy(this.Data, 0, dataPtr, dataLength);
178 }
179 bool updateSuccess;
180 if (this.Name.StartsWith("#", StringComparison.Ordinal))
181 {
182 // A numeric-named resource must be saved via the integer version of UpdateResource.
183 IntPtr intName = new IntPtr(Int32.Parse(this.Name.Substring(1), CultureInfo.InvariantCulture));
184 updateSuccess = NativeMethods.UpdateResource(updateHandle, new IntPtr(this.ResourceType.IntegerValue), intName, (ushort) this.Locale, dataPtr, (uint) dataLength);
185 }
186 else
187 {
188 updateSuccess = NativeMethods.UpdateResource(updateHandle, (string) this.ResourceType, this.Name, (ushort) this.Locale, dataPtr, (uint) dataLength);
189 }
190 if (!updateSuccess)
191 {
192 throw new IOException("Failed to save resource. Error: " + Marshal.GetLastWin32Error());
193 }
194 }
195 finally
196 {
197 if (dataPtr != IntPtr.Zero)
198 {
199 Marshal.FreeHGlobal(dataPtr);
200 }
201 }
202 }
203
204 /// <summary>
205 /// Tests if type, name, and locale of this Resource object match another Resource object.
206 /// </summary>
207 /// <param name="obj">Resource object to be compared</param>
208 /// <returns>True if the objects represent the same resource; false otherwise.</returns>
209 public override bool Equals(object obj)
210 {
211 Resource res = obj as Resource;
212 if (res == null) return false;
213 return this.ResourceType == res.ResourceType && this.Name == res.Name && this.Locale == res.Locale;
214 }
215
216 /// <summary>
217 /// Gets a hash code for this Resource object.
218 /// </summary>
219 /// <returns>Hash code generated from the resource type, name, and locale.</returns>
220 public override int GetHashCode()
221 {
222 return this.ResourceType.GetHashCode() ^ this.Name.GetHashCode() ^ this.Locale.GetHashCode();
223 }
224 }
225}