From b1ac97db88406492de5618ad0ad783867f74e281 Mon Sep 17 00:00:00 2001 From: Maksim An Date: Mon, 13 Apr 2026 16:48:13 -0700 Subject: [PATCH] Log cimwriter.dll version on load Add getFileVersion/formatFileVersion helpers to extract the PE file version from the loaded DLL and include it in the existing log line. Add unit tests for version extraction. Ref: https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo Signed-off-by: Maksim An --- internal/winapi/cimwriter/cimwriter.go | 37 +++++++++++++++- internal/winapi/cimwriter/cimwriter_test.go | 47 +++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 internal/winapi/cimwriter/cimwriter_test.go diff --git a/internal/winapi/cimwriter/cimwriter.go b/internal/winapi/cimwriter/cimwriter.go index 5d58e487d6..d8f4d4dc12 100644 --- a/internal/winapi/cimwriter/cimwriter.go +++ b/internal/winapi/cimwriter/cimwriter.go @@ -3,7 +3,9 @@ package cimwriter import ( + "fmt" "sync" + "unsafe" "github.com/sirupsen/logrus" "golang.org/x/sys/windows" @@ -53,7 +55,12 @@ var load = sync.OnceValue(func() error { var buf [windows.MAX_PATH]uint16 n, _ := windows.GetModuleFileName(windows.Handle(modcimwriter.Handle()), &buf[0], uint32(len(buf))) if n > 0 { - logrus.WithField("path", windows.UTF16ToString(buf[:n])).Info("loaded cimwriter.dll") + path := windows.UTF16ToString(buf[:n]) + fields := logrus.Fields{"path": path} + if ver, err := getFileVersion(path); err == nil { + fields["version"] = ver + } + logrus.WithFields(fields).Info("loaded cimwriter.dll") } return nil }) @@ -62,3 +69,31 @@ var load = sync.OnceValue(func() error { func Supported() bool { return load() == nil } + +// getFileVersion returns the file version string (e.g. "10.0.26100.1") for the given path. +func getFileVersion(path string) (string, error) { + size, err := windows.GetFileVersionInfoSize(path, nil) + if err != nil { + return "", err + } + data := make([]byte, size) + if err := windows.GetFileVersionInfo(path, 0, size, unsafe.Pointer(&data[0])); err != nil { + return "", err + } + var info *windows.VS_FIXEDFILEINFO + var infoLen uint32 + if err := windows.VerQueryValue(unsafe.Pointer(&data[0]), `\`, unsafe.Pointer(&info), &infoLen); err != nil { + return "", err + } + return formatFileVersion(info), nil +} + +// formatFileVersion formats the file version from a VS_FIXEDFILEINFO as "major.minor.build.revision". +// See https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo +func formatFileVersion(info *windows.VS_FIXEDFILEINFO) string { + major := info.FileVersionMS >> 16 + minor := info.FileVersionMS & 0xffff + build := info.FileVersionLS >> 16 + revision := info.FileVersionLS & 0xffff + return fmt.Sprintf("%d.%d.%d.%d", major, minor, build, revision) +} diff --git a/internal/winapi/cimwriter/cimwriter_test.go b/internal/winapi/cimwriter/cimwriter_test.go new file mode 100644 index 0000000000..49ce50a992 --- /dev/null +++ b/internal/winapi/cimwriter/cimwriter_test.go @@ -0,0 +1,47 @@ +//go:build windows + +package cimwriter + +import ( + "os" + "regexp" + "testing" +) + +func TestGetFileVersion_Kernel32(t *testing.T) { + // kernel32.dll is always present and always has version info. + path := os.Getenv("SystemRoot") + `\System32\kernel32.dll` + ver, err := getFileVersion(path) + if err != nil { + t.Fatalf("getFileVersion(%q) failed: %v", path, err) + } + // Version should match "major.minor.build.revision" pattern. + matched, _ := regexp.MatchString(`^\d+\.\d+\.\d+\.\d+$`, ver) + if !matched { + t.Fatalf("unexpected version format: %q", ver) + } + t.Logf("kernel32.dll version: %s", ver) +} + +func TestGetFileVersion_NonexistentFile(t *testing.T) { + _, err := getFileVersion(`C:\nonexistent_path\fake.dll`) + if err == nil { + t.Fatal("expected error for nonexistent file, got nil") + } +} + +func TestGetFileVersion_CimwriterDLL(t *testing.T) { + if !Supported() { + t.Skip("cimwriter.dll is not supported on this system") + } + path := os.Getenv("SystemRoot") + `\System32\cimwriter.dll` + ver, err := getFileVersion(path) + if err != nil { + t.Fatalf("getFileVersion(%q) failed: %v", path, err) + } + matched, _ := regexp.MatchString(`^\d+\.\d+\.\d+\.\d+$`, ver) + if !matched { + t.Fatalf("unexpected version format: %q", ver) + } + t.Logf("cimwriter.dll version: %s", ver) +}