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:

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:

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:

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:

We get the versions as decimal strings:

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

Which begs the questions:

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.