C#を用いたWindowsアプリケーションのバージョン情報取得手法

概要

FileInfoクラスでは取得できないバージョン情報リソースを、Win32 APIを介して取得する方法を説明します。

リソースフォーマットの特徴

VERSIONINFOリソースは以下の構造を持ちます:

1 VERSIONINFO
FILEVERSION 1,0,0,323
PRODUCTVERSION 1,0,0,0
FILEOS 0x4
FILETYPE 0x1
{
    BLOCK "StringFileInfo"
    {
        BLOCK "000004B0"
        {
            VALUE "CompanyName", "Example Corp"
            VALUE "ProductName", "Sample App"
            VALUE "FileVersion", "1.0.0.323"
        }
    }
    BLOCK "VarFileInfo"
    {
        VALUE "Translation", 0x0000 0x04B0
    }
}

実装コード

以下のコードはバージョン情報を取得するためのC#実装です。API呼び出しを最適化し、エラーハンドリングを強化しています。

// Win32 APIのラッパー
[DllImport("version.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool GetFileVersionInfo(string path, int handle, int size, byte[] buffer);

[DllImport("version.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int GetFileVersionInfoSize(string path, out int handle);

[DllImport("version.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool VerQueryValue(byte[] buffer, string subBlock, out IntPtr data, out int size);

public static Dictionary<string, string> ExtractVersionData(string filePath)
{
    int versionSize = GetFileVersionInfoSize(filePath, out _);
    if (versionSize <= 0) throw new Win32Exception(Marshal.GetLastWin32Error());

    byte[] versionBuffer = new byte[versionSize];
    if (!GetFileVersionInfo(filePath, 0, versionSize, versionBuffer))
        throw new Win32Exception(Marshal.GetLastWin32Error());

    // 言語コードの取得
    IntPtr langPtr = IntPtr.Zero;
    int langSize = 0;
    if (!VerQueryValue(versionBuffer, @"\VarFileInfo\Translation", out langPtr, out langSize))
        throw new Win32Exception(Marshal.GetLastWin32Error());

    // デフォルト言語を設定(英語)
    string languageTag = "040904B0";
    if (langSize >= 4)
    {
        int langId = BitConverter.ToInt32(Marshal.PtrToStructure<int>(langPtr), 0);
        languageTag = $"{langId:X8}";
    }

    // 必要なバージョン属性を取得
    var result = new Dictionary<string, string>();
    string[] requiredProps = { "CompanyName", "ProductName", "FileVersion" };
    
    foreach (string prop in requiredProps)
    {
        if (VerQueryValue(versionBuffer, $@"\StringFileInfo\{languageTag}\{prop}", out IntPtr valuePtr, out int valueSize))
        {
            result[prop] = Marshal.PtrToStringUni(valuePtr);
        }
    }
    return result;
}

タグ: C# Win32 API version.dll FileVersionInfo

8月18日 09:12 投稿