From 3137cd9c5dd0eaa7e29cbc6de55144006b9f24cf Mon Sep 17 00:00:00 2001 From: MashaSamoylova Date: Fri, 23 Aug 2019 11:16:58 +0100 Subject: [PATCH 1/7] Add package for extracting version info --- README.md | Bin 38 -> 1983 bytes examples/file_info/main.go | 34 +++++ explorer_properties.png | Bin 0 -> 23710 bytes go.mod | 8 + go.sum | 4 + version_info.go | 306 +++++++++++++++++++++++++++++++++++++ 6 files changed, 352 insertions(+) create mode 100644 examples/file_info/main.go create mode 100644 explorer_properties.png create mode 100644 go.mod create mode 100644 go.sum create mode 100644 version_info.go diff --git a/README.md b/README.md index 9dadb6f05642c38cc5df36b5b26a2667c3450b83..f0a4c01a0e6dcbb0062a471b3ce06b574a188748 100644 GIT binary patch literal 1983 zcmb7F?`{)05dYjrd50nWl89zErMc4)0TKtTgoH{31qoHCalEs;zItuT_9p3_crrfl zNW21L@21&QsSXrvygQyhzZuW`{NvmALn!n(7ZR6fozSY^3%?7-Z&-ma@9fOMSiKY( zI#^j|3|h#wg_Tg5UO8y($3l^`VxD|~MW<`aG3olmwYQ9W2fh>zy6U;r6@-Yhp#SLV z%dqs`xLKMO!k6_T;krr}V*IaGnC`+(7g8_M7Q=OLdQFZby`;D{Qd_jw1fM{Afi5ws z7&V#pdxwW`x@MJ;=psxjl;30%;X%YDl&O70*YC~e^Fm9eihj?um8zJCwG)-m*2A#h zI~e4ZAB0uvBCgue5(NL7PE_w`Wo(JOC_~cD;3^G&q!V+IiHhW7!+iN-K+X@o(vu$nS_`x1 zG5NN{$vLhzA+C5Ym3McC62JC%59mK( zO!m^U3@c`DTx61NrT6XcaYk*iWFG&nh0GfBu4IqF2ZLNN*)ix!q5-u~n3m*qU72B? zoQXAN0rVT<20rT!qM-!O0>;PHn2M@0X> zU7o>Ydi-=UT}%ceNK<%UJ16LH9z$ECUniWlMnv=H+6Rumq$KGYW1xvC;{iG-sU~z= z6tH3rxHf_gH`4x9a(;RVXO;$ZrR@z&lfSmLv6!Pp#W&a7ZkY~0+&Yg{>d-C)>7By1JE1uf?v5rWfAT-3Y|7a2Bs+PUA8jL2m;3}0s*R)o literal 38 qcmezWPnki1A)O(gL6;$oA(J78A(f#Fh>IACfxLW%JO*9{E(QR+x(ERP diff --git a/examples/file_info/main.go b/examples/file_info/main.go new file mode 100644 index 0000000..d88f668 --- /dev/null +++ b/examples/file_info/main.go @@ -0,0 +1,34 @@ +package main + +import ( + "fmt" + "log" + "os" + + "github.com/bi-zone/go-fileversion" + "github.com/davecgh/go-spew/spew" +) + +func main() { + if len(os.Args) < 2 { + log.Fatal("Usage: ./file_info.exe ") + } + f, err := fileversion.New(os.Args[1]) + if err != nil { + log.Fatal(err) + } + fmt.Println("CompanyName:", f.CompanyName()) + fmt.Println("FileDescription:", f.FileDescription()) + fmt.Println("FileVersion:", f.FileVersion()) + fmt.Println("InternalName:", f.InternalName()) + fmt.Println("LegalCopyright:", f.LegalCopyright()) + fmt.Println("OriginalFilename:", f.OriginalFilename()) + fmt.Println("ProductName:", f.ProductName()) + fmt.Println("ProductVersion:", f.ProductVersion()) + fmt.Println("Comments:", f.Comments()) + fmt.Println("FileVeLegalTrademarksrsion:", f.FileVeLegalTrademarksrsion()) + fmt.Println("PrivateBuild:", f.PrivateBuild()) + fmt.Println("SpecialBuild:", f.SpecialBuild()) + + spew.Dump(f.FixedFileInfo) +} diff --git a/explorer_properties.png b/explorer_properties.png new file mode 100644 index 0000000000000000000000000000000000000000..e76cb1c3b161f9d643c81e3b53db1f07691949cf GIT binary patch literal 23710 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 literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dd01512 --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/bi-zone/go-fileversion + +go 1.12 + +require ( + github.com/davecgh/go-spew v1.1.1 + golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2dc5806 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/version_info.go b/version_info.go new file mode 100644 index 0000000..667a585 --- /dev/null +++ b/version_info.go @@ -0,0 +1,306 @@ +package fileversion + +import ( + "fmt" + "syscall" + "unsafe" + + "golang.org/x/xerrors" +) + +// FixedFileInfo contains info from VS_FIXEDFILEINFO windows structure. +type FixedFileInfo struct { + FileMajor uint16 + FileMinor uint16 + FileBuild uint16 + FilePrivate uint16 + ProductMajor uint16 + ProductMinor uint16 + ProductBuild uint16 + ProductPrivate uint16 + FileFlagsMask uint32 + FileFlags uint32 + FileOs uint32 + FileType uint32 + FileSubType uint32 + FileDateMS uint32 + FileDateLS uint32 +} + +type Locale string + +var EnglishUSAscii = Locale("040904E4") // US English + CP_USASCII +var EnglishUnicode = Locale("040904B0") // US English + CP_UNICODE +var EnglishUnknown = Locale("04090000") // US English + unknown codepage + +// Info contains windows object, which is being used +// for getting file version properties. +type Info struct { + data []byte + FixedFileInfo FixedFileInfo + pageID Locale +} + +var ( + version = syscall.NewLazyDLL("version.dll") + getFileVersionInfoSizeProc = version.NewProc("GetFileVersionInfoSizeW") + getFileVersionInfoProc = version.NewProc("GetFileVersionInfoW") + verQueryValueProc = version.NewProc("VerQueryValueW") +) + +// CompanyName returns CompanyName property. +func (f Info) CompanyName() string { + return f.GetProperty("CompanyName") +} + +// FileDescription returns FileDescription property. +func (f Info) FileDescription() string { + return f.GetProperty("FileDescription") +} + +// FileVersion returns FileVersion property. +func (f Info) FileVersion() string { + return f.GetProperty("FileVersion") +} + +// InternalName returns InternalName property. +func (f Info) InternalName() string { + return f.GetProperty("InternalName") +} + +// LegalCopyright returns LegalCopyright property. +func (f Info) LegalCopyright() string { + return f.GetProperty("LegalCopyright") +} + +// OriginalFilename returns OriginalFilename property. +func (f Info) OriginalFilename() string { + return f.GetProperty("OriginalFilename") +} + +// ProductName returns ProductName property. +func (f Info) ProductName() string { + return f.GetProperty("ProductName") +} + +// ProductVersion returns ProductVersion property. +func (f Info) ProductVersion() string { + return f.GetProperty("ProductVersion") +} + +// Comments returns Comments property. +func (f Info) Comments() string { + return f.GetProperty("Comments") +} + +// FileVeLegalTrademarksrsion returns FileVeLegalTrademarksrsion property. +func (f Info) FileVeLegalTrademarksrsion() string { + return f.GetProperty("LegalTrademarks") +} + +// PrivateBuild returns PrivateBuild property. +func (f Info) PrivateBuild() string { + return f.GetProperty("PrivateBuild") +} + +// SpecialBuild returns SpecialBuild property. +func (f Info) SpecialBuild() string { + return f.GetProperty("SpecialBuild") +} + +// New creates Info instance with default locale. +func New(path string) (Info, error) { + info, err := newWithoutLocale(path) + if err != nil { + return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) + } + + pageID := info.GetPageID() + info.pageID = pageID + + return info, nil +} + +// NewWithLocale creates Info instance with user-defined locale. +func NewWithLocale(path string, pageID Locale) (Info, error) { + info, err := newWithoutLocale(path) + if err != nil { + return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) + } + + info.pageID = pageID + + return info, nil +} + +func newWithoutLocale(path string) (Info, error) { + pathPtr, err := syscall.UTF16PtrFromString(path) + if err != nil { + return Info{}, xerrors.Errorf("failed to convert image path to utf16; %s", err) + } + size, _, err := getFileVersionInfoSizeProc.Call( + uintptr(unsafe.Pointer(pathPtr)), + 0, + ) + if size == 0 { + return Info{}, xerrors.Errorf("failed to get memory size for VersionInfo slice; %s", err) + } + info := make([]byte, size) + ret, _, err := getFileVersionInfoProc.Call( + uintptr(unsafe.Pointer(pathPtr)), + 0, + uintptr(len(info)), + uintptr(unsafe.Pointer(&info[0])), + ) + if ret == 0 { + return Info{}, xerrors.Errorf("failed to get VersionInfo from windows; %s", err) + } + + vi := Info{data: info} + + fixedInfo := vi.GetFixedInfo() + vi.FixedFileInfo = fixedInfo + + return vi, nil +} + +// GetPageID gets pageID filed in structure. +// It tries to get pageID from VersionInfo data. +// If getting ends with fail, it returns default pageID +// 040904E4 // US English + CP_USASCII. +func (f Info) GetPageID() Locale { + data, err := f.verQueryValue(`\VarFileInfo\Translation`, false) + if err != nil || len(data) == 0 { + return EnglishUSAscii + } + + // each pageID consists of a 16-bit language ID and a 16-bit code page + type langAndCodePage struct { + Language uint16 + CodePage uint16 + } + if len(data)%int(unsafe.Sizeof(langAndCodePage{})) != 0 { + return EnglishUSAscii + } + n := len(data) / int(unsafe.Sizeof(langAndCodePage{})) + if n == 0 { + return EnglishUSAscii + } + + ids := (*[1 << 28]langAndCodePage)(unsafe.Pointer(&data[0]))[:n:n] + return Locale(fmt.Sprintf("%04x%04x", ids[0].Language, ids[0].CodePage)) +} + +// GetFixedInfo returns FixedFileInfo structure, which constructs from windows +// VS_FIXEDFILEINFO structure. +// source: +// https://helloacm.com/c-function-to-get-file-version-using-win32-api-ansi-and-unicode-version/ +func (f Info) GetFixedInfo() FixedFileInfo { + data, err := f.verQueryValue(`\`, false) + if err != nil { + return FixedFileInfo{} + } + // source: + // https://docs.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo + type rawFixedFileInfo struct { + Signature uint32 + StrucVersion uint32 + FileVersionMS uint32 + FileVersionLS uint32 + ProductVersionMS uint32 + ProductVersionLS uint32 + FileFlagsMask uint32 + FileFlags uint32 + FileOS uint32 + FileType uint32 + FileSubtype uint32 + FileDateMS uint32 + FileDateLS uint32 + } + vsFixedInfo := *((*rawFixedFileInfo)(unsafe.Pointer(&data[0]))) + return FixedFileInfo{ + FileMajor: uint16(vsFixedInfo.FileVersionMS >> 16), + FileMinor: uint16(vsFixedInfo.FileVersionMS & 0xffff), + FileBuild: uint16(vsFixedInfo.FileVersionLS >> 16), + FilePrivate: uint16(vsFixedInfo.FileVersionLS & 0xffff), + ProductMajor: uint16(vsFixedInfo.ProductVersionMS >> 16), + ProductMinor: uint16(vsFixedInfo.ProductVersionMS & 0xffff), + ProductBuild: uint16(vsFixedInfo.ProductVersionLS >> 16), + ProductPrivate: uint16(vsFixedInfo.ProductVersionLS & 0xffff), + FileFlagsMask: vsFixedInfo.FileFlagsMask, + FileFlags: vsFixedInfo.FileFlags, + FileOs: vsFixedInfo.FileOS, + FileType: vsFixedInfo.FileType, + FileSubType: vsFixedInfo.FileSubtype, + FileDateMS: vsFixedInfo.FileDateMS, + FileDateLS: vsFixedInfo.FileDateLS, + } +} + +// GetProperty returns string-property. +func (f Info) GetProperty(propertyName string) (property string) { + property, err := f.verQueryValueString(f.pageID, propertyName) + if err == nil { + return + } + // Some dlls might not contain correct codepage information. In this case we will fail during lookup. + // Explorer will take a few shots in dark by trying `defaultPageIDs`. + // Explorer also randomly guess 041D04B0=Swedish+CP_UNICODE and 040704B0=German+CP_UNICODE) sometimes. + // We will try to simulate similiar behavior here. + for _, id := range []Locale{EnglishUSAscii, EnglishUnicode, EnglishUnknown} { + if id == f.pageID { + continue + } + property, err = f.verQueryValueString(id, propertyName) + if err == nil { + return + } + } + return +} + +// verQueryValueString returns property with type UTF16. +func (f Info) verQueryValueString(pageID Locale, property string) (string, error) { + data, err := f.verQueryValue(`\StringFileInfo\`+string(pageID)+`\`+property, true) + if err != nil || len(data) == 0 { + return "", err + } + n := len(data) / 2 + u16 := (*[1 << 28]uint16)(unsafe.Pointer(&data[0]))[:n:n] + return syscall.UTF16ToString(u16), err +} + +// verQueryValue returns property data. +func (f Info) verQueryValue(property string, isUTF16String bool) ([]byte, error) { + var offset uintptr + var length uint + blockStart := uintptr(unsafe.Pointer(&f.data[0])) + propertyUTF16Ptr, err := syscall.UTF16PtrFromString(property) + if err != nil { + return nil, err + } + ret, _, err := verQueryValueProc.Call( + blockStart, + uintptr(unsafe.Pointer(propertyUTF16Ptr)), + uintptr(unsafe.Pointer(&offset)), + uintptr(unsafe.Pointer(&length)), + ) + if ret == 0 { + return nil, err + } + // We need calculate indexes of needed data in `f.data` memory. + // `end` depends on length, which can be represent in characters or in bytes + // source: `puLen` parameter in + // https://docs.microsoft.com/en-us/windows/win32/api/winver/nf-winver-verqueryvaluew + start := int(offset) - int(blockStart) + var end int + if isUTF16String { + end = start + int(2*length) // length represents in characters count in string + } else { + end = start + int(length) + } + if start < 0 || end > len(f.data) { + return nil, xerrors.New("Index out of array") + } + return f.data[start:end], nil +} From 57d7d11c741b27609fadb8a1e311e5ccab5ad5f6 Mon Sep 17 00:00:00 2001 From: MashaSamoylova Date: Fri, 23 Aug 2019 15:31:02 +0100 Subject: [PATCH 2/7] Change structures --- README.md | 53 +++-- .../explorer_properties.png | Bin examples/file_info/main.go | 12 +- go.mod | 2 +- go.sum | 6 +- version_info.go | 221 +++++++++++------- 6 files changed, 185 insertions(+), 109 deletions(-) rename explorer_properties.png => assets/explorer_properties.png (100%) diff --git a/README.md b/README.md index f0a4c01..608a0e1 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ Package `fileversion` provides wrapper for windows version-information resource. -Package extracts this information from file: +Using the package you can extract the following info: -![](https://github.com/bi-zone/go-fileversion/blob/version_info_fillinf/explorer_properties.png) +![](https://github.com/bi-zone/go-fileversion/blob/version_info_fillinf/assets/explorer_properties.png) @@ -21,7 +21,6 @@ import ( "os" "github.com/bi-zone/go-fileversion" - "github.com/davecgh/go-spew/spew" ) func main() { @@ -45,26 +44,50 @@ func main() { fmt.Println("PrivateBuild:", f.PrivateBuild()) fmt.Println("SpecialBuild:", f.SpecialBuild()) - spew.Dump(f.FixedFileInfo) + fmt.Printf("\n%+#v\n", f.GetFixedInfo()) + + fmt.Printf("%+#v\n", f.Locales) } ``` -You can create version info object with own-defined locale +You can choose the locale for getting property. + +Choose locale from object: ```golang -f, err := fileversion.New(os.Args[1], fileversion.EnglishUnicode) - if err != nil { - log.Fatal(err) - } +f, err := fileversion.New(os.Args[1]) +if err != nil { + log.Fatal(err) +} +if len(f.Locales) > 1 { + fmt.Println(f.GetPropertyWithLocale("PropertyName", f.Locales[len(f.Locales) - 1])) +} ``` -You can combine a locale with another language and another charset. Read the [docs](https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource). +Also you can get property with owen-defined locale: ```golang -f, err := fileversion.New(os.Args[1], "041904b0") // Russian + Unicode - if err != nil { - log.Fatal(err) +f, err := fileversion.New(os.Args[1]) +if err != nil { + log.Fatal(err) +} +germanLocale := fileversion.Locale{ + LangID: 0x0407, + CharsetID: fileversion.CSUnicode, } +fmt.Println(f.GetPropertyWithLocale("PropertyName", germanLocale)) ``` +But we don't recommend to do this :) If object doesn't have locale, +you will get an error. + + `f.GetProperty` method tries to get property with different locales given + windows features. +The idea of locales handling was copied from +[.NET Framework 4.8](https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/FileVersionInfo.cs,036c54a4aa10d39f,references) -## Notes -The idea of locales handling was copied from [.NET Framework 4.8](https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/FileVersionInfo.cs,036c54a4aa10d39f,references) +```golang +f, err := fileversion.New(os.Args[1]) +if err != nil { + log.Fatal(err) +} +f.GetProperty("PropertyName") +``` diff --git a/explorer_properties.png b/assets/explorer_properties.png similarity index 100% rename from explorer_properties.png rename to assets/explorer_properties.png diff --git a/examples/file_info/main.go b/examples/file_info/main.go index d88f668..3771d3f 100644 --- a/examples/file_info/main.go +++ b/examples/file_info/main.go @@ -6,7 +6,6 @@ import ( "os" "github.com/bi-zone/go-fileversion" - "github.com/davecgh/go-spew/spew" ) func main() { @@ -30,5 +29,14 @@ func main() { fmt.Println("PrivateBuild:", f.PrivateBuild()) fmt.Println("SpecialBuild:", f.SpecialBuild()) - spew.Dump(f.FixedFileInfo) + fmt.Printf("\n%+#v\n", f.GetFixedInfo()) + + fmt.Printf("%+#v\n", f.Locales) + + germanLocale := fileversion.Locale{ + LangID: 0x0407, + CharsetID: fileversion.CSUnicode, + } + fmt.Println(f.GetPropertyWithLocale("PropertyName", germanLocale)) + } diff --git a/go.mod b/go.mod index dd01512..7a799fa 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,6 @@ module github.com/bi-zone/go-fileversion go 1.12 require ( - github.com/davecgh/go-spew v1.1.1 + github.com/hashicorp/go-multierror v1.0.0 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 ) diff --git a/go.sum b/go.sum index 2dc5806..9496ea1 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,6 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/version_info.go b/version_info.go index 667a585..8817a92 100644 --- a/version_info.go +++ b/version_info.go @@ -5,19 +5,21 @@ import ( "syscall" "unsafe" + "github.com/hashicorp/go-multierror" "golang.org/x/xerrors" ) +type FileVersion struct { + Major uint16 + Minor uint16 + Patch uint16 + Build uint16 +} + // FixedFileInfo contains info from VS_FIXEDFILEINFO windows structure. type FixedFileInfo struct { - FileMajor uint16 - FileMinor uint16 - FileBuild uint16 - FilePrivate uint16 - ProductMajor uint16 - ProductMinor uint16 - ProductBuild uint16 - ProductPrivate uint16 + FileVersion + ProductVersion FileVersion FileFlagsMask uint32 FileFlags uint32 FileOs uint32 @@ -27,18 +29,33 @@ type FixedFileInfo struct { FileDateLS uint32 } -type Locale string +type LangID uint16 +type CharsetID uint16 + +// Locale defines a pair of a language ID and a charsetID. +// It can be either one of default locales or any of locales +// get from the file information using `.Locales()` method +type Locale struct { + LangID + CharsetID +} -var EnglishUSAscii = Locale("040904E4") // US English + CP_USASCII -var EnglishUnicode = Locale("040904B0") // US English + CP_UNICODE -var EnglishUnknown = Locale("04090000") // US English + unknown codepage +// LangID and CharsetID constants. +// More combinations you can find in windows docs or +// https://godoc.org/github.com/josephspurrier/goversioninfo#pkg-constants +const ( + LangEnglish = LangID(0x049) + + CSAscii = CharsetID(0x04e4) + CSUnicode = CharsetID(0x04B0) + CSUnknown = CharsetID(0x0000) +) // Info contains windows object, which is being used // for getting file version properties. type Info struct { - data []byte - FixedFileInfo FixedFileInfo - pageID Locale + data []byte + Locales []Locale } var ( @@ -50,62 +67,74 @@ var ( // CompanyName returns CompanyName property. func (f Info) CompanyName() string { - return f.GetProperty("CompanyName") + p, _ := f.GetProperty("CompanyName") + return p } // FileDescription returns FileDescription property. func (f Info) FileDescription() string { - return f.GetProperty("FileDescription") + p, _ := f.GetProperty("FileDescription") + return p } // FileVersion returns FileVersion property. func (f Info) FileVersion() string { - return f.GetProperty("FileVersion") + p, _ := f.GetProperty("FileVersion") + return p } // InternalName returns InternalName property. func (f Info) InternalName() string { - return f.GetProperty("InternalName") + p, _ := f.GetProperty("InternalName") + return p } // LegalCopyright returns LegalCopyright property. func (f Info) LegalCopyright() string { - return f.GetProperty("LegalCopyright") + p, _ := f.GetProperty("LegalCopyright") + return p } // OriginalFilename returns OriginalFilename property. func (f Info) OriginalFilename() string { - return f.GetProperty("OriginalFilename") + p, _ := f.GetProperty("OriginalFilename") + return p } // ProductName returns ProductName property. func (f Info) ProductName() string { - return f.GetProperty("ProductName") + p, _ := f.GetProperty("ProductName") + return p } // ProductVersion returns ProductVersion property. func (f Info) ProductVersion() string { - return f.GetProperty("ProductVersion") + p, _ := f.GetProperty("ProductVersion") + return p } // Comments returns Comments property. func (f Info) Comments() string { - return f.GetProperty("Comments") + p, _ := f.GetProperty("Comments") + return p } // FileVeLegalTrademarksrsion returns FileVeLegalTrademarksrsion property. func (f Info) FileVeLegalTrademarksrsion() string { - return f.GetProperty("LegalTrademarks") + p, _ := f.GetProperty("LegalTrademarks") + return p } // PrivateBuild returns PrivateBuild property. func (f Info) PrivateBuild() string { - return f.GetProperty("PrivateBuild") + p, _ := f.GetProperty("PrivateBuild") + return p } // SpecialBuild returns SpecialBuild property. func (f Info) SpecialBuild() string { - return f.GetProperty("SpecialBuild") + p, _ := f.GetProperty("SpecialBuild") + return p } // New creates Info instance with default locale. @@ -115,20 +144,11 @@ func New(path string) (Info, error) { return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) } - pageID := info.GetPageID() - info.pageID = pageID - - return info, nil -} - -// NewWithLocale creates Info instance with user-defined locale. -func NewWithLocale(path string, pageID Locale) (Info, error) { - info, err := newWithoutLocale(path) + locales, err := info.getLocales() if err != nil { - return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) + return Info{}, xerrors.Errorf("failed to get locales; %s", err) } - - info.pageID = pageID + info.Locales = locales return info, nil } @@ -157,38 +177,25 @@ func newWithoutLocale(path string) (Info, error) { } vi := Info{data: info} - - fixedInfo := vi.GetFixedInfo() - vi.FixedFileInfo = fixedInfo - return vi, nil } -// GetPageID gets pageID filed in structure. -// It tries to get pageID from VersionInfo data. -// If getting ends with fail, it returns default pageID -// 040904E4 // US English + CP_USASCII. -func (f Info) GetPageID() Locale { +// getLocales tries to get `Translation` property from VersionInfo data. +func (f Info) getLocales() ([]Locale, error) { data, err := f.verQueryValue(`\VarFileInfo\Translation`, false) if err != nil || len(data) == 0 { - return EnglishUSAscii + return nil, xerrors.Errorf("failed to get Translation property from windows object; %s", err) } - // each pageID consists of a 16-bit language ID and a 16-bit code page - type langAndCodePage struct { - Language uint16 - CodePage uint16 + if len(data)%int(unsafe.Sizeof(Locale{})) != 0 { + return nil, xerrors.New("get wrong locales len from windows object") } - if len(data)%int(unsafe.Sizeof(langAndCodePage{})) != 0 { - return EnglishUSAscii - } - n := len(data) / int(unsafe.Sizeof(langAndCodePage{})) + n := len(data) / int(unsafe.Sizeof(Locale{})) if n == 0 { - return EnglishUSAscii + return nil, xerrors.New("get empty locales array from windows object") } - - ids := (*[1 << 28]langAndCodePage)(unsafe.Pointer(&data[0]))[:n:n] - return Locale(fmt.Sprintf("%04x%04x", ids[0].Language, ids[0].CodePage)) + locales := (*[1 << 28]Locale)(unsafe.Pointer(&data[0]))[:n:n] + return locales, nil } // GetFixedInfo returns FixedFileInfo structure, which constructs from windows @@ -219,53 +226,89 @@ func (f Info) GetFixedInfo() FixedFileInfo { } vsFixedInfo := *((*rawFixedFileInfo)(unsafe.Pointer(&data[0]))) return FixedFileInfo{ - FileMajor: uint16(vsFixedInfo.FileVersionMS >> 16), - FileMinor: uint16(vsFixedInfo.FileVersionMS & 0xffff), - FileBuild: uint16(vsFixedInfo.FileVersionLS >> 16), - FilePrivate: uint16(vsFixedInfo.FileVersionLS & 0xffff), - ProductMajor: uint16(vsFixedInfo.ProductVersionMS >> 16), - ProductMinor: uint16(vsFixedInfo.ProductVersionMS & 0xffff), - ProductBuild: uint16(vsFixedInfo.ProductVersionLS >> 16), - ProductPrivate: uint16(vsFixedInfo.ProductVersionLS & 0xffff), - FileFlagsMask: vsFixedInfo.FileFlagsMask, - FileFlags: vsFixedInfo.FileFlags, - FileOs: vsFixedInfo.FileOS, - FileType: vsFixedInfo.FileType, - FileSubType: vsFixedInfo.FileSubtype, - FileDateMS: vsFixedInfo.FileDateMS, - FileDateLS: vsFixedInfo.FileDateLS, + FileVersion: FileVersion{ + Major: uint16(vsFixedInfo.FileVersionMS >> 16), + Minor: uint16(vsFixedInfo.FileVersionMS & 0xffff), + Patch: uint16(vsFixedInfo.FileVersionLS & 0xffff), + Build: uint16(vsFixedInfo.FileVersionLS >> 16), + }, + ProductVersion: FileVersion{ + Major: uint16(vsFixedInfo.ProductVersionMS >> 16), + Minor: uint16(vsFixedInfo.ProductVersionMS & 0xffff), + Patch: uint16(vsFixedInfo.ProductVersionLS & 0xffff), + Build: uint16(vsFixedInfo.ProductVersionLS >> 16), + }, + FileFlagsMask: vsFixedInfo.FileFlagsMask, + FileFlags: vsFixedInfo.FileFlags, + FileOs: vsFixedInfo.FileOS, + FileType: vsFixedInfo.FileType, + FileSubType: vsFixedInfo.FileSubtype, + FileDateMS: vsFixedInfo.FileDateMS, + FileDateLS: vsFixedInfo.FileDateLS, } } +var defaultLocales = []Locale{ + { + LangID: LangEnglish, + CharsetID: CSAscii, + }, + { + LangID: LangEnglish, + CharsetID: CSUnicode, + }, + { + LangID: LangEnglish, + CharsetID: CSUnknown, + }, +} + // GetProperty returns string-property. -func (f Info) GetProperty(propertyName string) (property string) { - property, err := f.verQueryValueString(f.pageID, propertyName) - if err == nil { - return +func (f Info) GetProperty(propertyName string) (string, error) { + var resErr error + if len(f.Locales) != 0 { + property, err := f.GetPropertyWithLocale(propertyName, f.Locales[0]) + if err == nil { + return property, nil + } + resErr = multierror.Append(resErr, err) } // Some dlls might not contain correct codepage information. In this case we will fail during lookup. // Explorer will take a few shots in dark by trying `defaultPageIDs`. // Explorer also randomly guess 041D04B0=Swedish+CP_UNICODE and 040704B0=German+CP_UNICODE) sometimes. // We will try to simulate similiar behavior here. - for _, id := range []Locale{EnglishUSAscii, EnglishUnicode, EnglishUnknown} { - if id == f.pageID { + for _, id := range defaultLocales { + if len(f.Locales) != 0 && id == f.Locales[0] { continue } - property, err = f.verQueryValueString(id, propertyName) + property, err := f.GetPropertyWithLocale(propertyName, id) if err == nil { - return + return property, nil } + resErr = multierror.Append(resErr, err) + } + return "", resErr +} + +// GetProperty returns string-property with user-defined locale. +func (f Info) GetPropertyWithLocale(propertyName string, locale Locale) (string, error) { + property, err := f.verQueryValueString(locale, propertyName) + if err != nil { + return "", xerrors.Errorf("failed to get property %q with locale %s", propertyName, locale) } - return + return property, nil } +var uint16Size = int(unsafe.Sizeof(uint16(0))) + // verQueryValueString returns property with type UTF16. -func (f Info) verQueryValueString(pageID Locale, property string) (string, error) { - data, err := f.verQueryValue(`\StringFileInfo\`+string(pageID)+`\`+property, true) +func (f Info) verQueryValueString(locale Locale, property string) (string, error) { + localeStr := fmt.Sprintf("%04x%04x", locale.LangID, locale.CharsetID) + data, err := f.verQueryValue(`\StringFileInfo\`+localeStr+`\`+property, true) if err != nil || len(data) == 0 { return "", err } - n := len(data) / 2 + n := len(data) / uint16Size u16 := (*[1 << 28]uint16)(unsafe.Pointer(&data[0]))[:n:n] return syscall.UTF16ToString(u16), err } @@ -295,7 +338,7 @@ func (f Info) verQueryValue(property string, isUTF16String bool) ([]byte, error) start := int(offset) - int(blockStart) var end int if isUTF16String { - end = start + int(2*length) // length represents in characters count in string + end = start + uint16Size*int(length) // length represents in characters count in string } else { end = start + int(length) } From 19bad0fc1c6bb01ad1ee8ccff92cb54ff1b03681 Mon Sep 17 00:00:00 2001 From: MashaSamoylova Date: Mon, 26 Aug 2019 06:56:26 +0100 Subject: [PATCH 3/7] Some fixes --- examples/file_info/main.go | 6 ++++-- go.mod | 5 +---- go.sum | 4 ---- version_info.go | 23 +++++++++++++++-------- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/examples/file_info/main.go b/examples/file_info/main.go index 3771d3f..aabd986 100644 --- a/examples/file_info/main.go +++ b/examples/file_info/main.go @@ -29,7 +29,10 @@ func main() { fmt.Println("PrivateBuild:", f.PrivateBuild()) fmt.Println("SpecialBuild:", f.SpecialBuild()) - fmt.Printf("\n%+#v\n", f.GetFixedInfo()) + fixedInfo := f.GetFixedInfo() + fmt.Printf("\n%+#v\n", fixedInfo) + fmt.Println("File version:", fixedInfo.FileVersion) + fmt.Println("Product version:", fixedInfo.ProductVersion) fmt.Printf("%+#v\n", f.Locales) @@ -38,5 +41,4 @@ func main() { CharsetID: fileversion.CSUnicode, } fmt.Println(f.GetPropertyWithLocale("PropertyName", germanLocale)) - } diff --git a/go.mod b/go.mod index 7a799fa..47c5542 100644 --- a/go.mod +++ b/go.mod @@ -2,7 +2,4 @@ module github.com/bi-zone/go-fileversion go 1.12 -require ( - github.com/hashicorp/go-multierror v1.0.0 - golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 -) +require golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 diff --git a/go.sum b/go.sum index 9496ea1..d087b1a 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,2 @@ -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/version_info.go b/version_info.go index 8817a92..693cb95 100644 --- a/version_info.go +++ b/version_info.go @@ -5,10 +5,10 @@ import ( "syscall" "unsafe" - "github.com/hashicorp/go-multierror" "golang.org/x/xerrors" ) +// FileVersion consists info about version. type FileVersion struct { Major uint16 Minor uint16 @@ -16,6 +16,11 @@ type FileVersion struct { Build uint16 } +// String returns a string representation of the version. +func (f FileVersion) String() string { + return fmt.Sprintf("%d.%d.%d.%d", f.Major, f.Minor, f.Patch, f.Build) +} + // FixedFileInfo contains info from VS_FIXEDFILEINFO windows structure. type FixedFileInfo struct { FileVersion @@ -29,15 +34,20 @@ type FixedFileInfo struct { FileDateLS uint32 } +// LangID is a language id, should set considering +// https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource type LangID uint16 + +// CharsetId is character-set identifier, should set considering +// https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource type CharsetID uint16 // Locale defines a pair of a language ID and a charsetID. // It can be either one of default locales or any of locales // get from the file information using `.Locales()` method type Locale struct { - LangID - CharsetID + LangID LangID + CharsetID CharsetID } // LangID and CharsetID constants. @@ -265,13 +275,11 @@ var defaultLocales = []Locale{ // GetProperty returns string-property. func (f Info) GetProperty(propertyName string) (string, error) { - var resErr error if len(f.Locales) != 0 { property, err := f.GetPropertyWithLocale(propertyName, f.Locales[0]) if err == nil { return property, nil } - resErr = multierror.Append(resErr, err) } // Some dlls might not contain correct codepage information. In this case we will fail during lookup. // Explorer will take a few shots in dark by trying `defaultPageIDs`. @@ -285,16 +293,15 @@ func (f Info) GetProperty(propertyName string) (string, error) { if err == nil { return property, nil } - resErr = multierror.Append(resErr, err) } - return "", resErr + return "", xerrors.Errorf("failed to get property %q", propertyName) } // GetProperty returns string-property with user-defined locale. func (f Info) GetPropertyWithLocale(propertyName string, locale Locale) (string, error) { property, err := f.verQueryValueString(locale, propertyName) if err != nil { - return "", xerrors.Errorf("failed to get property %q with locale %s", propertyName, locale) + return "", xerrors.Errorf("failed to get property %q with locale %#+v", propertyName, locale) } return property, nil } From a483fd5ac399367bc96181b76ad113adb45f78f8 Mon Sep 17 00:00:00 2001 From: Oleg Broslavsky Date: Mon, 26 Aug 2019 16:44:54 +0700 Subject: [PATCH 4/7] Rewrite doc comments a bit --- examples/file_info/main.go | 4 +- version_info.go | 250 +++++++++++++++++++++---------------- 2 files changed, 141 insertions(+), 113 deletions(-) diff --git a/examples/file_info/main.go b/examples/file_info/main.go index aabd986..c28228b 100644 --- a/examples/file_info/main.go +++ b/examples/file_info/main.go @@ -25,11 +25,11 @@ func main() { fmt.Println("ProductName:", f.ProductName()) fmt.Println("ProductVersion:", f.ProductVersion()) fmt.Println("Comments:", f.Comments()) - fmt.Println("FileVeLegalTrademarksrsion:", f.FileVeLegalTrademarksrsion()) + fmt.Println("LegalTrademarks:", f.LegalTrademarks()) fmt.Println("PrivateBuild:", f.PrivateBuild()) fmt.Println("SpecialBuild:", f.SpecialBuild()) - fixedInfo := f.GetFixedInfo() + fixedInfo := f.FixedInfo() fmt.Printf("\n%+#v\n", fixedInfo) fmt.Println("File version:", fixedInfo.FileVersion) fmt.Println("Product version:", fixedInfo.ProductVersion) diff --git a/version_info.go b/version_info.go index 693cb95..b4c495b 100644 --- a/version_info.go +++ b/version_info.go @@ -8,7 +8,7 @@ import ( "golang.org/x/xerrors" ) -// FileVersion consists info about version. +// FileVersion is a multi-component version. type FileVersion struct { Major uint16 Minor uint16 @@ -21,9 +21,12 @@ func (f FileVersion) String() string { return fmt.Sprintf("%d.%d.%d.%d", f.Major, f.Minor, f.Patch, f.Build) } -// FixedFileInfo contains info from VS_FIXEDFILEINFO windows structure. +// FixedFileInfo contains a "fixed" part of a file information (without any strings). +// +// Ref VS_FIXEDFILEINFO: +// https://docs.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo type FixedFileInfo struct { - FileVersion + FileVersion FileVersion ProductVersion FileVersion FileFlagsMask uint32 FileFlags uint32 @@ -34,24 +37,26 @@ type FixedFileInfo struct { FileDateLS uint32 } -// LangID is a language id, should set considering +// LangID is a Windows language identifier. Could be one of the codes listed in +// `langID` section of // https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource type LangID uint16 -// CharsetId is character-set identifier, should set considering +// CharsetId is character-set identifier. Could be one of the codes listed in +// `charsetID` section of // https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource type CharsetID uint16 -// Locale defines a pair of a language ID and a charsetID. -// It can be either one of default locales or any of locales -// get from the file information using `.Locales()` method +// Locale defines a pair of a language ID and a charsetID. It can be either any +// combination of predefined LangID and CharsetID or crafted manually suing +// values from https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource type Locale struct { LangID LangID CharsetID CharsetID } -// LangID and CharsetID constants. -// More combinations you can find in windows docs or +// The package defines a list of most commonly used LangID and CharsetID +// constant. More combinations you can find in windows docs or at // https://godoc.org/github.com/josephspurrier/goversioninfo#pkg-constants const ( LangEnglish = LangID(0x049) @@ -61,19 +66,58 @@ const ( CSUnknown = CharsetID(0x0000) ) -// Info contains windows object, which is being used -// for getting file version properties. +// DefaultLocales is a list of default Locale values. It's used as a fallback +// in a calls with automatic locales detection. +// +//nolint:gochecknoglobals +var DefaultLocales = []Locale{ + { + LangID: LangEnglish, + CharsetID: CSAscii, + }, + { + LangID: LangEnglish, + CharsetID: CSUnicode, + }, + { + LangID: LangEnglish, + CharsetID: CSUnknown, + }, +} + +// Info contains a transparent windows object, which is being used for getting +// file version resource properties. +// +// Locales is a list of locales defined for the object. For the Info created +// using New it's queried from `\VarFileInfo\Translation`, for ones created +// using NewWithLocale it's just the given locale. +// +// A translation for the any property value is automatically chosen from Locales +// and then from fileversion.DefaultLocales prior to to the list order. Use +// GetPropertyWithLocale for deterministic selection of the property translation. type Info struct { - data []byte Locales []Locale + data []byte } -var ( - version = syscall.NewLazyDLL("version.dll") - getFileVersionInfoSizeProc = version.NewProc("GetFileVersionInfoSizeW") - getFileVersionInfoProc = version.NewProc("GetFileVersionInfoW") - verQueryValueProc = version.NewProc("VerQueryValueW") -) +// New creates an Info instance. +// +// It queries a list of translations from the version-information resource and +// uses them as preferred translations for string properties. +func New(path string) (Info, error) { + info, err := newWithoutLocale(path) + if err != nil { + return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) + } + + if locales, err := info.getLocales(); err == nil { + info.Locales = locales + } else { + info.Locales = DefaultLocales + } + + return info, nil +} // CompanyName returns CompanyName property. func (f Info) CompanyName() string { @@ -129,8 +173,8 @@ func (f Info) Comments() string { return p } -// FileVeLegalTrademarksrsion returns FileVeLegalTrademarksrsion property. -func (f Info) FileVeLegalTrademarksrsion() string { +// LegalTrademarks returns LegalTrademarks property. +func (f Info) LegalTrademarks() string { p, _ := f.GetProperty("LegalTrademarks") return p } @@ -147,72 +191,11 @@ func (f Info) SpecialBuild() string { return p } -// New creates Info instance with default locale. -func New(path string) (Info, error) { - info, err := newWithoutLocale(path) - if err != nil { - return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) - } - - locales, err := info.getLocales() - if err != nil { - return Info{}, xerrors.Errorf("failed to get locales; %s", err) - } - info.Locales = locales - - return info, nil -} - -func newWithoutLocale(path string) (Info, error) { - pathPtr, err := syscall.UTF16PtrFromString(path) - if err != nil { - return Info{}, xerrors.Errorf("failed to convert image path to utf16; %s", err) - } - size, _, err := getFileVersionInfoSizeProc.Call( - uintptr(unsafe.Pointer(pathPtr)), - 0, - ) - if size == 0 { - return Info{}, xerrors.Errorf("failed to get memory size for VersionInfo slice; %s", err) - } - info := make([]byte, size) - ret, _, err := getFileVersionInfoProc.Call( - uintptr(unsafe.Pointer(pathPtr)), - 0, - uintptr(len(info)), - uintptr(unsafe.Pointer(&info[0])), - ) - if ret == 0 { - return Info{}, xerrors.Errorf("failed to get VersionInfo from windows; %s", err) - } - - vi := Info{data: info} - return vi, nil -} - -// getLocales tries to get `Translation` property from VersionInfo data. -func (f Info) getLocales() ([]Locale, error) { - data, err := f.verQueryValue(`\VarFileInfo\Translation`, false) - if err != nil || len(data) == 0 { - return nil, xerrors.Errorf("failed to get Translation property from windows object; %s", err) - } - - if len(data)%int(unsafe.Sizeof(Locale{})) != 0 { - return nil, xerrors.New("get wrong locales len from windows object") - } - n := len(data) / int(unsafe.Sizeof(Locale{})) - if n == 0 { - return nil, xerrors.New("get empty locales array from windows object") - } - locales := (*[1 << 28]Locale)(unsafe.Pointer(&data[0]))[:n:n] - return locales, nil -} - -// GetFixedInfo returns FixedFileInfo structure, which constructs from windows -// VS_FIXEDFILEINFO structure. -// source: -// https://helloacm.com/c-function-to-get-file-version-using-win32-api-ansi-and-unicode-version/ -func (f Info) GetFixedInfo() FixedFileInfo { +// FixedInfo returns a fixed (non-string) part of the file version-information +// resource. Contains file and product versions. +// +// Ref: https://helloacm.com/c-function-to-get-file-version-using-win32-api-ansi-and-unicode-version/ +func (f Info) FixedInfo() FixedFileInfo { data, err := f.verQueryValue(`\`, false) if err != nil { return FixedFileInfo{} @@ -258,25 +241,15 @@ func (f Info) GetFixedInfo() FixedFileInfo { } } -var defaultLocales = []Locale{ - { - LangID: LangEnglish, - CharsetID: CSAscii, - }, - { - LangID: LangEnglish, - CharsetID: CSUnicode, - }, - { - LangID: LangEnglish, - CharsetID: CSUnknown, - }, -} - -// GetProperty returns string-property. +// GetProperty queries a string-property from version-information resource. +// +// Single property in a version-information resource can have multiple +// translations. GetProperty does its best trying to find an existing +// translation: it returns a first existing translation for any of .Locales +// and if failed tries to query it for locales from fileversion.DefaultLocales. func (f Info) GetProperty(propertyName string) (string, error) { - if len(f.Locales) != 0 { - property, err := f.GetPropertyWithLocale(propertyName, f.Locales[0]) + for _, id := range DefaultLocales { + property, err := f.GetPropertyWithLocale(propertyName, id) if err == nil { return property, nil } @@ -284,11 +257,8 @@ func (f Info) GetProperty(propertyName string) (string, error) { // Some dlls might not contain correct codepage information. In this case we will fail during lookup. // Explorer will take a few shots in dark by trying `defaultPageIDs`. // Explorer also randomly guess 041D04B0=Swedish+CP_UNICODE and 040704B0=German+CP_UNICODE) sometimes. - // We will try to simulate similiar behavior here. - for _, id := range defaultLocales { - if len(f.Locales) != 0 && id == f.Locales[0] { - continue - } + // We will try to simulate similar behavior here. + for _, id := range DefaultLocales { property, err := f.GetPropertyWithLocale(propertyName, id) if err == nil { return property, nil @@ -297,7 +267,11 @@ func (f Info) GetProperty(propertyName string) (string, error) { return "", xerrors.Errorf("failed to get property %q", propertyName) } -// GetProperty returns string-property with user-defined locale. +// GetProperty returns string-property with user-defined locale. It's the only +// way to get the property with the selected translation, all other methods +// do heuristics in translation choosing. +// +// See Locale, LangID and CharsetID docs for more info about locales. func (f Info) GetPropertyWithLocale(propertyName string, locale Locale) (string, error) { property, err := f.verQueryValueString(locale, propertyName) if err != nil { @@ -306,8 +280,17 @@ func (f Info) GetPropertyWithLocale(propertyName string, locale Locale) (string, return property, nil } +//nolint:gochecknoglobals var uint16Size = int(unsafe.Sizeof(uint16(0))) +//nolint:gochecknoglobals +var ( + version = syscall.NewLazyDLL("version.dll") + getFileVersionInfoSizeProc = version.NewProc("GetFileVersionInfoSizeW") + getFileVersionInfoProc = version.NewProc("GetFileVersionInfoW") + verQueryValueProc = version.NewProc("VerQueryValueW") +) + // verQueryValueString returns property with type UTF16. func (f Info) verQueryValueString(locale Locale, property string) (string, error) { localeStr := fmt.Sprintf("%04x%04x", locale.LangID, locale.CharsetID) @@ -354,3 +337,48 @@ func (f Info) verQueryValue(property string, isUTF16String bool) ([]byte, error) } return f.data[start:end], nil } + +func newWithoutLocale(path string) (Info, error) { + pathPtr, err := syscall.UTF16PtrFromString(path) + if err != nil { + return Info{}, xerrors.Errorf("failed to convert image path to utf16; %s", err) + } + size, _, err := getFileVersionInfoSizeProc.Call( + uintptr(unsafe.Pointer(pathPtr)), + 0, + ) + if size == 0 { + return Info{}, xerrors.Errorf("failed to get memory size for VersionInfo slice; %s", err) + } + info := make([]byte, size) + ret, _, err := getFileVersionInfoProc.Call( + uintptr(unsafe.Pointer(pathPtr)), + 0, + uintptr(len(info)), + uintptr(unsafe.Pointer(&info[0])), + ) + if ret == 0 { + return Info{}, xerrors.Errorf("failed to get VersionInfo from windows; %s", err) + } + + vi := Info{data: info} + return vi, nil +} + +// getLocales tries to get `Translation` property from VersionInfo data. +func (f Info) getLocales() ([]Locale, error) { + data, err := f.verQueryValue(`\VarFileInfo\Translation`, false) + if err != nil || len(data) == 0 { + return nil, xerrors.Errorf("failed to get Translation property from windows object; %s", err) + } + + if len(data)%int(unsafe.Sizeof(Locale{})) != 0 { + return nil, xerrors.New("get wrong locales len from windows object") + } + n := len(data) / int(unsafe.Sizeof(Locale{})) + if n == 0 { + return nil, xerrors.New("get empty locales array from windows object") + } + locales := (*[1 << 28]Locale)(unsafe.Pointer(&data[0]))[:n:n] + return locales, nil +} From aa3bbb5178738ca3558cd750f4b1e89fb9c15ac0 Mon Sep 17 00:00:00 2001 From: MashaSamoylova Date: Mon, 26 Aug 2019 11:10:53 +0100 Subject: [PATCH 5/7] Fix GetProperty; Change link to image in README.md --- README.md | 5 +++-- version_info.go | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 608a0e1..92fa04d 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,12 @@ # go-fileversion +[![GoDoc](https://godoc.org/github.com/bi-zone/go-fileversion?status.svg)](https://godoc.org/github.com/bi-zone/go-fileversion/) Package `fileversion` provides wrapper for windows version-information resource. Using the package you can extract the following info: -![](https://github.com/bi-zone/go-fileversion/blob/version_info_fillinf/assets/explorer_properties.png) +![](https://github.com/bi-zone/go-fileversion/assets/explorer_properties.png) @@ -44,7 +45,7 @@ func main() { fmt.Println("PrivateBuild:", f.PrivateBuild()) fmt.Println("SpecialBuild:", f.SpecialBuild()) - fmt.Printf("\n%+#v\n", f.GetFixedInfo()) + fmt.Printf("\n%+#v\n", f.FixedInfo()) fmt.Printf("%+#v\n", f.Locales) } diff --git a/version_info.go b/version_info.go index b4c495b..3fe22b7 100644 --- a/version_info.go +++ b/version_info.go @@ -248,7 +248,7 @@ func (f Info) FixedInfo() FixedFileInfo { // translation: it returns a first existing translation for any of .Locales // and if failed tries to query it for locales from fileversion.DefaultLocales. func (f Info) GetProperty(propertyName string) (string, error) { - for _, id := range DefaultLocales { + for _, id := range f.Locales { property, err := f.GetPropertyWithLocale(propertyName, id) if err == nil { return property, nil From f585964c1f1d73bc642e424af5c1f560fab7e46f Mon Sep 17 00:00:00 2001 From: Oleg Broslavsky Date: Mon, 26 Aug 2019 17:38:51 +0700 Subject: [PATCH 6/7] Add NewWithLocale function --- examples/file_info/main.go | 7 ++++--- version_info.go | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/examples/file_info/main.go b/examples/file_info/main.go index c28228b..7bfb3bc 100644 --- a/examples/file_info/main.go +++ b/examples/file_info/main.go @@ -30,14 +30,15 @@ func main() { fmt.Println("SpecialBuild:", f.SpecialBuild()) fixedInfo := f.FixedInfo() - fmt.Printf("\n%+#v\n", fixedInfo) + fmt.Printf("FixedInfo:\n%+v\n", fixedInfo) fmt.Println("File version:", fixedInfo.FileVersion) fmt.Println("Product version:", fixedInfo.ProductVersion) - fmt.Printf("%+#v\n", f.Locales) + fmt.Printf("Locales: %+v\n", f.Locales) + // https://docs.microsoft.com/en-us/windows/win32/menurc/versioninfo-resource germanLocale := fileversion.Locale{ - LangID: 0x0407, + LangID: 0x0407, // langID German CharsetID: fileversion.CSUnicode, } fmt.Println(f.GetPropertyWithLocale("PropertyName", germanLocale)) diff --git a/version_info.go b/version_info.go index 3fe22b7..66fdc46 100644 --- a/version_info.go +++ b/version_info.go @@ -119,6 +119,19 @@ func New(path string) (Info, error) { return info, nil } +// NewWithLocale creates an Info instance with a given locale. All the string +// properties translations will be firstly queried with the given locale. +// +// See GetPropertyWithLocale for exact properties querying. +func NewWithLocale(path string, locale Locale) (Info, error) { + info, err := newWithoutLocale(path) + if err != nil { + return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) + } + info.Locales = []Locale{locale} + return info, nil +} + // CompanyName returns CompanyName property. func (f Info) CompanyName() string { p, _ := f.GetProperty("CompanyName") @@ -267,9 +280,9 @@ func (f Info) GetProperty(propertyName string) (string, error) { return "", xerrors.Errorf("failed to get property %q", propertyName) } -// GetProperty returns string-property with user-defined locale. It's the only -// way to get the property with the selected translation, all other methods -// do heuristics in translation choosing. +// GetPropertyWithLocale returns string-property with user-defined locale. It's +// the only way to get the property with the selected translation, all other +// methods do heuristics in translation choosing. // // See Locale, LangID and CharsetID docs for more info about locales. func (f Info) GetPropertyWithLocale(propertyName string, locale Locale) (string, error) { From a3012811f99057379d3813230e38ec3f14dffa33 Mon Sep 17 00:00:00 2001 From: Oleg Broslavsky Date: Mon, 26 Aug 2019 17:50:46 +0700 Subject: [PATCH 7/7] Fix errors wrapping --- version_info.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/version_info.go b/version_info.go index 66fdc46..9b5c934 100644 --- a/version_info.go +++ b/version_info.go @@ -107,7 +107,7 @@ type Info struct { func New(path string) (Info, error) { info, err := newWithoutLocale(path) if err != nil { - return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) + return Info{}, xerrors.Errorf("failed to get VersionInfo: %w", err) } if locales, err := info.getLocales(); err == nil { @@ -126,7 +126,7 @@ func New(path string) (Info, error) { func NewWithLocale(path string, locale Locale) (Info, error) { info, err := newWithoutLocale(path) if err != nil { - return Info{}, xerrors.Errorf("failed to get VersionInfo; %s", err) + return Info{}, xerrors.Errorf("failed to get VersionInfo: %w", err) } info.Locales = []Locale{locale} return info, nil @@ -288,7 +288,7 @@ func (f Info) GetProperty(propertyName string) (string, error) { func (f Info) GetPropertyWithLocale(propertyName string, locale Locale) (string, error) { property, err := f.verQueryValueString(locale, propertyName) if err != nil { - return "", xerrors.Errorf("failed to get property %q with locale %#+v", propertyName, locale) + return "", xerrors.Errorf("failed to get property %q with locale %+v", propertyName, locale) } return property, nil } @@ -346,7 +346,7 @@ func (f Info) verQueryValue(property string, isUTF16String bool) ([]byte, error) end = start + int(length) } if start < 0 || end > len(f.data) { - return nil, xerrors.New("Index out of array") + return nil, xerrors.New("index out of range") } return f.data[start:end], nil } @@ -354,14 +354,14 @@ func (f Info) verQueryValue(property string, isUTF16String bool) ([]byte, error) func newWithoutLocale(path string) (Info, error) { pathPtr, err := syscall.UTF16PtrFromString(path) if err != nil { - return Info{}, xerrors.Errorf("failed to convert image path to utf16; %s", err) + return Info{}, xerrors.Errorf("failed to convert image path to utf16: %w", err) } size, _, err := getFileVersionInfoSizeProc.Call( uintptr(unsafe.Pointer(pathPtr)), 0, ) if size == 0 { - return Info{}, xerrors.Errorf("failed to get memory size for VersionInfo slice; %s", err) + return Info{}, xerrors.Errorf("failed to get memory size for VersionInfo slice: %w", err) } info := make([]byte, size) ret, _, err := getFileVersionInfoProc.Call( @@ -371,7 +371,7 @@ func newWithoutLocale(path string) (Info, error) { uintptr(unsafe.Pointer(&info[0])), ) if ret == 0 { - return Info{}, xerrors.Errorf("failed to get VersionInfo from windows; %s", err) + return Info{}, xerrors.Errorf("failed to get VersionInfo from windows: %w", err) } vi := Info{data: info} @@ -382,15 +382,15 @@ func newWithoutLocale(path string) (Info, error) { func (f Info) getLocales() ([]Locale, error) { data, err := f.verQueryValue(`\VarFileInfo\Translation`, false) if err != nil || len(data) == 0 { - return nil, xerrors.Errorf("failed to get Translation property from windows object; %s", err) + return nil, xerrors.Errorf("failed to get Translation property from a windows object: %w", err) } if len(data)%int(unsafe.Sizeof(Locale{})) != 0 { - return nil, xerrors.New("get wrong locales len from windows object") + return nil, xerrors.New("get wrong locales len in a windows object") } n := len(data) / int(unsafe.Sizeof(Locale{})) if n == 0 { - return nil, xerrors.New("get empty locales array from windows object") + return nil, xerrors.New("get empty locales array in a windows object") } locales := (*[1 << 28]Locale)(unsafe.Pointer(&data[0]))[:n:n] return locales, nil