JD2022-TU1/main/extern/Vestris.ResourceLib/1.2/Article/resourcelibdotnet.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, &quot;atl.dll&quot;);
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, &quot;atl.dll&quot;);
VersionResource versionResource = new VersionResource();
versionResource.LoadFrom(filename);
Console.WriteLine(&quot;File version: {0}&quot;, versionResource.FileVersion);
StringFileInfo stringFileInfo = (StringFileInfo) versionResource[&quot;StringFileInfo&quot;];
foreach (KeyValuePair&lt;ResourceId, StringTableEntry&gt; versionStringTableEntry in stringFileInfo.Default.Strings)
{
Console.WriteLine(&quot;{0} = {1}&quot;, 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, &quot;atl.dll&quot;);
VersionResource versionResource = new VersionResource();
versionResource.LoadFrom(filename);
Console.WriteLine(&quot;File version: {0}&quot;, versionResource.FileVersion);
versionResource.FileVersion = &quot;1.2.3.4&quot;;
StringFileInfo stringFileInfo = (StringFileInfo) versionResource[&quot;StringFileInfo&quot;];
stringFileInfo[&quot;CompanyName&quot;] = &quot;My Company\0&quot;;
stringFileInfo[&quot;Weather&quot;] = &quot;Sunshine, beach weather.&quot;;
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() &lt; (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 &gt; 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&lt;string, StringTable&gt;.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>