Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

DLL Versions from C and VB


May 2001/DLL Versions from C and VB


One of the most annoying things about maintaining component- based software is determining the actual version number of every component that your application uses. I have done this tedious task more times than I would like to admit. This is typically accomplished by using Explorer, finding the file (in the abyss I call Windows\System), right-clicking the file, choosing the Properties menu item, choosing the Version tab... blah, blah, blah. Did you ever try to explain this process over the phone to a user who isn’t computer savvy? After the fifth or sixth object module, the user gets a nice lesson on using Windows Explorer, but that isn’t really the result you want and this essential information is required for technical support to be able to quickly and effectively determine the version and load path of any one of the software components used in your system.

Tired of this repetitive work, I created a software routine that, given a filename, returns the load path and the version of that file extracted from the version resource of that file (if available). This is pretty straightforward. Win32 calls and structures. However, there was a catch: I needed to implement this in both C and in Visual Basic. OK, big deal, right? Programmers have been calling the Win32 API from Visual Basic for years. But, the wrinkle that I ran into was that the structure that contains resource information aligns portions of the memory block on a 32-bit boundary. This makes the structure size unknown until runtime. This is no big deal in C, using pointers, but how do you access it from a language like Visual Basic that prefers to avoid pointers? Read on and I’ll show you how I did it.

Defining the Interface

Since I think in C, I naturally solved the problem in C first, creating a function named GetDllVersion(). The C source for this function (and a simple demonstration program) is in VerRes.c (Listing 1), and it has the following prototype:

int GetDllVersion(
    const char* filename,
    char* loadpath,
    int maxloadpath,
    int &maj,
    int &min,
    int &rev,
    int &bld )

filename is a string containing the name of the file the caller wants to gather information about. This should be a PE format file of some kind: DLL, OCX, .exe, etc.

loadpath must point to a buffer of at least maxloadpath characters. GetDllVersion() will store in loadpath the path that LoadLibrary() would use to load the module named in filename.

maj, min, rev, and bld are the addresses where GetDllVersion() will store the version information it obtains for the target module. They represent, respectively: the major version, the minor version, the revision number, and the build number.

GetDllVersion() starts by attempting to load the specified file by calling Win32’s LoadLibrary(). If LoadLibrary() can’t locate the module, then GetDllVersion() returns FALSE. Otherwise, LoadLibrary() returns an instance handle to the loaded module. GetDllVersion() then calls GetModuleFileName(), which will supply the actual load path of the filename parameter that was found by LoadLibrary(). GetDllVersion() copies this path to the loadpath parameter so that the caller can use it later.

The Version Resource

Once you have a loaded module’s handle, any resource in that module is available through various Win32 functions. I used LoadResource() to obtain a handle to the VS_VERSION_INFO resource, and LockResource() to obtain a pointer to that resource. The first parameter of LoadResource() is the name of the resource being requested. The second parameter is the RT_VERSION resource type. #1 means resource ID 1, which is the default number used when you add a version resource in Visual Studio. This seems to be the default ID for the version resource, so I went with it. This was determined empirically by loading a compiled file in Visual Studio as a resource. The VS_VERSION_INFO structure is defined in LoadVersion.cpp because it really isn’t defined in windows.h, but if you do a help search in Developer Studio it will seemingly appear as a valid structure. The reason it isn’t defined in the header files is because this structure is dynamic - its length isn’t known until you start to traverse it. The first four elements are fixed in size. However, the structure I am really interested in, VS_FIXEDFILEINFO, is located right after the Padding1. The padding parameter is defined in MSDN as: “Contains as many zero words as necessary to align the Value member on a 32-bit boundary,” which makes its length unknown.

What exactly does the MSDN definition mean? It means that the value or VS_FIXEDFILEINFO structure is on a DWORD boundary in memory. An item that is DWORD aligned means that the structure is located at an offset that is a multiple of four bytes. For example: valid DWORD aligned addresses might be: 0x0000, 0x0004, 0x0008, 0x0010, etc. Unaligned accesses are illegal on some hardware architectures, but merely slower on 80x86 architectures.

Walking Along

To find the VS_FIXEDFILEINFO structure, I first assign a byte pointer to the Padding1 member:

char * p = (char *) pInf->Padding1;

Next, I increment this pointer in byte increments until it is aligned on a DWORD boundary. This is accomplished in the while condition: (((long)p)&0x04) != 0). As I walk along one byte at a time, I continue to refresh a VS_FIXEDFILEINFO pointer so that when the while condition fails, I will have a memory pointer ready to dereference the version information. The last step is to extract the version numbers and assign them to the output parameter passed by the callee.

Visual Basic Implementation

I have to admit that Visual Basic is the easiest and most robust ActiveX container application development platform that I know. When I write ActiveX components, I always test them with Visual Basic. This is why I needed this algorithm implemented in Visual Basic and because many of my front-end user interfaces are written in Visual Basic. Setting up a pointer and traversing a memory structure is simple to do in C because of its loose type casting. It was trivial for me to simply assign my character pointer to a VS_FIXEDFILEINFO structure by typecasting. This is a very powerful tool to the C programmer (a tool that has its dark side as well,) and the C compiler makes no judgments on my intentions. Visual Basic has stronger type casting, and worst of all, it has no pointer types.

I have implemented the exact algorithm from the description above in Visual Basic in VerRes.Bas (Listing 2). All of the API calls are the same, but the Visual Basic implementation has a few exceptions. Once the version resource is locked, the memory pointer is assigned to a long data type. This works out well to do the search for the 32-bit boundary as well.

The difficulty is in extracting the version information once I have located it. I have a long data type that is “pointing” to my VS_FIXEDFILEINFO, but since I can’t dereference a long, I’m pretty much beat, right? Not really. All I need to do is copy Len(VS_FIXEDFILEINFO) bytes from the memory pointed to by my long pointer into a Visual Basic type of VS_FIXEDFILEINFO.

I have “overloaded” a Win32 function (RtlMoveMemory()) to a private function named CopyMemoryFromPointer() to do exactly that. I use the term “overloaded” because I really can’t describe it in a better way: I play a trick on Visual Basic by using the ByVal source parameter as my long pointer and a ByRef destination parameter as my Visual Basic type variable. Once inside Win32’s RtlMoveMemory(), the two parameters are treated as void pointers, and Win32 happily copies the data from the source data block to the destination. Upon return, I can now dereference the VS_FIXEDFILEINFO structure and extract the version number from the resource. This works well with any type variables. As long as I know the size so that I can create memory in Visual Basic to hold the variable, I can copy any one type to another using VarPtr() to get the address of a variable. That’s typecasting in Visual Basic!

Summary

Both the listings include main() subroutines that will allow them to be built as a test program. The C version can be built as a Win32 console application and takes the input filename as a command-line argument. The Visual Basic version also requires a filename command-line argument and will display a message box with the results of the run. The main() subroutines can easily be removed to incorporate each module into your own project.

It’s worth noting that the next version of Visual Basic (.NET) has a variety of incompatibilities. VarPtr() is one of the features that Microsoft has removed from the language and labeled a “legacy feature.” You can read more about these incompatibilities at: msdn.Microsoft.com/library/techart/vb6tovbdotnet.htm.

Jim Gentilin is Director of Sofware Engineering at OmniTester Corporation. He’s been programming professionally for 11 years and spends his free time as a husband, an electronics hobbyist, and as the guitarist for the Diablo Sandwich Band (www.diablosandwich.com) who regularly appears in clubs on the Jersey shore.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.