// 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. namespace WixToolset.Dtf.Resources { using System; using System.IO; using System.Text; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; using System.Diagnostics.CodeAnalysis; /// /// Allows reading and editing of resource data in a Win32 PE file. /// /// /// To use this class: /// Create a new ResourceCollection /// Locate resources for the collection by calling one of the methods /// Load data of one or more s from a file by calling the method of the /// Resource class, or load them all at once (more efficient) with the method of the ResourceCollection. /// Read and/or edit data of the individual Resource objects using the methods on that class. /// Save data of one or more s to a file by calling the method of the /// Resource class, or save them all at once (more efficient) with the method of the ResourceCollection. /// /// public class ResourceCollection : ICollection { private List resources; /// /// Creates a new, empty ResourceCollection. /// public ResourceCollection() { this.resources = new List(); } /// /// Locates all resources in a file, including all resource types and languages. For each located resource, /// a instance (or subclass) is added to the collection. /// /// The file to be searched for resources. /// resources could not be read from the file public void Find(string resFile) { this.Clear(); IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); if (module == IntPtr.Zero) { int err = Marshal.GetLastWin32Error(); throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to load resource file. Error code: {0}", err)); } try { if (!NativeMethods.EnumResourceTypes(module, new NativeMethods.EnumResTypesProc(this.EnumResTypes), IntPtr.Zero)) { int err = Marshal.GetLastWin32Error(); throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to enumerate resources. Error code: {0}", err)); } } finally { NativeMethods.FreeLibrary(module); } } /// /// Locates all resources in a file of a given type, including all languages. For each located resource, /// a instance (or subclass) is added to the collection. /// /// The file to be searched for resources. /// The type of resource to search for; may be one of the ResourceType constants or a user-defined type. /// resources could not be read from the file public void Find(string resFile, ResourceType type) { this.Clear(); IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); try { if (!NativeMethods.EnumResourceNames(module, (string) type, new NativeMethods.EnumResNamesProc(this.EnumResNames), IntPtr.Zero)) { int err = Marshal.GetLastWin32Error(); throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error. Error code: {0}", err)); } } finally { NativeMethods.FreeLibrary(module); } } /// /// Locates all resources in a file of a given type and language. For each located resource, /// a instance (or subclass) is added to the collection. /// /// The file to be searched for resources. /// The type of resource to search for; may be one of the ResourceType constants or a user-defined type. /// The name of the resource to search for. /// resources could not be read from the file public void Find(string resFile, ResourceType type, string name) { this.Clear(); IntPtr module = NativeMethods.LoadLibraryEx(resFile, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); try { if (!NativeMethods.EnumResourceLanguages(module, (string) type, name, new NativeMethods.EnumResLangsProc(this.EnumResLangs), IntPtr.Zero)) { int err = Marshal.GetLastWin32Error(); throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err)); } } finally { NativeMethods.FreeLibrary(module); } } private bool EnumResTypes(IntPtr module, IntPtr type, IntPtr param) { if (!NativeMethods.EnumResourceNames(module, type, new NativeMethods.EnumResNamesProc(EnumResNames), IntPtr.Zero)) { int err = Marshal.GetLastWin32Error(); throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceNames error! Error code: {0}", err)); } return true; } private bool EnumResNames(IntPtr module, IntPtr type, IntPtr name, IntPtr param) { if (!NativeMethods.EnumResourceLanguages(module, type, name, new NativeMethods.EnumResLangsProc(EnumResLangs), IntPtr.Zero)) { int err = Marshal.GetLastWin32Error(); throw new IOException(String.Format(CultureInfo.InvariantCulture, "EnumResourceLanguages error. Error code: {0}", err)); } return true; } private bool EnumResLangs(IntPtr module, IntPtr type, IntPtr name, ushort langId, IntPtr param) { Resource res; if (((int) type) == ResourceType.Version.IntegerValue) { res = new VersionResource(ResourceNameToString(name), langId); } else { res = new Resource(ResourceNameToString(type), ResourceNameToString(name), langId); } if (!this.Contains(res)) { this.Add(res); } return true; } private static string ResourceNameToString(IntPtr resName) { if ((resName.ToInt64() >> 16) == 0) { return "#" + resName.ToString(); } else { return Marshal.PtrToStringAuto(resName); } } /// /// For all resources in the collection, loads their data from a resource file. /// /// The file from which resources are loaded. public void Load(string file) { IntPtr module = NativeMethods.LoadLibraryEx(file, IntPtr.Zero, NativeMethods.LOAD_LIBRARY_AS_DATAFILE); try { foreach (Resource res in this) { res.Load(module); } } finally { NativeMethods.FreeLibrary(module); } } /// /// For all resources in the collection, saves their data to a resource file. /// /// The file to which resources are saved. public void Save(string file) { IntPtr updateHandle = IntPtr.Zero; try { updateHandle = NativeMethods.BeginUpdateResource(file, false); foreach (Resource res in this) { res.Save(updateHandle); } if (!NativeMethods.EndUpdateResource(updateHandle, false)) { int err = Marshal.GetLastWin32Error(); throw new IOException(String.Format(CultureInfo.InvariantCulture, "Failed to save resource. Error {0}", err)); } updateHandle = IntPtr.Zero; } finally { if (updateHandle != IntPtr.Zero) { NativeMethods.EndUpdateResource(updateHandle, true); } } } /// /// Gets or sets the element at the specified index. /// public Resource this[int index] { get { return (Resource) this.resources[index]; } set { this.resources[index] = value; } } /// /// Adds a new item to the collection. /// /// The Resource to add. public void Add(Resource item) { this.resources.Add(item); } /// /// Removes an item to the collection. /// /// The Resource to remove. public bool Remove(Resource item) { return this.resources.Remove(item); } /// /// Gets the index of an item in the collection. /// /// The Resource to search for. /// The index of the item, or -1 if not found. public int IndexOf(Resource item) { return this.resources.IndexOf(item); } /// /// Inserts a item into the collection. /// /// The insertion index. /// The Resource to insert. public void Insert(int index, Resource item) { this.resources.Insert(index, item); } /// /// Tests if the collection contains an item. /// /// The Resource to search for. /// true if the item is found; false otherwise public bool Contains(Resource item) { return this.resources.Contains(item); } /// /// Copies the collection into an array. /// /// The array to copy into. /// The starting index in the destination array. public void CopyTo(Resource[] array, int arrayIndex) { this.resources.CopyTo(array, arrayIndex); } /// /// Removes all resources from the collection. /// public void Clear() { this.resources.Clear(); } /// /// Gets the number of resources in the collection. /// public int Count { get { return this.resources.Count; } } /// /// Gets an enumerator over all resources in the collection. /// /// public IEnumerator GetEnumerator() { return this.resources.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable) this.resources).GetEnumerator(); } bool ICollection.IsReadOnly { get { return false; } } } }