Search code examples
delphiwinapidelphi-xe6delphi-5sqloledb

GetFileVersionInfo returning incorrect fileVersion for SQLOLEDB.dll


Short Version

File version of SqlOledB.dll:

Long Version

I am trying to get the file version of the Windows dll "C:\Program Files (x86)\Common Files\System\Ole DB\sqloledb.dll"

In Windows Explorer, I can see:

  • File version: 10.0.18362.1
  • Product version: 10.0.18362.1

image

When I use Powershell:

> [System.Diagnostics.FileVersionInfo]::GetVersionInfo("C:\Program Files (x86)\Common Files\System\Ole DB\sqloledb.dll") | Format-List -property *

I can see the file version:

FileVersionRaw     : 10.0.18362.1
ProductVersionRaw  : 10.0.18362.1
Comments           :
CompanyName        : Microsoft Corporation
FileBuildPart      : 18362
FileDescription    : OLE DB Provider for SQL Server
FileMajorPart      : 10
FileMinorPart      : 0
FileName           : C:\Program Files (x86)\Common Files\System\Ole DB\sqloledb.dll
FilePrivatePart    : 1
FileVersion        : 10.0.18362.1 (WinBuild.160101.0800)
InternalName       : sqloledb.dll
IsDebug            : False
IsPatched          : False
IsPrivateBuild     : False
IsPreRelease       : False
IsSpecialBuild     : False
Language           : English (United States)
LegalCopyright     : © Microsoft Corporation. All rights reserved.
LegalTrademarks    :
OriginalFilename   : sqloledb.dll
PrivateBuild       :
ProductBuildPart   : 18362
ProductMajorPart   : 10
ProductMinorPart   : 0
ProductName        : Microsoft® Windows® Operating System
ProductPrivatePart : 1
ProductVersion     : 10.0.18362.1
SpecialBuild       :

When I look at the VersionInfo resource in the DLL, I see the file and product versions match:

1 VERSIONINFO
FILEVERSION 10,0,18362,1
PRODUCTVERSION 10,0,18362,1
FILEOS 0x40004
FILETYPE 0x2
{
BLOCK "StringFileInfo"
{
    BLOCK "040904B0"
    {
        VALUE "CompanyName", "Microsoft Corporation"
        VALUE "FileDescription", "OLE DB Provider for SQL Server"
        VALUE "FileVersion", "10.0.18362.1 (WinBuild.160101.0800)"
        VALUE "InternalName", "sqloledb.dll"
        VALUE "LegalCopyright", "© Microsoft Corporation. All rights reserved."
        VALUE "OriginalFilename", "sqloledb.dll"
        VALUE "ProductName", "Microsoft® Windows® Operating System"
        VALUE "ProductVersion", "10.0.18362.1"
    }
}

BLOCK "VarFileInfo"
{
    VALUE "Translation", 0x0409 0x04B0
}
}

When I use PEView to view the raw version resource, I see 000A0000 47BA0001 for both the file and product versions. I also see the file info string section that says:

  • "FileVersion": "10.0.18362.1 (WinBuild.160101.0800)"
  • "ProductVersion": "10.0.183612.1"

And when I use Process Monitor, I can see my application querying the correct DLL:

image

But, when I query it from code, I get the wrong value!

When my 32-bit application calls:

and look at the contents of the returned VS_FIXEDFILEINFO buffer, I see:

  • dwSignature: 4277077181
  • dwStrucVersion: 65536
  • dwFileVersionMS: 393218
  • dwFileVersionLS: 1203372033
  • dwProductVersionMS: 655360
  • dwProductVersionLS: 1203372033
  • dwFileFlagsMask: 63
  • dwFileFlags: 0
  • dwFileOS: 262148
  • dwFileType: 2
  • dwFileSubtype: 0
  • dwFileDateMS: 0
  • dwFileDateLS: 0

If you look at the fileVersion and productVersion values, and convert them to hex:

Field Decimal Hex
dwFileVersionMS 393218 0x00060002
dwFileVersionLS 1203372033 0x47BA0001
dwProductVersionMS 655360 0x000A0000
dwProductVersionLS 1203372033 0x47BA0001

Which gives you the versions in hex of:

  • File Version: 0x0006, 0x0002, 0x47BA, 0x0001
  • Product Version: 0x000A, 0x0000, 0x47BA, 0x0001

We get the versions as decimal strings:

  • File Version: 6.2.18362.1
  • Product Version: 10.0.13621.1

The product version is correct, but the file version is completely wrong.

Which begs the questions:

  • Where is the GetFileVersion() API function possibly getting version 6.2.x.x from; when everything about the DLL binary says 10.0.x.x?
  • Is there an appcompat shim kicking in; lying to me about the version number so that it's Windows 6.2 rather than Windows 10.0?
  • Do I need to update my SupportedOS entries in my assembly manifest?

Short Version: How is Windows returning a version value that does not exist anywhere in the binary?

CRME for the lazy

program Project1;

{$APPTYPE CONSOLE}

uses
  SysUtils, Windows;

function GetFileVersion(Filename: WideString): string;
var
    dwHandle: Cardinal;
    dwInfoLength: Cardinal;
    pInfoData: Pointer;
    lengthOfReturned: Cardinal;
    value: Pointer;
    fileInfo: PVSFixedFileInfo;
    iMajor, iMinor, iBuild, iRelease: Integer;
begin
    Result := '?.?.?.?';

    // Get the number of bytes he have to allocate for the file information structure
    dwHandle := 0;
    dwInfoLength := GetFileVersionInfoSizeW(PWideChar(Filename), {var}dwHandle);
    if dwInfoLength = 0 then
        Exit;

    // Allocate the memory needed to hold the file info
    GetMem(pInfoData, dwInfoLength);
    try
        //Put the information into the buffer we just allocated
        if not GetFileVersionInfoW(PWideChar(Filename), 0, dwInfoLength, pInfoData) then
            Exit;

        // Extract the desired data from pInfoData into the FileInformation structure
        value := nil;
        lengthOfReturned := 0;
        if not VerQueryValueW(pInfoData, '\', {var}value, {var}lengthOfReturned) then
            Exit;

        fileInfo := PVSFixedFileInfo(value);

        iMajor := fileInfo.dwFileVersionMS shr 16;
        iMinor := fileInfo.dwFileVersionMS and $FFFF;
        iRelease := fileInfo.dwFileVersionLS shr 16;
        iBuild := fileInfo.dwFileVersionLS and $FFFF;
    finally
        FreeMem(pInfoData);
    end;

    Result := Format('%d.%d.%d.%d', [iMajor, iMinor, iRelease, iBuild]);
end;

procedure Main;
var
    filename: string;
    version: string;
begin
    filename := 'C:\Program Files (x86)\Common Files\System\Ole DB\sqloledb.dll';
    version := GetFileVersion(filename);

    WriteLn('Filename:     '+filename);
    WriteLn('File version: '+version);
end;


begin
    Main;

    WriteLn('Press enter to close...');
    ReadLn;
end.

Bonus Chatter

I also tried using the ANSI versions (with ANSI strings), in case the legacy ANSI version had some app-compat feature that lies about version numbers.

Tried in Delphi 5 and Delphi XE6 - both 32-bit apps.

Bonus Reading


Solution

  • Yes, it is an app-compat shim kicking in - lying to me about the version of the .dll i use.

    Adding the Windows 10 entry

    <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
    

    to my Assembly.manifest fixes it.