Search code examples
c#.netdll

Is there a way to update the file version of a dll file without rebuilding?


I have some .dll files that contain the wrong version numbers. I want to update these versions with the correct values without rebuilding. I found this UpdateResource function and am attempting to implement it using Platform Invoke.
Here is what I have so far:

public class UpdateBinaryResource {

class VersionResourceUpdater
{
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool UpdateResource(IntPtr hUpdate, IntPtr lpType, IntPtr lpName, ushort wLanguage, byte[] lpData, uint cbData);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);

    [StructLayout(LayoutKind.Sequential)]
    private struct VS_FIXEDFILEINFO
    {
     public uint dwSignature;
     public uint dwStrucVersion;
     public uint dwFileVersionMS;
     public uint dwFileVersionLS;
     public uint dwProductVersionMS;
     public uint dwProductVersionLS;
     public uint dwFileFlagsMask;
     public uint dwFileFlags;
     public uint dwFileOS;
     public uint dwFileType;
     public uint dwFileSubtype;
     public uint dwFileDateMS;
     public uint dwFileDateLS;
 }

    public static bool UpdateVersionInfo(string filePath, string productVersion, string fileVersion)
    {

        string[] fileVersionParts = fileVersion.Split('.');
        string[] productVersionParts = productVersion.Split('.');
        Console.WriteLine(fileVersionParts[0]);
        // combine parts to 32-bit uInt
        VS_FIXEDFILEINFO fileInfo = new VS_FIXEDFILEINFO
        {
            dwSignature = 0xFEEF04BD,
            dwStrucVersion = 0x00010000,
            dwFileVersionMS = (uint.Parse(fileVersionParts[0]) << 16) | uint.Parse(fileVersionParts[1]),
            dwFileVersionLS = (uint.Parse(fileVersionParts[2]) << 16) | uint.Parse(fileVersionParts[3]),
            dwProductVersionMS = (uint.Parse(productVersionParts[0]) << 16) | uint.Parse(productVersionParts[1]),
            dwProductVersionLS = (uint.Parse(productVersionParts[2]) << 16) | uint.Parse(productVersionParts[3]),
            dwFileFlagsMask = 0x3F,
            dwFileFlags = 0,
            dwFileOS = 0x40004,
            dwFileType = 0x1,
            dwFileSubtype = 0x0,
            dwFileDateMS = 0,
            dwFileDateLS = 0
        };
        Console.WriteLine(fileInfo.dwFileVersionMS);
        var fileInfoPtr = Marshal.AllocHGlobal(Marshal.SizeOf(fileInfo));
        Marshal.StructureToPtr(fileInfo, fileInfoPtr, false);

        IntPtr hUpdate = BeginUpdateResource(filePath, false);
        if (hUpdate == IntPtr.Zero)
        {
            int error = Marshal.GetLastWin32Error();
            throw new System.ComponentModel.Win32Exception(error, $"Failed to open file for updating: {filePath}. Error code: {error}");
        }

        byte[] fileInfoBytes = new byte[Marshal.SizeOf(fileInfo)];
        Marshal.Copy(fileInfoPtr, fileInfoBytes, 0, fileInfoBytes.Length);

        if (!UpdateResource(hUpdate, new IntPtr(16), new IntPtr(1), 0, fileInfoBytes, (uint)fileInfoBytes.Length))
        {
            int error = Marshal.GetLastWin32Error();
            EndUpdateResource(hUpdate, false);
            throw new System.ComponentModel.Win32Exception(error, $"Failed to update resource. Error code: {error}");
        }

        if (!EndUpdateResource(hUpdate, false))
        {
            int error = Marshal.GetLastWin32Error();
            throw new System.ComponentModel.Win32Exception(error, $"Failed to end resource update. Error code: {error}");
        }

        Marshal.FreeHGlobal(fileInfoPtr);

        return true;
    }
}
class Program
{
    static void Main(string[] args)
    {
        string dllPath =  @"C:\Users\user\Desktop\temp.dll";
        string productVer = "23.0.3.0";
        string fileVer = "23.0.3.1";

        var UpdateBinaryResource = VersionResourceUpdater.UpdateVersionInfo(dllPath, productVer, fileVer);

        Console.WriteLine("Success");
    }
}
}

After running this, I checked the details section in the properties of the dll file and found that it has only erased the values that were previously there instead of updating the versions with the new values.
Is it even possible to update the version numbers without rebuilding, and if so, can it be done through C#?


Solution

  • Needed this too. The only solution I found was that building my own VS_VERSION_INFO data structure in memory and pass the buffer to

    UpdateResource(hUpdate, RT_VERSION, MAKEINTRESOURCE(VS_VERSION_INFO), languageID, &buffer, buffer.size());
    

    It's pretty well explained with code on this Japanese blog (just use translate in your browser): https://espresso3389.hatenablog.com/entry/20100907/1283788958