// 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;
}
}
}
}