plugins/cmd/query-hcs-capabilities/main.go (101 lines of code) (raw):

//go:build windows package main import ( "encoding/json" "errors" "fmt" "log" "os/exec" "strings" "unsafe" "golang.org/x/sys/windows" "golang.org/x/sys/windows/registry" ) var ( vmcompute = windows.NewLazyDLL("vmcompute.dll") hcsGetServiceProperties = vmcompute.NewProc("HcsGetServiceProperties") ) // HCS schema Version structure: <https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#version> type Version struct { Major uint32 Minor uint32 } // HCS schema BasicInformation structure: <https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#BasicInformation> type BasicInformation struct { SupportedSchemaVersions []*Version } // Modified version of the HCS schema ServiceProperties structure: <https://learn.microsoft.com/en-us/virtualization/api/hcs/schemareference#ServiceProperties> // Note that we just treat the array as containing BasicInformation objects, since that's what our specific query returns type ServiceProperties struct { Properties []*BasicInformation } func getSupportedSchemas() ([]*Version, error) { // Attempt to load vmcompute.dll if err := vmcompute.Load(); err != nil { return nil, fmt.Errorf("failed to load %s: %s", vmcompute.Name, err.Error()) } // Convert our query string into a UTF-16 pointer queryPtr, err := windows.UTF16PtrFromString("{\"PropertyTypes\": [\"Basic\"]}") if err != nil { return nil, fmt.Errorf("failed to convert string to UTF-16: %s", err.Error()) } // Call HcsGetServiceProperties() to query the supported schema version var resultPtr *uint16 = nil retval, _, _ := hcsGetServiceProperties.Call( uintptr(unsafe.Pointer(queryPtr)), uintptr(unsafe.Pointer(&resultPtr)), ) // Verify that the query was successful if retval != 0 { return nil, fmt.Errorf("HcsGetServiceProperties() failed: %v", windows.Errno(retval)) } // Convert the result into a JSON string result := windows.UTF16PtrToString((*uint16)(unsafe.Pointer(resultPtr))) // Parse the JSON serviceProperties := &ServiceProperties{} if err := json.Unmarshal([]byte(result), &serviceProperties); err != nil { return nil, err } // Verify that we have at least one supported schema version if len(serviceProperties.Properties) == 0 || len(serviceProperties.Properties[0].SupportedSchemaVersions) == 0 { return nil, errors.New("HcsGetServiceProperties() returned zero supported schema versions") } // Return the list of supported schema versions return serviceProperties.Properties[0].SupportedSchemaVersions, nil } func getWindowsVersion() (string, error) { // Use `RtlGetVersion()` to query the Windows version number, so manifest semantics are ignored versionInfo := windows.RtlGetVersion() // Use PowerShell to query WMI for the system caption, since `ProductName` in the registry is no longer reliable productName, err := exec.Command("powershell", "-Command", "(Get-WmiObject -Class Win32_OperatingSystem).Caption").Output() if err != nil { return "", fmt.Errorf("failed to query WMI for the product version string: %s", err.Error()) } // Open the registry key for the Windows version information key, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) if err != nil { return "", fmt.Errorf("failed to open the registry key \"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\": %s", err.Error()) } defer key.Close() // Attempt to retrieve the display version on newer systems, falling back to the old release ID value on older systems displayVersion, _, err := key.GetStringValue("DisplayVersion") if err != nil { displayVersion, _, err = key.GetStringValue("ReleaseId") if err != nil { return "", fmt.Errorf("failed to retrieve either the \"DisplayVersion\" or \"ReleaseId\" registry value: %s", err.Error()) } } // Retrieve the revision number, since this isn't included in the `RtlGetVersion()` output revisionNumber, _, err := key.GetIntegerValue("UBR") if err != nil { return "", fmt.Errorf("failed to retrieve the \"UBR\" registry value: %s", err.Error()) } // Build an aggregated version string from the retrieved values return fmt.Sprintf( "%s, version %s (OS build %d.%d.%d.%d)", strings.TrimSpace(string(productName)), displayVersion, versionInfo.MajorVersion, versionInfo.MinorVersion, versionInfo.BuildNumber, revisionNumber, ), nil } func main() { // Retrieve the Windows version information windowsVersion, err := getWindowsVersion() if err != nil { log.Fatalf("Failed to retrieve Windows version information: %s", err.Error()) } // Query the Host Compute Service (HCS) for the list of supported schema versions supportedSchemas, err := getSupportedSchemas() if err != nil { log.Fatalf("Failed to retrieve the supported HCS schema version: %s", err.Error()) } // Print the Windows version details and supported schema version fmt.Println("Operating system version:") fmt.Println(windowsVersion) fmt.Println() fmt.Println("Supported HCS schema versions:") for _, version := range supportedSchemas { fmt.Printf("- %d.%d", version.Major, version.Minor) } }