![]() |
Platforms, Frameworks & Libraries »
Libraries »
General
Intermediate
License: The Code Project Open License (CPOL)
File Resource Management Library (.NET)By dB.A .NET implementation of a file resource management, with complete support for VS_VERSIONINFO version resources. |
C# (C# 1.0, C# 2.0, C# 3.0), Windows (Win2K, WinXP, Win2003, Vista), .NET (.NET 2.0), Win32, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||
There are several good articles about reading and writing resources from/to a compiled binary. Most focus on retrieving module version information and modifying version information, mostly in C++. Some detail the same operations for cursors, icons or dialog resources. There is, however, no single .NET library to retrieve and save any type of resources, or any library to edit version resources specifically.
This implementation is a framework that enumerates resources and implements both read and write of the file version (VS_VERSIONINFO), string resources (company, copyright and product information) and icon (RT_GROUP_ICON, RT_ICON) resources. It has been extended to icons and can be further extended to other resource types easily.
Initially, I started porting the version resource implementation from Denis Zabavchik's C++ VerInfoLib. Then, it grew bigger ...
The following example demonstrates enumerating resources by type. From the sample atl.dll in the Windows system directory, you will typically get the following resources: MUI, REGISTRY, TYPELIB, and "16", which is the RT_VERSION resource.
string filename = Path.Combine(Environment.SystemDirectory, "atl.dll");
using (ResourceInfo vi = new ResourceInfo())
{
vi.Load(filename);
foreach (string type in vi.ResourceTypes)
{
Console.WriteLine(type);
foreach (Resource resource in vi.Resources[type])
{
Console.WriteLine(" {0} ({1}) - {2} byte(s)",
resource.Name, resource.Language, resource.Size);
}
}
}
You can load file version information without enumerating resources.
string filename = Path.Combine(Environment.SystemDirectory, "atl.dll");
VersionResource versionResource = new VersionResource();
versionResource.LoadFrom(filename);
Console.WriteLine("File version: {0}", versionResource.FileVersion);
StringFileInfo stringFileInfo =
(StringFileInfo) versionResource["StringFileInfo"];
foreach (KeyValuePair<string, StringResource> stringResource
in stringFileInfo.Default.Strings)
{
Console.WriteLine("{0} = {1}", stringResource.Value.Key,
stringResource.Value.StringValue);
}
You can also write updated version information. Note that string resources should end with an extra null terminator.
string filename = Path.Combine(Environment.SystemDirectory, "atl.dll");
VersionResource versionResource = new VersionResource();
versionResource.LoadFrom(filename);
Console.WriteLine("File version: {0}", versionResource.FileVersion);
versionResource.FileVersion = "1.2.3.4";
StringFileInfo stringFileInfo = (StringFileInfo) versionResource["StringFileInfo"];
stringFileInfo["CompanyName"] = "My Company\0";
stringFileInfo["Weather"] = "Sunshine, beach weather.\0";
versionResource.SaveTo(filename);
Every resource structure has a similar header, implemented in ResourceTable.cs.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct RESOURCE_HEADER
{
public UInt16 wLength;
public UInt16 wValueLength;
public UInt16 wType;
};
The header is usually followed by a Unicode string (a key) and an array of data structures, each with a similar resource header.
All version resource structures are aligned to 32-bit boundaries (DWORD), while icon resources are aligned to a 16-bit boundary (WORD). The resource library uses both math to align pointers (ResourceUtil.Align) to align to DWORD, and struct alignment (Pack = 2) to align icon structures to WORD.
public static IntPtr Align(Int32 p)
{
return new IntPtr((p + 3) & ~3);
}
public static IntPtr Align(IntPtr p)
{
return Align(p.ToInt32());
}
[StructLayout(LayoutKind.Sequential, Pack = 2)]
public struct GRPICONDIR
{
public UInt16 wReserved; // reserved
public UInt16 wType; // type, 1 = icon, 2 = cursor
public UInt16 wImageCount; // image count
};
Because of a uniform type of header, you'll find the same pattern in reading structured data throughout the code.
Here's an example of StringTable:
public override IntPtr Read(IntPtr lpRes)
{
IntPtr pChild = base.Read(lpRes);
while (pChild.ToInt32() < (lpRes.ToInt32() + _header.wLength))
{
StringResource res = new StringResource(pChild);
_strings.Add(res.Key, res);
pChild = ResourceUtil.Align(pChild.ToInt32() + res.Header.wLength);
}
return new IntPtr(lpRes.ToInt32() + _header.wLength);
}
Each StringResource is the endpoint structure without any children.
public void Read(IntPtr lpRes)
{
_header = (Kernel32.RESOURCE_HEADER) Marshal.PtrToStructure( lpRes,
typeof(Kernel32.RESOURCE_HEADER));
IntPtr pKey = new IntPtr(lpRes.ToInt32() + Marshal.SizeOf(_header));
_key = Marshal.PtrToStringUni(pKey);
IntPtr pValue = ResourceUtil.Align(pKey.ToInt32() + (_key.Length + 1) * 2);
_value = _header.wValueLength > 0 ? Marshal.PtrToStringUni(pValue,
_header.wValueLength) : null;
}
Writing is the reverse operation of reading, but the header must be updated to the correct length. It's easier to align the structure and to calculate the difference between the end of the structure and the beginning of it after it's written.
public override void Write(BinaryWriter w)
{
long headerPos = w.BaseStream.Position;
base.Write(w);
Dictionary<string />.Enumerator stringsEnum = _strings.GetEnumerator();
while (stringsEnum.MoveNext())
{
stringsEnum.Current.Value.Write(w);
}
ResourceUtil.PadToDWORD(w);
ResourceUtil.WriteAt(w, w.BaseStream.Position - headerPos, headerPos);
}
Extending the library to support icons means implementing the data structures for icon storage and hooking up ResourceInfo callbacks. When ResourceInfo encouters a resource of type "14" (RT_GROUP_ICON), it creates an object of type GroupIconResource, much like string and version resources. The latter creates an IconResource, which loads an IconImage.
In order to embed an existing icon from a .ico file into an executable (.exe or .dll) we load the .ico file and convert it to a GroupIconResource. The structure in an .ico file is similar to the structure of the icon in an executable. The only difference is that the executable headers store the icon ID, while a .ico header contains the offset of icon data. See IconFile and IconFileIcon classes for implementation details. The GroupIconResource is written to the target file, then each icon resource is written separately. Note that the current implementation would replace icons with the same ID in the executable, but doesn't delete old icons if you're storing less icon images than the previous number — it probably should since these icons become orphaned.
The ease of extending the library to icons validated our initial design model.
The latest version of this article and source code can always be found in Subversion under svn://svn.vestris.com/codeproject/ResourceLib. You can also browse the source code.
This article combines, implements, ports, or obsoletes some of the functionalities of the following publications:
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 29 Sep 2008 Editor: Sean Ewington |
Copyright 2008 by dB. Everything else Copyright © CodeProject, 1999-2009 Web16 | Advertise on the Code Project |