-
Notifications
You must be signed in to change notification settings - Fork 23
/
Copy pathversion.go
158 lines (134 loc) · 5.4 KB
/
version.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
// Licensed to Elasticsearch B.V. under one or more contributor
// license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright
// ownership. Elasticsearch B.V. licenses this file to you under
// the Apache License, Version 2.0 (the "License"); you may
// not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//go:build windows
// +build windows
package windows
import (
"errors"
"fmt"
"unsafe"
)
// Syscalls
//sys _GetFileVersionInfo(filename string, reserved uint32, dataLen uint32, data *byte) (success bool, err error) [!success] = version.GetFileVersionInfoW
//sys _GetFileVersionInfoSize(filename string, handle uintptr) (size uint32, err error) = version.GetFileVersionInfoSizeW
//sys _VerQueryValueW(data *byte, subBlock string, pBuffer *uintptr, len *uint32) (success bool, err error) [!success] = version.VerQueryValueW
// FixedFileInfo contains version information for a file. This information is
// language and code page independent. This is an equivalent representation of
// VS_FIXEDFILEINFO.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms646997(v=vs.85).aspx
type FixedFileInfo 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
}
// ProductVersion returns the ProductVersion value in string format.
func (info FixedFileInfo) ProductVersion() string {
return fmt.Sprintf("%d.%d.%d.%d",
(info.ProductVersionMS >> 16),
(info.ProductVersionMS & 0xFFFF),
(info.ProductVersionLS >> 16),
(info.ProductVersionLS & 0xFFFF))
}
// FileVersion returns the FileVersion value in string format.
func (info FixedFileInfo) FileVersion() string {
return fmt.Sprintf("%d.%d.%d.%d",
(info.FileVersionMS >> 16),
(info.FileVersionMS & 0xFFFF),
(info.FileVersionLS >> 16),
(info.FileVersionLS & 0xFFFF))
}
// VersionData is a buffer holding the data returned by GetFileVersionInfo.
type VersionData []byte
// QueryValue uses VerQueryValue to query version information from the a
// version-information resource. It returns responses using the first language
// and code point found in the resource. The accepted keys are listed in
// the VerQueryValue documentation (e.g. ProductVersion, FileVersion, etc.).
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx
func (d VersionData) QueryValue(key string) (string, error) {
type LangAndCodePage struct {
Language uint16
CodePage uint16
}
var dataPtr uintptr
var size uint32
if _, err := _VerQueryValueW(&d[0], `\VarFileInfo\Translation`, &dataPtr, &size); err != nil || size == 0 {
return "", fmt.Errorf("failed to get list of languages: %w", err)
}
offset := int(dataPtr - (uintptr)(unsafe.Pointer(&d[0])))
if offset <= 0 || offset > len(d)-1 {
return "", errors.New("invalid address")
}
l := *(*LangAndCodePage)(unsafe.Pointer(&d[offset]))
subBlock := fmt.Sprintf(`\StringFileInfo\%04x%04x\%v`, l.Language, l.CodePage, key)
if _, err := _VerQueryValueW(&d[0], subBlock, &dataPtr, &size); err != nil || size == 0 {
return "", fmt.Errorf("failed to query %v: %w", subBlock, err)
}
offset = int(dataPtr - (uintptr)(unsafe.Pointer(&d[0])))
if offset <= 0 || offset > len(d)-1 {
return "", errors.New("invalid address")
}
str, _, err := UTF16BytesToString(d[offset : offset+int(size)*2])
if err != nil {
return "", fmt.Errorf("failed to decode UTF16 data: %w", err)
}
return str, nil
}
// FixedFileInfo returns the fixed version information from a
// version-information resource. It queries the root block to get the
// VS_FIXEDFILEINFO value.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647464(v=vs.85).aspx
func (d VersionData) FixedFileInfo() (*FixedFileInfo, error) {
if len(d) == 0 {
return nil, errors.New("use GetFileVersionInfo to initialize VersionData")
}
var dataPtr uintptr
var size uint32
if _, err := _VerQueryValueW(&d[0], `\`, &dataPtr, &size); err != nil {
return nil, fmt.Errorf("VerQueryValue failed for \\: %w", err)
}
offset := int(dataPtr - (uintptr)(unsafe.Pointer(&d[0])))
if offset <= 0 || offset > len(d)-1 {
return nil, errors.New("invalid address")
}
// Make a copy of the struct.
ffi := *(*FixedFileInfo)(unsafe.Pointer(&d[offset]))
return &ffi, nil
}
// GetFileVersionInfo retrieves version information for the specified file.
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms647003(v=vs.85).aspx
func GetFileVersionInfo(filename string) (VersionData, error) {
size, err := _GetFileVersionInfoSize(filename, 0)
if err != nil {
return nil, fmt.Errorf("GetFileVersionInfoSize failed: %w", err)
}
data := make(VersionData, size)
_, err = _GetFileVersionInfo(filename, 0, uint32(len(data)), &data[0])
if err != nil {
return nil, fmt.Errorf("GetFileVersionInfo failed: %w", err)
}
return data, nil
}