834 lines
37 KiB
HTML
834 lines
37 KiB
HTML
<!--------------------------------------------------------------------------->
|
|
<!-- IGNORE THIS SECTION -->
|
|
<html>
|
|
<head>
|
|
<title>File Resource Management Library (.NET)</title>
|
|
<style>
|
|
BODY, P, TD
|
|
{
|
|
font-family: Verdana, Arial, Helvetica, sans-serif;
|
|
font-size: 10pt;
|
|
}
|
|
H2, H3, H4, H5
|
|
{
|
|
color: #ff9900;
|
|
font-weight: bold;
|
|
}
|
|
H2
|
|
{
|
|
font-size: 13pt;
|
|
}
|
|
H3
|
|
{
|
|
font-size: 12pt;
|
|
}
|
|
H4
|
|
{
|
|
font-size: 10pt;
|
|
color: black;
|
|
}
|
|
PRE
|
|
{
|
|
background-color: #FBEDBB;
|
|
font-family: "Courier New" , Courier, mono;
|
|
white-space: pre;
|
|
}
|
|
CODE
|
|
{
|
|
color: #990000;
|
|
font-family: "Courier New" , Courier, mono;
|
|
}
|
|
</style>
|
|
<link rel="stylesheet" type="text/css" href="http://www.codeproject.com/styles/global.css">
|
|
</head>
|
|
<body bgcolor="#FFFFFF" color="#000000">
|
|
<h2>
|
|
Introduction</h2>
|
|
<p>
|
|
There are several good articles about reading and writing resources from and back to a Windows PE executable or DLL.
|
|
Most publications focus on retrieving module version information and modifying version information, mostly in C++.
|
|
Some detail the same operations for cursors, icons or dialog resources. Others have limitations and can only edit
|
|
structures in-place. There's, however, no single managed .NET library to retrieve and save any type of resources,
|
|
no library to edit or generate version resources and no library that has a consistent programming model for all
|
|
resource types.</p>
|
|
<p>
|
|
This implementation is a framework that enumerates resources and implements both reading and writing of the
|
|
file version (<code>VS_VERSIONINFO</code>), string (company, copyright and product information),
|
|
bitmap (<code>RT_BITMAP</code>), icon (<code>RT_GROUP_ICON</code>, <code>RT_ICON</code>), dialog
|
|
(<code>RT_DIALOG</code>), menu (<code>RT_MENU</code>), cursor (<code>RT_GROUP_CURSOR</code>,
|
|
<code>RT_CURSOR</code>), accelerator (<code>RT_ACCELERATOR</code>) and SxS manifest (<code>RT_MANIFEST</code>)
|
|
resources. Overtime this library was extended through these resource types and can be easily completed for all
|
|
the remaining ones.
|
|
</p>
|
|
<h2>
|
|
Background</h2>
|
|
<p>
|
|
Initially, I started porting the version resource implementation from <a href="VerInfoLib.aspx">
|
|
Denis Zabavchik's C++ VerInfoLib</a> for the <a href="http://dotnetinstaller.codeplex.com">dotNetInstaller Bootstrapper</a>
|
|
open-source project. Then, it grew bigger ...</p>
|
|
<h2>
|
|
Using the Code</h2>
|
|
<h3>
|
|
Enumerating Resources</h3>
|
|
<p>
|
|
The following example demonstrates enumerating resources by type.
|
|
</p>
|
|
<pre lang="cs">
|
|
string filename = Path.Combine(Environment.SystemDirectory, "atl.dll");
|
|
using (ResourceInfo vi = new ResourceInfo())
|
|
{
|
|
vi.Load(filename);
|
|
foreach (ResourceId type in vi.ResourceTypes)
|
|
{
|
|
foreach (Resource resource in vi.Resources[type])
|
|
{
|
|
Console.WriteLine("{0} - {1} ({2}) - {3} byte(s)",
|
|
resource.TypeName, resource.Name, resource.Language, resource.Size);
|
|
}
|
|
}
|
|
}</pre>
|
|
<p>
|
|
From the Windows Vista <em>atl.dll</em>, you will typically get the following resources.
|
|
</p>
|
|
<pre>
|
|
MUI - 1 (1033) - 232 byte(s)
|
|
REGISTRY - 101 (1033) - 335 byte(s)
|
|
TYPELIB - 1 (1033) - 7132 byte(s)
|
|
RT_STRING - 1 (1033) - 72 byte(s)
|
|
RT_STRING - 7 (1033) - 38 byte(s)
|
|
RT_VERSION - 1 (1033) - 828 byte(s)</pre>
|
|
<h3>
|
|
Reading Version Information</h3>
|
|
<p>
|
|
You can load file version information without enumerating resources.</p>
|
|
<pre lang="cs">
|
|
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<ResourceId, StringTableEntry> versionStringTableEntry in stringFileInfo.Default.Strings)
|
|
{
|
|
Console.WriteLine("{0} = {1}", versionStringTableEntry.Value.Key, versionStringTableEntry.Value.StringValue);
|
|
}</pre>
|
|
<h3>
|
|
Writing Version Information</h3>
|
|
<p>
|
|
You can write updated version information back into the executable. The easiest way is to load an existing
|
|
binary resource, update it and save it back. Note that internally string resources are stored with an extra
|
|
null terminator. The library is consistent with this and always stores the value with two null
|
|
terminators while doing the dirty work for you and appending it only when required.</p>
|
|
<pre lang="cs">
|
|
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.";
|
|
versionResource.SaveTo(filename);
|
|
</pre>
|
|
<p>
|
|
Generating a complete version resource header allows you to save version information
|
|
into a file that doesn't have any. This is more involved because you must generate
|
|
all the structures. ResourceLib makes it easy since you don't have to worry about
|
|
structure sizes or data alignment.</p>
|
|
<pre lang="cs">VersionResource versionResource = new VersionResource();
|
|
versionResource.FileVersion = "1.2.3.4";
|
|
versionResource.ProductVersion = "4.5.6.7";
|
|
|
|
StringFileInfo stringFileInfo = new StringFileInfo();
|
|
versionResource[stringFileInfo.Key] = stringFileInfo;
|
|
StringTable stringFileInfoStrings = new StringTable();
|
|
stringFileInfoStrings.LanguageID = 1033;
|
|
stringFileInfoStrings.CodePage = 1200;
|
|
stringFileInfo.Strings.Add(stringFileInfoStrings.Key, stringFileInfoStrings);
|
|
stringFileInfoStrings["ProductName"] = "ResourceLib";
|
|
stringFileInfoStrings["FileDescription"] = "File updated by ResourceLib";
|
|
stringFileInfoStrings["CompanyName"] = "Vestris Inc.";
|
|
stringFileInfoStrings["LegalCopyright"] = "All Rights Reserved";
|
|
stringFileInfoStrings["Comments"] = "This file has a version resource updated by ResourceLib";
|
|
stringFileInfoStrings["ProductVersion"] = versionResource.ProductVersion;
|
|
|
|
VarFileInfo varFileInfo = new VarFileInfo();
|
|
versionResource[varFileInfo.Key] = varFileInfo;
|
|
VarTable varFileInfoTranslation = new VarTable("Translation");
|
|
varFileInfo.Vars.Add(varFileInfoTranslation.Key, varFileInfoTranslation);
|
|
varFileInfoTranslation[ResourceUtil.USENGLISHLANGID] = 1300;
|
|
|
|
versionResource.SaveTo(targetFilename);</pre>
|
|
<p>
|
|
A unit test, called <code>VersionResourceTests.TestDeleteAndSaveVersionResource</code>
|
|
implements this behavior and can be found in the ResourceLibUnitTests project. It
|
|
makes a copy of <em>atl.dll</em> from the Windows system directory into the temporary folder,
|
|
deletes its version resource, generates a new one without copying any data and updates
|
|
the copy.</p>
|
|
<h3>
|
|
Other Resource Types</h3>
|
|
<p>
|
|
You can load and save other resource types in a similar manner. The class that gives you access to all
|
|
resources is <code>ResourceInfo</code>, while each resource class (eg. <code>MenuResource</code>) can
|
|
be used directly to <code>LoadFrom</code> an executable or <code>SaveTo</code> the same or another
|
|
executable.
|
|
</p>
|
|
<p>
|
|
For example, the following code loads an English <code>MenuResource</code> with ID 204 from
|
|
<em>explorer.exe</em>.
|
|
</p>
|
|
<pre>
|
|
MenuResource menuResource = new MenuResource();
|
|
menuResource.Name = new ResourceId(204);
|
|
menuResource.Language = ResourceUtil.USENGLISHLANGID;
|
|
menuResource.LoadFrom("explorer.exe"); </pre>
|
|
<h2>
|
|
Implementation</h2>
|
|
<h3>
|
|
Resource Identity and Type</h3>
|
|
<p>
|
|
Every resource has a resource identity and a resource type, implemented in <em>ResourceId.cs</em>.
|
|
A resource identity can either be a positive integer or a string. The Windows API requires a raw pointer
|
|
for each of these parameters. If the value is between 1-65535, it is an integer identity. Otherwise it's
|
|
a pointer to a string. This adds a pointless complication into managed code - the correct PInvoke parameters
|
|
for Win32 functions that manipulate resources are <code>IntPtr</code> and neither <code>String</code> nor
|
|
<code>int</code>.
|
|
</p>
|
|
<pre lang="cs">
|
|
[DllImport("kernel32.dll", EntryPoint = "FindResourceExW", CharSet = CharSet.Unicode, SetLastError = true)]
|
|
internal static extern IntPtr FindResourceEx(IntPtr hModule, IntPtr type, IntPtr name, UInt16 language);
|
|
</pre>
|
|
<p>
|
|
In order to make an <code>IntPtr</code> from an <code>int</code>, we use <code>new IntPtr(int)</code> and in
|
|
order to make an <code>IntPtr</code> from a <code>string</code>, we use <code>Marshal.PtrToStringUni(string)</code>.
|
|
</p>
|
|
<h3>
|
|
Header Structures</h3>
|
|
<p>
|
|
Every resource structure has a similar header, implemented in <em>ResourceTable.cs</em>.
|
|
</p>
|
|
<pre lang="cs">[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
|
|
public struct RESOURCE_HEADER
|
|
{
|
|
public UInt16 wLength;
|
|
public UInt16 wValueLength;
|
|
public UInt16 wType;
|
|
} </pre>
|
|
<p>
|
|
The header is usually followed by a Unicode string (a key) and an array of data
|
|
structures, each with a similar resource header.
|
|
</p>
|
|
<h3>
|
|
Alignment
|
|
</h3>
|
|
<p>
|
|
Most resource structures have evolved from 16-bit Windows and are <a href="http://msdn.microsoft.com/en-us/library/aa290049.aspx">aligned</a>
|
|
to 16-bit <code>WORD</code> boundaries. Newer structures in 32-bit Windows are aligned to 32-bit
|
|
<code>DWORD</code> boundaries. For example, all version resource structures are aligned to <code>DWORD</code>,
|
|
while icon resources are aligned to <code>WORD</code>. Other resource types may have specific alignment
|
|
requirements for various fields. The resource library uses both math to align pointers (
|
|
<code>ResourceUtil.Align</code>) to align pointers to <code>DWORD</code>, and struct alignment
|
|
(<code>Pack = 2</code>) to align structures to <code>WORD</code>. The latter is preferred where possible because
|
|
it's automatic.
|
|
</p>
|
|
<pre lang="cs">public static IntPtr Align(Int32 p)
|
|
{
|
|
return new IntPtr((p + 3) & ~3);
|
|
}
|
|
|
|
public static IntPtr Align(IntPtr p)
|
|
{
|
|
return Align(p.ToInt32());
|
|
}
|
|
</pre>
|
|
<pre lang="cs">[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
|
|
}
|
|
</pre>
|
|
<h3>
|
|
Reading</h3>
|
|
<p>
|
|
Because of a uniform type of header, you'll find the same pattern in reading structured
|
|
data throughout the code.</p>
|
|
<p>
|
|
Here's an example of <code>StringTable</code>:</p>
|
|
<pre lang="cs">
|
|
public override IntPtr Read(IntPtr lpRes)
|
|
{
|
|
IntPtr pChild = base.Read(lpRes);
|
|
while (pChild.ToInt32() < (lpRes.ToInt32() + _header.wLength))
|
|
{
|
|
StringTableEntry res = new StringTableEntry(pChild);
|
|
_strings.Add(res.Key, res);
|
|
pChild = ResourceUtil.Align(pChild.ToInt32() + res.Header.wLength);
|
|
}
|
|
return new IntPtr(lpRes.ToInt32() + _header.wLength);
|
|
}</pre>
|
|
<p>
|
|
Each <code>StringTableEntry</code> is the endpoint structure without any children.</p>
|
|
<pre lang="cs">
|
|
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;
|
|
}</pre>
|
|
<h3>
|
|
Writing</h3>
|
|
<p>
|
|
Writing is the reverse operation of reading, but the header must be updated to the correct length. It is 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. The sizes don't include any padding.
|
|
</p>
|
|
<pre lang="cs">
|
|
public override void Write(BinaryWriter w)
|
|
{
|
|
long headerPos = w.BaseStream.Position;
|
|
base.Write(w);
|
|
Dictionary<string, StringTable>.Enumerator stringsEnum = _strings.GetEnumerator();
|
|
while (stringsEnum.MoveNext())
|
|
{
|
|
stringsEnum.Current.Value.Write(w);
|
|
}
|
|
ResourceUtil.WriteAt(w, w.BaseStream.Position - headerPos, headerPos);
|
|
ResourceUtil.PadToDWORD(w);
|
|
}</pre>
|
|
<h3>
|
|
Binary Compatibility</h3>
|
|
<p>
|
|
It is very important to ensure that the library is capable of generating correct binary resources with proper sizes
|
|
and correctly aligned structures. Each top-level resource is capable of reading from raw data and writing back the
|
|
raw data. All lengths and sizes are recalculated at write time or preserved between loading and saving. An interesting
|
|
complication exists in side-by-side manifest resources: rewriting an identical manifest does not necessarily
|
|
have to preserve the size of the <code>RT_MANIFEST</code> resource because new lines in attribute values are
|
|
normalized per <a href="http://www.w3.org/TR/REC-xml/#AVNormalize" target="_blank">W3C spec section 3.3.3</a>.
|
|
</p>
|
|
<p>
|
|
A series of unit tests ensure that data read is identical to the data written.
|
|
The easy start is the <code>ResourceTests.TestReadWriteResourceBytes</code> unit test which ensures that data
|
|
read is identical to data written.
|
|
</p>
|
|
<pre>
|
|
[Test]
|
|
public void TestReadWriteResourceBytes()
|
|
{
|
|
Uri uri = new Uri(Assembly.GetExecutingAssembly().CodeBase);
|
|
string uriPath = Path.GetDirectoryName(HttpUtility.UrlDecode(uri.AbsolutePath));
|
|
foreach (string filename in Directory.GetFiles(Path.Combine(uriPath, "Binaries")))
|
|
{
|
|
Console.WriteLine(filename);
|
|
using (ResourceInfo ri = new ResourceInfo())
|
|
{
|
|
ri.Load(filename);
|
|
foreach (Resource rc in ri)
|
|
{
|
|
Console.WriteLine("Resource: {0} - {1}", rc.TypeName, rc.Name);
|
|
GenericResource genericResource = new GenericResource(rc.Type, rc.Name, rc.Language);
|
|
genericResource.LoadFrom(filename);
|
|
byte[] data = rc.WriteAndGetBytes();
|
|
ByteUtils.CompareBytes(genericResource.Data, data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</pre>
|
|
<p>
|
|
A more extensive test is needed to ensure that data is correct even if resource
|
|
contents change: this is accomplished by loading resources from an existing file, performing a deep copy of the
|
|
data without any structural fields (lengths, number of elements, etc.), writing the copy to a vector of bytes and
|
|
comparing the two. A good example of such unit test is <code>VersionResourceTests.TestDeepCopyBytes</code>.
|
|
</p>
|
|
<h2>Extending to Other Types</h2>
|
|
<p>
|
|
Extending the library to other types means implementing a new class that derives from <code>Resource</code> and
|
|
implements both <code>Read</code> and <code>Write</code> functions. For example, for <code>RT_VERSION</code> we'll
|
|
start with the following skeleton.
|
|
</p>
|
|
<pre lang="cs">
|
|
public class VersionResource : Resource
|
|
{
|
|
public VersionResource()
|
|
: base(IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
new ResourceId(Kernel32.ResourceTypes.RC_VERSION),
|
|
null,
|
|
ResourceUtil.NEUTRALLANGID,
|
|
0)
|
|
{
|
|
|
|
}
|
|
|
|
public VersionResource(IntPtr hModule, IntPtr hResource, ResourceId type, ResourceId name, UInt16 language, int size)
|
|
: base(hModule, hResource, type, name, language, size)
|
|
{
|
|
|
|
}
|
|
|
|
internal override IntPtr Read(IntPtr hModule, IntPtr lpRes)
|
|
{
|
|
return lpRes;
|
|
}
|
|
|
|
internal override void Write(System.IO.BinaryWriter w)
|
|
{
|
|
}
|
|
}</pre>
|
|
<p>
|
|
The new resource type must also be added to <code>ResourceInfo.CreateResource</code> in order to create a specialized
|
|
instance when such a resource is encountered.
|
|
</p>
|
|
<pre lang="cs">
|
|
switch (type.ResourceType)
|
|
{
|
|
case Kernel32.ResourceTypes.RT_VERSION:
|
|
return new VersionResource(hModule, hResourceGlobal, type, name, wIDLanguage, size);
|
|
}</pre>
|
|
<h2>Version</h2>
|
|
<p>
|
|
Version resources are amongst the most complex resource structures.
|
|
Their evolution is well <a href="http://blogs.msdn.com/oldnewthing/archive/2006/12/21/1340571.aspx">described
|
|
in Raymond Chen's "The Old New Thing"</a>. The top of the version resource is a <code>VS_VERSION_INFO</code>
|
|
resource table header followed by a <code>VS_FIXEDFILEINFO</code> structure that depicts the static portion
|
|
of the version resource which contains binary file version information that you see in file properties in
|
|
Windows Explorer. The default Windows fixed file information for a dynamic link library looks like this.
|
|
</p>
|
|
<pre lang="cs">
|
|
public static VS_FIXEDFILEINFO GetWindowsDefault()
|
|
{
|
|
VS_FIXEDFILEINFO fixedFileInfo = new VS_FIXEDFILEINFO();
|
|
fixedFileInfo.dwSignature = Winver.VS_FFI_SIGNATURE;
|
|
fixedFileInfo.dwStrucVersion = Winver.VS_FFI_STRUCVERSION;
|
|
fixedFileInfo.dwFileFlagsMask = Winver.VS_FFI_FILEFLAGSMASK;
|
|
fixedFileInfo.dwFileOS = (uint) Winver.FileOs.VOS__WINDOWS32;
|
|
fixedFileInfo.dwFileSubtype = (uint) Winver.FileSubType.VFT2_UNKNOWN;
|
|
fixedFileInfo.dwFileType = (uint) Winver.FileType.VFT_DLL;
|
|
return fixedFileInfo;
|
|
}</pre>
|
|
<p>
|
|
This is followed by two resource tables, <code>StringFileInfo</code> and <code>VarFileInfo</code>.
|
|
These contain the version information that can be displayed for a particular language and code page
|
|
and information not dependent on a particular language and code page combination respectively. This
|
|
is also what you see in the file properties in Windows Explorer, but the information may be different
|
|
depending on the language and region of the operating system and the user logged-in.</p>
|
|
<h2>Icons</h2>
|
|
<p>
|
|
With above infrastructure in-place and with support for the most complicated of all resources,
|
|
version resource structures, it is possible to extend the library to one
|
|
of the two dozen other known resource types. We've started with icons.
|
|
</p>
|
|
<p>
|
|
Extending the library to support icons means implementing the data structures for
|
|
icon storage and hooking up ResourceInfo callbacks. When <code>ResourceInfo</code>
|
|
encouters a resource of type <code>14</code> (<code>RT_GROUP_ICON</code>), it
|
|
creates an object of type <code>IconDirectoryResource</code>. The latter creates an
|
|
<code>IconResource</code>, which loads an <code>DeviceIndependentBitmap</code>.</p>
|
|
<ul>
|
|
<li>A <code>IconDirectoryResource</code> represents <code>RT_GROUP_ICON</code>, a
|
|
collection of icon resources.</li>
|
|
<li>An <code>IconResource</code> represents a single <code>RT_ICON</code> icon with
|
|
one or more images.</li>
|
|
<li>An <code>DeviceIndependentBitmap</code> is not a resource, but raw data embedded in the
|
|
file at an offset defined by the icon resource and represents a single icon bitmap
|
|
in a .bmp format.</li>
|
|
</ul>
|
|
<p>
|
|
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 <code>IconDirectoryResource</code>.
|
|
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
|
|
<code>IconFile</code> and <code>IconFileIcon</code> classes for implementation details.
|
|
The <code>IconDirectoryResource</code> 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.</p>
|
|
<p>
|
|
The ease of extending the library to icons validated our initial design model.
|
|
</p>
|
|
<h2>
|
|
Cursors</h2>
|
|
<p>
|
|
The next natural extension after icons is cursors. Cursor structure is virtually identical
|
|
to icons with a few notable differences.
|
|
</p>
|
|
<ul>
|
|
<li>A <code>CursorDirectoryResource</code> represents <code>RT_GROUP_CURSOR</code>, a
|
|
collection of cursor resources.</li>
|
|
<li>A <code>CursorResource</code> represents a single <code>RT_CURSOR</code> cursor with
|
|
a cursor image. <code>RT_CURSOR</code> describes a single cursor image's resource data
|
|
as a two-byte x hotspot value, followed by a two-byte y hotspot value followed by a
|
|
<code>BITMAPINFOHEADER</code> structure. The hot spot of a cursor is the point to
|
|
which Windows refers in tracking the cursor's position.
|
|
</li>
|
|
<li>An <code>DeviceIndependentBitmap</code> is not a resource, but raw data embedded in the
|
|
file as a resource and represents a single cursor bitmap. This data includes the image's
|
|
XOR bitmap followed by its AND bitmap. These two bitmaps are used together to support
|
|
transparency.</li>
|
|
</ul>
|
|
<p>
|
|
The .cur files contain hot spot data in the <code>wPlanes</code> and the <code>wBitsPerPixel</code>
|
|
fields. These are copied to the top of the <code>RT_CURSOR</code> resource when transforming
|
|
an <code>DeviceIndependentBitmap</code> into a <code>CursorResource</code>.
|
|
</p>
|
|
<p>
|
|
All common functions between icons and cursors are implemented in a shared class,
|
|
<code>DirectoryResource</code>. The differences are implemented in two derived
|
|
<code>CursorDirectoryResource</code> and <code>IconDirectoryResource</code>.
|
|
</p>
|
|
<h2>
|
|
Bitmaps</h2>
|
|
<p>
|
|
Bitmap resources are device-independent bitmaps stored as-is. Most implementation complications belong
|
|
in the <code>DeviceIndependentBitmap</code> class that is capable of separating bitmap mask and color
|
|
information. While technically somewhat challenging, this is not required for resource manipulation and
|
|
is beyond the scope of this article. The only desirable improvement in the current implementation would
|
|
to be able to assign a <code>System.Drawing.Image</code> instance to <code>DeviceIndependentBitmap.Bitmap</code>.
|
|
</p>
|
|
<h2>Dialogs</h2>
|
|
<p>
|
|
There're two dialog resource formats. The original one is described in the Win32 documentation
|
|
as part of the <code>DIALOGTEMPLATE</code> structure. A single <code>DIALOGTEMPLATE</code> can contain controls in the
|
|
<code>DIALOGITEMTEMPLATE</code> 16-bit format. In Windows NT 3.51, Microsoft introduced
|
|
<code>DIALOGEXTEMPLATE</code> and <code>DIALOGEXITEMTEMPLATE</code>, a 32-bit format. This evolution
|
|
<a href="http://blogs.msdn.com/oldnewthing/archive/2004/06/23/163596.aspx">is explained in detail in Raymond
|
|
Chen's "The Old New Thing" on MSDN</a>.
|
|
</p>
|
|
<p>
|
|
Reading dialog structures involves making a decision of whether the dialog is in a standard or extended format.
|
|
Extended dialogs start with a different 0xFFFF header.
|
|
</p>
|
|
<pre lang="cs">
|
|
internal override IntPtr Read(IntPtr hModule, IntPtr lpRes)
|
|
{
|
|
switch ((uint)Marshal.ReadInt32(lpRes) >> 16)
|
|
{
|
|
case 0xFFFF:
|
|
_dlgtemplate = new DialogExTemplate();
|
|
break;
|
|
default:
|
|
_dlgtemplate = new DialogTemplate();
|
|
break;
|
|
}
|
|
|
|
// dialog structure itself
|
|
return _dlgtemplate.Read(lpRes);
|
|
}</pre>
|
|
<p>
|
|
Subsequent structures are straightforward with the additional difficulty of variable length arrays and different alignments
|
|
within the structure. For example, the typeface name is aligned on a 32-bit boundary, but only when present. The
|
|
<code>DIALOGITEMTEMPLATE</code> and the <code>DIALOGEXITEMTEMPLATE</code> controls are aligned on a 32-bit boundary.
|
|
</p>
|
|
<p>
|
|
The optional fields (eg. control class id) are generally identified in three ways: a 0x0000 value indicating that the
|
|
value is not present, 0xFFFF indicating one additional element that specifies the ordinal value of the resource and
|
|
a null-terminated unicode string otherwise. This is generically implemented in the <code>DialogTemplateUtil.ReadResourceId</code>
|
|
and <code>DialogTemplateUtil.WriteResourceId</code> pair of functions.
|
|
</p>
|
|
<pre lang="cs">
|
|
internal static IntPtr ReadResourceId(IntPtr lpRes, out ResourceId rc)
|
|
{
|
|
rc = null;
|
|
|
|
switch ((UInt16) Marshal.ReadInt16(lpRes))
|
|
{
|
|
case 0x0000: // no predefined resource
|
|
lpRes = new IntPtr(lpRes.ToInt32() + 2);
|
|
break;
|
|
case 0xFFFF: // one additional element that specifies the ordinal value of the resource
|
|
lpRes = new IntPtr(lpRes.ToInt32() + 2);
|
|
rc = new ResourceId((UInt16)Marshal.ReadInt16(lpRes));
|
|
lpRes = new IntPtr(lpRes.ToInt32() + 2);
|
|
break;
|
|
default: // null-terminated Unicode string that specifies the name of the resource
|
|
rc = new ResourceId(Marshal.PtrToStringUni(lpRes));
|
|
lpRes = new IntPtr(lpRes.ToInt32() + (rc.Name.Length + 1) * 2);
|
|
break;
|
|
}
|
|
|
|
return lpRes;
|
|
}
|
|
|
|
internal static void WriteResourceId(BinaryWriter w, ResourceId rc)
|
|
{
|
|
if (rc == null)
|
|
{
|
|
w.Write((UInt16) 0);
|
|
}
|
|
else if (rc.IsIntResource())
|
|
{
|
|
w.Write((UInt16) 0xFFFF);
|
|
w.Write((UInt16) rc.Id);
|
|
}
|
|
else
|
|
{
|
|
ResourceUtil.PadToWORD(w);
|
|
w.Write(Encoding.Unicode.GetBytes(rc.Name));
|
|
w.Write((UInt16)0);
|
|
}
|
|
}</pre>
|
|
<p>
|
|
The implementation in the library also attempts to return a standard string representation of the dialog and its controls
|
|
overriding the <code>ToString</code> methods.
|
|
</p>
|
|
<pre>
|
|
DialogResource: STRINGINPUT, RT_DIALOG
|
|
"STRINGINPUT" DIALOG 6, 18, 166, 96
|
|
STYLE WS_SYSMENU | WS_DLGFRAME | WS_BORDER | WS_CAPTION | WS_VISIBLE | WS_POPUP | DS_SETFONT | DS_SHELLFONT | DS_MODALFRAME
|
|
EXSTYLE WS_OVERLAPPED | WS_EX_LTRREADING | WS_EX_LTRREADING | WS_EX_LTRREADING
|
|
CAPTION "Set Options"
|
|
FONT 8, "MS Shell Dlg"
|
|
{
|
|
Static "Prompt goes here" 301, 9, 12, 140, 8, WS_GROUP | WS_VISIBLE | WS_CHILD
|
|
Edit "" 302, 18, 30, 101, 12, WS_TABSTOP | WS_BORDER | WS_CAPTION | WS_VISIBLE | WS_CHILD | DS_MODALFRAME| ES_AUTOHSCROLL
|
|
Button "OK" 1, 63, 55, 40, 14, WS_TABSTOP | WS_VISIBLE | WS_CHILD | DS_ABSALIGN| BS_DEFPUSHBUTTON
|
|
Button "Cancel" 2, 108, 55, 40, 14, WS_TABSTOP | WS_VISIBLE | WS_CHILD| BS_TEXT
|
|
} </pre>
|
|
<h2>String Tables</h2>
|
|
<p>
|
|
Unlike the other resource formats, where the resource identifier is the same as the value listed in the *.rc file,
|
|
string resources are packaged in <em>blocks</em>. Each string resource block has sixteen strings. To find the block
|
|
id for a given string we need some math.
|
|
</p>
|
|
<pre lang="cs">
|
|
public static UInt16 GetBlockId(int stringId)
|
|
{
|
|
return (UInt16)((stringId / 16) + 1);
|
|
}</pre>
|
|
<p>
|
|
Reading string resources is a loop over 16 strings where each string is prefixed by its length. Since there's always
|
|
16 strings in each <code>RT_STRING</code> resource block, missing strings are identified by a length of zero. Hence
|
|
there's no way to add an empty string. A string id is derived from the block Id (resource <code>Name</code>).
|
|
</p>
|
|
<pre lang="cs">
|
|
public UInt16 BlockId
|
|
{
|
|
get
|
|
{
|
|
return (UInt16) Name.Id.ToInt32();
|
|
}
|
|
set
|
|
{
|
|
Name = new ResourceId(value);
|
|
}
|
|
}
|
|
|
|
internal override IntPtr Read(IntPtr hModule, IntPtr lpRes)
|
|
{
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
UInt16 len = (UInt16)Marshal.ReadInt16(lpRes);
|
|
if (len != 0)
|
|
{
|
|
UInt16 id = (UInt16) ((BlockId - 1) * 16 + i);
|
|
IntPtr lpString = new IntPtr(lpRes.ToInt32() + 2);
|
|
string s = Marshal.PtrToStringUni(lpString, len);
|
|
_strings.Add(id, s);
|
|
}
|
|
lpRes = new IntPtr(lpRes.ToInt32() + 2 + (len * Marshal.SystemDefaultCharSize));
|
|
}
|
|
|
|
return lpRes;
|
|
}</pre>
|
|
<p>
|
|
For example, explorer.exe contains many string tables, including this one.
|
|
</p>
|
|
<pre>
|
|
STRINGTABLE
|
|
BEGIN
|
|
300 Store letters, reports, notes, and other kinds of documents.
|
|
301 Displays recently opened documents and folders.
|
|
302 Store and play music and other audio files.
|
|
303 Store pictures and other graphics files.
|
|
END</pre>
|
|
<h2>Accelerators</h2>
|
|
<p>
|
|
An accelerator is a keystroke defined by the application to give the user a quick way to perform a task.
|
|
Accelerators' storage is a straightforward list of <code>ACCEL</code> structures aligned on <code>WORD</code> boundaries.
|
|
The number of items in an <code>RT_ACCELERATOR</code> resource is the size of the resource divided by the size of each
|
|
structure. <code>ACCEL</code> contains a combination of flags that make any combination of Control, Alt and/or Shift
|
|
keys and either an ASCII character or a special key.
|
|
</p>
|
|
<p>
|
|
The list of special keys is one that tells some interesting stories of Microsoft partnerships and Windows evolution
|
|
as it includes such keys as the <em>Fujitsu/OASYS keyboard 'Left OYAYUBI' key</em> (<code>VK_OEM_FJ_TOUROKU</code>) or
|
|
<em>NEC PC-9800 keyboard '=' key</em> (<code>VK_OEM_NEC_EQUAL</code>).
|
|
</p>
|
|
<p>
|
|
Here's an example of the accelerator table with id 251 from Windows Vista explorer.exe.
|
|
</p>
|
|
<pre>
|
|
251 ACCELERATORS
|
|
BEGIN
|
|
VK_F4, 305, ALT
|
|
VK_TAB, 41008, VIRTKEY, NOINVERT
|
|
VK_TAB, 41008, VIRTKEY, NOINVERT, SHIFT
|
|
VK_TAB, 41008, VIRTKEY, NOINVERT, CONTROL
|
|
VK_TAB, 41008, VIRTKEY, NOINVERT, SHIFT, CONTROL
|
|
VK_F5, 41061, VIRTKEY, NOINVERT
|
|
VK_F6, 41008, VIRTKEY, NOINVERT
|
|
VK_RETURN, 413, VIRTKEY, NOINVERT, ALT
|
|
Z, 416, VIRTKEY, NOINVERT, CONTROL
|
|
VK_F3, 41093, VIRTKEY, NOINVERT
|
|
M, 419, VIRTKEY, NOINVERT, ALT
|
|
END</pre>
|
|
<h2>Menus</h2>
|
|
<p>
|
|
There're two kinds of menu resources: classic 16 bit menus and 32-bit extended menus. The latter was
|
|
introduced in Windows 95 and remains the menu format through Windows Vista. This evolution
|
|
<a href="http://blogs.msdn.com/oldnewthing/archive/2008/07/16/8735896.aspx">is explained in detail in Raymond
|
|
Chen's "The Old New Thing" on MSDN</a>. Similarly to dialogs, we implement a <code>MenuResource</code>
|
|
that contains a menu in either a <code>MenuTemplate</code> or <code>MenuExTemplate</code> format. Each
|
|
menu contains a <code>MenuTemplateItemCollection</code> or <code>MenuExTemplateItemCollection</code> of
|
|
menu entries. The latter can be <code>MenuTemplateItemCommand</code> and <code>MenuTemplateItemPopup</code>
|
|
for 16-bit classic menus and <code>MenuExTemplateItemCommand</code> and <code>MenuExTemplateItemPopup</code>
|
|
for 32-bit extended menus.
|
|
</p>
|
|
<p>
|
|
A <code>MenuTemplate</code> is a straightforward header and a collection of menu items. A popup (one additional
|
|
level deeper) is indentified by the <code>MF_POPUP</code> flag in the <code>mtOption</code> field in the menu
|
|
item's header, while the last item in a collection by the <code>MF_END</code> flag. Each item and each menu
|
|
string are aligned on <code>WORD</code> boundaries.
|
|
</p>
|
|
<p>
|
|
A <code>MenuExTemplate</code> is more complicated. The <code>MENUEXTEMPLATE</code> header has a strange
|
|
offset construct: the first item starts at an offset from the offset header value itself.
|
|
Therefore the implementation of reading and writing the menu may seem odd.
|
|
</p>
|
|
<pre lang="cs">
|
|
internal override IntPtr Read(IntPtr lpRes)
|
|
{
|
|
_header = (User32.MENUEXTEMPLATE) Marshal.PtrToStructure(
|
|
lpRes, typeof(User32.MENUEXTEMPLATE));
|
|
|
|
IntPtr lpMenuItem = new IntPtr(lpRes.ToInt32()
|
|
+ _header.wOffset // offset from offset field
|
|
+ 4 // offset of the offset field
|
|
);
|
|
|
|
return _menuItems.Read(lpMenuItem);
|
|
}
|
|
|
|
internal override void Write(System.IO.BinaryWriter w)
|
|
{
|
|
long head = w.BaseStream.Position;
|
|
// write header
|
|
w.Write(_header.wVersion);
|
|
w.Write(_header.wOffset);
|
|
w.Write(_header.dwHelpId);
|
|
// pad to match the offset value
|
|
ResourceUtil.Pad(w, (UInt16) (_header.wOffset - 4));
|
|
// seek to the beginning of the menu item per offset value
|
|
// this may be behind, ie. the help id structure is part of the first popup menu
|
|
w.BaseStream.Seek(head + _header.wOffset + 4, System.IO.SeekOrigin.Begin);
|
|
// write menu items
|
|
_menuItems.Write(w);
|
|
}</pre>
|
|
<p>
|
|
Each collection of items has a prefix that contains the a context help ID. The popup items and last items
|
|
in a collection are identified by a special field reserved for this purpose and not a flag as in the 16-bit
|
|
structure. Each item and each menu string are aligned on <code>DWORD</code> boundaries.
|
|
</p>
|
|
<p>
|
|
Menu separators are an interesting special case. There're two ways of identifying a separator: the menu flags
|
|
or type field contains the <code>MFT_SEPARATOR</code> option or both the type and the menu string are zero and
|
|
<code>NULL</code> respectively.
|
|
</p>
|
|
<h2>Fonts</h2>
|
|
<p>
|
|
The library implements limited support for fonts.
|
|
</p>
|
|
<p>
|
|
Font resources are different from the other types of resources in that they are not usually added to the resources
|
|
of a specific application program. Font resources are added to .exe files that are renamed to be .fon files.
|
|
These files are libraries as opposed to applications.
|
|
</p>
|
|
<p>
|
|
A <code>FontDirectoryResource</code> is composed of one or more <code>FontDirectoryEntry</code> instances. Each
|
|
instance contains a <code>FONTDIRENTRY</code> structure that describes the font. Note that this is the only
|
|
structure that is not aligned on an even boundary.
|
|
</p>
|
|
<h2>Side-by-Side Manifests</h2>
|
|
<p>
|
|
Since Windows XP, Windows reserves a new type of resource <code>RT_MANIFEST</code>, <code>24</code>
|
|
for Side-by-Side manifests. The manifest name can be one of the following values, best described
|
|
in <a href="http://blogs.msdn.com/junfeng/archive/2007/06/26/rt-manifest-resource-and-isolation-aware-enabled.aspx">this MSDN post</a>.
|
|
</p>
|
|
<ul>
|
|
<li><code>CREATEPROCESS_MANIFEST_RESOURCE_ID</code></li>
|
|
<li><code>ISOLATIONAWARE_MANIFEST_RESOURCE_ID</code></li>
|
|
<li><code>ISOLATIONAWARE_NOSTATICIMPORT_MANIFEST_RESOURCE_ID</code></li>
|
|
</ul>
|
|
<p>
|
|
Reading and writing the manifest resource is completely straightforward. The data is loaded in an
|
|
<code>XmlDocument</code>. The library avoids using <code>XmlDocument</code> unless it's used to make changes by the caller -
|
|
this ensures binary compatibility between reading and writing. When loading and resaving an <code>XmlDocument</code>
|
|
new lines in attribute values are normalized per <a href="http://www.w3.org/TR/REC-xml/#AVNormalize" target="_blank">W3C
|
|
spec section 3.3.3</a>.
|
|
</p>
|
|
<h2>
|
|
Source Code</h2>
|
|
<p>
|
|
The latest version of this article and source code can always be found on
|
|
CodePlex at <a href="http://resourcelib.codeplex.com/">http://resourcelib.codeplex.com/</a>.
|
|
</p>
|
|
<h2>
|
|
Links</h2>
|
|
<p>
|
|
This article combines, implements, ports, or obsoletes some or all of the functionalities
|
|
of the following publications:</p>
|
|
<ul>
|
|
<li>Raymond Chen's "The Old New Thing":
|
|
<ul>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2004/01/30/65013.aspx">The format of string resources.</a></li>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2008/07/09/8711897.aspx">The evolution of menu templates: 16-bit classic menus.</a></li>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2008/07/11/8719254.aspx">The evolution of menu templates: 32-bit classic menus.</a></li>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2008/07/16/8735896.aspx">The evolution of menu templates: 32-bit extended menus.</a></li>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2008/07/10/8714471.aspx">What's that deal with the alternate form for menu item template separator?</a></li>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2008/08/20/8880062.aspx">Icons and cursors know where they came from.</a></li>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2004/06/23/163596.aspx">The evolution of dialog templates: 32-bit extended templates.</a></li>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2006/12/21/1340571.aspx">The evolution of version resources: 32-bit version resources.</a></li>
|
|
<li><a href="http://blogs.msdn.com/oldnewthing/archive/2006/12/22/1348663.aspx">The evolution of version resources: corrupted 32-bit version resources.</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="http://www.codeproject.com/KB/library/VerInfoLib.aspx">CodeProject:
|
|
Modification of Version Information Resources in Compiled Binaries</a></li>
|
|
<li><a href="http://www.codeproject.com/KB/cpp/GetLocalVersionInfos.aspx">CodeProject:
|
|
Retrieving version information from your local application's resource</a></li>
|
|
<li><a href="http://www.codeproject.com/KB/dotnet/FastFileVersion.aspx">CodeProject:
|
|
A Fast Way to Get at the File's Version</a></li>
|
|
<li><a href="http://www.codeguru.com/csharp/.net/net_general/graphics/article.php/c12787/">
|
|
CodeGuru: IconLib: Icons Unfolded</a></li>
|
|
<li><a href="http://www.codeguru.com/cpp/w-p/win32/tutorials/print.php/c12873__1">
|
|
CodeGuru: Hacking Icon Resources</a></li>
|
|
<li><a href="http://www.codeproject.com/KB/GDI/using_vista_icons.aspx">CodeProject:
|
|
Windows Vista Icons</a></li>
|
|
<li><a href="http://msdn.microsoft.com/en-us/library/ms997538.aspx">MSDN: Icons in
|
|
Win32</a></li>
|
|
<li><a href="http://www.devsource.com/c/a/Architecture/Resources-From-PE-I/2/">DevSource:
|
|
Retrieving Resources from PE Files</a></li>
|
|
<li><a href="http://www.skynet.ie/~caolan/publink/winresdump/winresdump/doc/resfmt.txt">Microsoft Developer Support:
|
|
Win32 Binary Resource Formats</a></li>
|
|
<li><a href="http://blogs.msdn.com/junfeng/archive/2007/06/26/rt-manifest-resource-and-isolation-aware-enabled.aspx">MSDN:
|
|
RT_MANIFEST Resource and Isolation-Aware and Enabled Executables</a></li>
|
|
<li><a href="http://www.vbaccelerator.com/home/NET/Utilities/Icon_Extractor/article.asp">VBAccelerator:
|
|
.NET Icon Explorer</a></li>
|
|
<li><a href="http://www.microsoft.com/msj/0198/hood0198.aspx">MSJ:
|
|
Under The Hood - Into Depths of Dialogs</a></li>
|
|
</ul>
|
|
<h2>
|
|
History</h2>
|
|
<ul>
|
|
<li>2008-06-30: Initial version.</li>
|
|
<li>2008-09-28: Added support for icons.</li>
|
|
<li>2009-02-19: Moved to CodePlex, released 1.1.</li>
|
|
<li>2009-09-15: Added support for cursors, bitmaps, dialogs, string tables,
|
|
accelerators, menus and SxS manifests.</li>
|
|
</ul>
|
|
</body>
|
|
</html>
|