One of our biggest pet peeves is finding a driver installed on a system, written by a 3rd party that either doesn’t have a Version-Information resource in the image at all, or has one that is blatantly incorrect. The most common Version-Information resource error that we see is a 3rd party containing a Microsoft copyright string (!) as a result of not overriding the default values supplied in the WDK.
This article will discuss what a Version-Information is and how to create one for your driver.
What is a Version-Information Resource
A Version-Information (VI) resource is a type of Resource Compiler (RC) object that provides information about the executable image that contains it. The information within the VI resource typically includes the executable image’s version number, intended operating system, and original file name. This information is viewed in Windows Explorer as shown in Figure 1, and also may be retrieved programmatically using the Windows Version Information functions.
The VI resource of an executable is defined by a VERSIONINFO block contained within an RC file (a file with the .RC extension) that is compiled along with the other files that are part of the executable. In the case of a WDK project, the RC file would be declared as an element in the SOURCES= line of a project’s sources file.
For readers unfamiliar with an RC file, it is a resource-definition script file (in text format) that describes the resources used by your executable object. For applications, these objects can be cursors, icons, bitmaps, dialog boxes, fonts, version-information, strings, or user-definable objects. For drivers, only the version-information is typically specified in the RC file.
For the purposes of defining a VI resource for a driver, the definitions needed to compile the RC file can be satisfied by including winver.h. The RC file is compiled with the resource compiler, which generates a compiled resource (.res) file that the linker links into your driver’s executable image. See below for a typical example of an OSR created RC file that defines a VERSIONINFO resource:
// // Include the necessary resources // #include <winver.h> #include <ntdef.h> #ifdef RC_INVOKED // // Set up debug information // #if DBG #define VER_DBG VS_FF_DEBUG #else #define VER_DBG 0 #endif // ------- version info ------------------------------------------------------- VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 FILEFLAGSMASK VS_FFI_FILEFLAGSMASK FILEFLAGS VER_DBG FILEOS VOS_NT FILETYPE VFT_DRV FILESUBTYPE VFT2_DRV_SYSTEM BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments", "OSR Driver" VALUE "CompanyName", "OSR Open Systems Resources, Inc." VALUE "FileDescription", "OSR Driver" VALUE "FileVersion", "V1.0.0.0" VALUE "InternalName", "A OSR Written Driver" VALUE "LegalCopyright", "(C)2011 OSR Open Systems Resources, Inc." VALUE "OriginalFilename", "OSRDRV.sys" VALUE "ProductName", "OSR Driver" VALUE "ProductVersion", "V1.0.0.0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0409,1200 END END #endif
As you can see above the VI resource contains a number of elements that describe the executable with which it is associated. Let us discuss the VERSIONINFO resource in the next section.
One thing that should be mentioned here: This RC file in (again, above) that OSR typically uses does not follow the typical format from the examples that you find in a WDK. That’s because the OSR resource file is “fully specified” – it’s not built using a set of Microsoft-supplied macros. Contrast the OSR file above with the typical RC file from the WDK shown below:
#include <windows.h> #include <ntverp.h> #define VER_FILETYPE VFT_DRV #define VER_FILESUBTYPE VFT2_DRV_SYSTEM #define VER_FILEDESCRIPTION_STR "WDM Driver for Intel 8255x Ethernet Adapters" #define VER_INTERNALNAME_STR "PCIDRV.sys" #define VER_ORIGINALFILENAME_STR "PCIDRV.sys" #define VER_FILEVERSION 1,00,00,0000 #define VER_FILEVERSION_STR "1.00.00.0000" #undef VER_PRODUCTVERSION #define VER_PRODUCTVERSION VER_FILEVERSION #undef VER_PRODUCTVERSION_STR #define VER_PRODUCTVERSION_STR VER_FILEVERSION_STR #define VER_LEGALCOPYRIGHT_STR "Copyright (C) 2003 Microsoft Corporation" #ifdef VER_COMPANYNAME_STR #undef VER_COMPANYNAME_STR #define VER_COMPANYNAME_STR "Microsoft Corporation" #endif #undef VER_PRODUCTNAME_STR #define VER_PRODUCTNAME_STR "Microsoft Sample Driver for PCI Device" #include "common.ver" PciDrvWMI MOFDATA pcidrv.bmf
Notice that the OSR created RC does not include “ntverp.h” or “common.ver”. Why? We don’t include these files because they contain Microsoft-specific defaults that will be used if you fail to properly set all the fields in your driver’s VI resource. These defaults are appropriate for Microsoft developers, but probably not the best choice for others. Driver writers who use a WDK RC file as a template for their RC file are the ones who typically end up with a VI resource that contains things like the default Microsoft copyright because they forgot to specify a copyright of their own.
Now we’re not saying that using the WDK-provided method is bad. All we are trying doing is warning you of the consequences of using that method. Namely, if you’re not careful when you use the WDK-provided method, your RC file you may end up with information in your VI resource that you did not intend to be there.
So, given that, let’s talk about what is in a VERSIONINFO resource.
VI resource
The VI resource that we mention above is defined in the RC file using the VERSIONINFO statement. The VI resource itself can be defined using one of two following formats:
versionID VERSIONINFO fixed-info { block-statements …}
Or,
versionID VERSIONINFO fixed-info BEGIN block-statement …. END
Let’s describe the fields used in each of these formats.
versionID
This field is the version resource identifier and must be 1. As an alternative to specifying 1 in this field, devs often specify VS_VERSION_INFO.The value for VS_VERSION_INFO is defined in the include file “verrsrc.h”, but is referenced in the include file “winver.h” (typically referenced in an RC file).
fixed-info
The fixed-info section of a VI resource contains version information such as the file version and intended operating system. It is composed of the following statements:
- FILEVERSIONversion – This statement defines the binary version number of the executable. It is made up of two 32-bit integers which are broken up into four 16-bit fields. Given a the statement “FILEVERSION 2,5,6,7” this would produce the integers 0x00020005 and 0x00060007 or the 4 16-bit fields of 0x0002, 0x0005, 0x0006, 0x0007 respectively.
- PRODUCTVERSIONversion– This statement defines the binary version number of a product associated with the executable. Its format follows the same format as defined in the FILEVERSION statement. One thing that devs often find confusing is that this version number does not need to match the version number in the DriverVer field of the driver’s INF file. In fact, in most cases, these two values are unrelated. Crazy, I know… but that’s the way it is.
- FILEFLAGSMASKfileflagsmask – This statement is used to give some information about the state of the executable image associated with this VI. The state of the image is defined by OR’ing together one or more of the following flags (example: “FILEFLAGS VS_FF_DEBUG | VS_FF_PRERELEASE”):
- VS_FF_DEBUG – the image contains debugging information or is compiled with debugging features enabled.
- VS_FF_PATCHED – This image has been patched and is not identical to the original image containing the same FILEVERSION number.
- VS_FF_PRERELEASE – This image is a development version of the image and is not a released product
- VS_FF_PRIVATEBUILD – This image was not built using the standard release procedures. If this flag is set the StringFileInfo block must contain a PrivateBuild string.
- VS_FF_SPECIALBUILD – This image was built using the standard release procedures but varies from the original image with the same FILEVERSION number. If this flag is set the StringFileInfo block must contain a SpecialBuild string.
- VS_FF_FILEFLAGSMASK – this flag is a combination of all preceding values.
- FILEOSfileos – This statement is used to define the operating system that this image is valid for. This field is a 32-bit integer that is made up of two 16-bit fields. The high 16 bits indicate the OS that the image is valid for, while the low 16-bits indicates’ the windowing system. Most driver writers only care about Windows NT, so the only value that makes sense is (If you care to know what the other values are check out “verrsrc.h”):
- VOS_NT – This value indicates that this image was designed for 32-bit windows. The reader should note that there is no value for 64-bit windows, setting VOS_NT will suffice
- FILETYPEfiletype –This statement is used to define the type image this VI resource describes. This field can contain one of a number of values, but as stated above we are driver writers and only care about drivers. Thus for us, the only value that make sense is:
- VFT_DRV – This value indicates that the image contains a device driver. The FILESUBTYPE value described below contains a more specific description of the contained driver.
- FILESUBTYPEfilesubtype –This statement is used to further refine the driver image that was indicated in the FILETYPE state. It can have one of the following values:
- VFT2_UNKNOWN – this value indicates the driver type is unknown
- VFT2_DRV_COMM – image contains a communications driver
- VFT2_DRV_PRINTER – image contains a printer driver
- VFT2_DRV_KEYBOARD – image contains a keyboard driver
- VFT2_DRV_LANGUAGE – image contains a language driver
- VFT2_DRV_DISPLAY – image contains a display driver
- VFT2_DRV_MOUSE – image contains a mouse driver
- VFT2_DRV_NETWORK – image contains a network driver
- VFT2_DRV_SYSTEM – image contains a system driver
- VFT2_DRV_INSTALLABLE – image contains an installable driver
- VFT2_DRV_SOUND – image contains a sound driver
- VFT2_DRV_VERSIONED_PRINTER – image contains a versioned printer driver
The reader should note that any field listed above that is not specified in your RC file, will default to 0. In addition, if you reexamine Figure 1 you will notice that none of the information from the fixed-info section is visible via Explorer. You should keep in mind that the information contained in the VI is really only there for informational purposes, the real information about the image is contained within the Portable Executable header (PE Header) of the image itself.
block-statement
The “block-statement” section defines one or more version-information blocks. The version-information blocks can contain multiple StringFileInfo blocks and accompanying VarFileInfo blocks as defined below.
VarFileInfo Block
VarFileInfo defines a Variable File information block. This block describes the language and character set used to encode the strings contained within the associated StringFileInfo block discussed in the next section. The VarFileInfo block is defined as follows:
BLOCK “VarFileInfo” {VALUE “Translation”, langID, charsetID ….}
Where:
- langId – This field contains a language code in hexadecimal format. All the possible values for this field are contained within the documentation, but an example would be a value like “0x0409” which indicates a language of U.S English.
- charsetID – This field contains a character set code in hexadecimal format. All the possible values for this field are contained within the documentation, but an example would be a value like “1200” which indicates a character set of Unicode.
With this “Translation” statement you can specify multiple langId and charsetID pairs which will show the user that that are multiple translations for the VI in the image. So as an example let’s assume that in our RC file we wanted to have a StringFileInfo information block in U.S. English using the Unicode character set. Our VarFileInfo block would be defined as seen below:
BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x409, 1200 END END
So now that we’ve indicated the language and character set to be used for our information, we need to create a StringFileInfo block in U.S. English using Unicode with the appropriate information. Let us see how it is done in the next section.
StringFileInfo Block
StringFileInfo defines an information block that contains a number of string-name parameters that describe the contained image in human-readable format. This block describes the language and character set used to encode the strings contained within the block. The StringFileInfo block is defined as follows:
BLOCK “StringFileInfo” {BLOCK “lang-charset” {VALUE “string-name”, “value” …}}
Where:
- lang-charset – this field contains a language and character-set pair. All the possible values for this field are contained within the documentation, but an example would be a string value like “040904B0” which indicates a language of U.S English (0409) and a character set of Unicode (04B0).
- string-name –this field contains one or more predefined names that describe the image in human readable format (in the language and character set defined by the lang-charset statement). The predefined names are:
- Comments – a optional comment string
- CompanyName – a required string indicating the company that created this image
- FileDescription – a required string that contains a string describing the image
- FileVersion – a required string that contains the version number of the file in human-readable format, for example “V1.0.3.2” or “A1.0.0.0 RC2”
- InternalName – a required string that contains the name of the image, for example “osrdrv.sys”
- LegalCopyright – an optional string that contains the copyright notices that apply to this image. If used, this should contain all copyright notices, legal symbols, copyright dates, and other information that apply.
- LegalTrademarks – an optional string that contains all trademarks and registered trademarks that apply to the image. If used, this should contain all notices, legal symbols, trademark numbers, and other information that apply,
- OriginalFileName – a required string that contains the original name of the file. This allows an application to determine whether the image has been renamed by the user.
- PrivateBuild – this string should only be present if the user specified VS_FF_PRIVATEBUILD in the FILEFLAGMASK defined in the fixed-info section. It should describe something about the private build.
- ProductName – This is a required string that describes the name of the product which this image is associated with.
- ProductVersion – This is a required string that contains the version number of the product which this image is associated with. For example, “OSRUSB Learning Kit V2.0.1.0”
- SpecialBuild – this string should only be present if the user specified VS_FF_SPECIALBUILD in the FILEFLAGMASK defined in the fixed-info section. It should describe something about the special build.
It is worth noting that you can add your own string-name fields to this block. Unfortunately you will not be able to see them via Explorer. To see them you would have to write a program that uses the Win32 Version Information functions to retrieve them. In addition if you don’t specify a pre-defined name, its information will not be shown via Explorer.
So those are the fields that are contained within a StringFileInfo block. Now, as we showed in Figure 4 we defined a VarFileInfo block that indicated that our StringFileInfo block is in U.S. English using the Unicode character set. Therefore we have to have a StringFileInfo block with a lang-charset that corresponds to what we specified in the VarFileInfo block. Since we put 0x409 (U.S. English) and 1200 (Unicode) then the setting for our lang-charset in our StringFileInfo block will be “040904b0”, where 0409 corresponds to our specified language and 04b0 corresponds to our specified character set (1200 == 0x04b0). Simple! See below for the StringFileInfo block for U.S English in Unicode.
BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "040904b0" BEGIN VALUE "Comments", "OSR Driver" VALUE "CompanyName", "OSR Open Systems Resources, Inc." VALUE "FileDescription", "OSR Driver" VALUE "FileVersion", “V1.0.0.0” VALUE "InternalName", "A OSR Written Driver" VALUE "LegalCopyright", "2011 OSR Open Systems Resources, Inc." VALUE "OriginalFilename", "OSRDRV.sys" VALUE "ProductName", "OSR Driver" VALUE "ProductVersion", “V1.0.0.0” END END
So as you can see, the VERSIONINFO block can be used to thoroughly describe the image that is embedded in and this allows the user and the developer to quickly identify the software installed on the users’ machine.
Note: RC files and VERSIONINFO resources can be internationalized. All you need to do is create additional VERSIONINFO resources in your RC file in the languages you require.
Now that we’ve discussed what fields are in a VERSIONINFO resource, let’s talk about creating one for our image.
Creating your own VI Resource
As mentioned earlier, an RC file is just a text script file. While it can be created with a resource editor, for example within Visual Studio, it can just as well be created by hand using your favorite text editor. Instead of starting from scratch and writing your RC file from scratch, the easiest thing to do is find an existing RC file with the required information and replace the fields that are important to your driver. We would suggest you copy the information shown earlier in the simple resource file into your WDK project’s RC file and edit it appropriately.
Now when we say edit it appropriately, what we mean is that you modify the following fields with settings that are appropriate for your image (you should note that if you want your Version Information to be visible in multiple languages you are going to be required to have a StringFileInfo/VarFileInfo section for each supported language):
FILEVERSION
PRODUCTVERSION
FILEFLAGSMASK
FILETYPE
FILESUBTYPE
CompanyName
FileDescription
InternalName
LegalCopyRight
LegalTradeMarks
OriginalFileName
ProductName
ProductVersion
Once you have completed that you need to add the name of the “.RC” file that you created to your sources file. So in an example “Sources” file, for OSR’s “nothing” driver shown below, you can see how we added “nothingver.rc” to the “SOURCES” line.
MAJORCOMP=ntos MINORCOMP=osr TARGETNAME=nothing TARGETPATH=obj TARGETTYPE=DRIVER INCLUDES=$(DDK_INC_PATH)\inc;..\inc SOURCES=nothing.c nothingver.rc
When we use the WDK “build” utility to compile our nothing driver, it will see the “nothingver.rc” file on the “SOURCES” line in our “SOURCES” file and automatically compile it and link it to the “nothing.sys” file that it builds.
After you have successfully built the driver, make sure that you use Explorer to examine the information that you specified to ensure that all the information you want the user to see is there. In addition you want to make sure that there is no information present that you did not intend to be there. Remember that sometimes (depending on how the VI got created) Microsoft supplied information can sneak in, so you have to make sure that you’re supplying all the correct fields.
Summary
With the information contained within this article you can now create and add a comprehensive RC file to your driver without the risk of getting unexpected values in the various fields. This information will allow your users and support people to immediately know what version of your product and image is being run. Now that is being resourceful!