Click here to Skip to main content
6,305,776 members and growing! (15,479 online)
Email Password   helpLost your password?
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
Posted:30 Jun 2008
Updated:29 Sep 2008
Views:15,573
Bookmarked:49 times
Announcements
Loading...
 
Search    
Advanced Search
printPrint   Broken Article?Report       add Share
  Discuss Discuss   Recommend Article Email
13 votes for this article.
Popularity: 5.21 Rating: 4.68 out of 5
1 vote, 7.7%
1

2

3
3 votes, 23.1%
4
9 votes, 69.2%
5

Introduction

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.

Background

Initially, I started porting the version resource implementation from Denis Zabavchik's C++ VerInfoLib. Then, it grew bigger ...

Using the Code

Enumerating Resources

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

Reading Version Information

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

Writing Version Information

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);

Implementation

Base Structures

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.

Alignment

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

Reading

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

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 to Icons

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.

  • A GroupIconResource represents a collection of icon resources.
  • An IconResource represents a single icon with many images.
  • An IconImage is not a resource per-se, but raw data embedded in the file at an offset defined by the icon resource and represents a single icon bitmap, for example in a BMP format.

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.

Source Code

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.

Links

This article combines, implements, ports, or obsoletes some of the functionalities of the following publications:

History

  • 2008-06-30: Initial version
  • 2008-07-08: Bug: wrong value for RT_RCDATA
  • 2008-09-28: Added support for icons

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

dB.


Member
Daniel Doubrovkine has been in software engineering for twelve years and is currently development manager at Application Security Inc. in New York City. He has been involved in many software ventures, including Xo3 and Vestris Inc, was a development lead at Microsoft Corp. in Redmond, and director of Engineering at Visible Path Corp. in New York City. Daniel also builds and runs a foodie website, http://www.foodcandy.com.
Occupation: Team Leader
Company: Application Security Inc., www.appsecinc.com
Location: United States United States

Other popular Libraries articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 17 of 17 (Total in Forum: 17) (Refresh)FirstPrevNext
Generalgood good good good PinmemberKoaQiu19:00 2 Jul '09  
GeneralGreat Work PinmemberHarshdeep Khatri0:39 6 Mar '09  
GeneralRe: Great Work PinmemberdB.13:49 18 Mar '09  
GeneralProject moved to CodePlex, 1.1 released PinmemberdB.5:42 19 Feb '09  
GeneralNew build 1.0.1605.0 PinmemberdB.18:15 5 Jan '09  
QuestionFile size changing Pinmemberdenisejanse5:05 27 Oct '08  
AnswerRe: File size changing PinmemberdB.8:15 27 Oct '08  
GeneralAdding RT_VERSION to dll without RT_VERSION Pinmembergyldor11:36 1 Oct '08  
GeneralRe: Adding RT_VERSION to dll without RT_VERSION PinmemberdB.18:17 1 Oct '08  
GeneralRe: Adding RT_VERSION to dll without RT_VERSION Pinmembergyldor11:04 2 Oct '08  
GeneralRe: Adding RT_VERSION to dll without RT_VERSION [modified] PinmemberdB.4:28 3 Oct '08  
GeneralRe: Adding RT_VERSION to dll without RT_VERSION Pinmembergyldor12:43 3 Oct '08  
NewsNew: support for icons [modified] PinmemberdB.9:59 28 Sep '08  
GeneralExcelent, but wrong CONSTs in Kernel32.ws PinmemberJürgen Kliegl5:50 8 Jul '08  
GeneralRe: Excelent, but wrong CONSTs in Kernel32.ws PinmemberdB.17:47 8 Jul '08  
GeneralRe: Excelent, but wrong CONSTs in Kernel32.ws PinmemberJürgen Kliegl23:26 8 Jul '08  
GeneralRe: Excelent, but wrong CONSTs in Kernel32.ws PinmemberTobiasP1:09 9 Jul '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin 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