-
Notifications
You must be signed in to change notification settings - Fork 25
/
fileversion.go
323 lines (290 loc) · 9.6 KB
/
fileversion.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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
/*
* Copyright (c) 2014-2017 MongoDB, Inc.
*
* Licensed 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.
*/
package gowin32
import (
"github.com/winlabs/gowin32/wrappers"
"errors"
"fmt"
"strconv"
"strings"
"syscall"
"unsafe"
)
type VerFileFlags uint32
const (
VerFileDebug VerFileFlags = wrappers.VS_FF_DEBUG
VerFilePrerelease VerFileFlags = wrappers.VS_FF_PRERELEASE
VerFilePatched VerFileFlags = wrappers.VS_FF_PATCHED
VerFilePrivateBuild VerFileFlags = wrappers.VS_FF_PRIVATEBUILD
VerFileInfoInferred VerFileFlags = wrappers.VS_FF_INFOINFERRED
VerFileSpecialBuild VerFileFlags = wrappers.VS_FF_SPECIALBUILD
)
type VerFileOS uint32
const (
VerFileOSUnknown VerFileOS = wrappers.VOS_UNKNOWN
VerFileDOS VerFileOS = wrappers.VOS_DOS
VerFileOS216 VerFileOS = wrappers.VOS_OS216
VerFileOS232 VerFileOS = wrappers.VOS_OS232
VerFileNT VerFileOS = wrappers.VOS_NT
VerFileWindows16 VerFileOS = wrappers.VOS__WINDOWS16
VerFilePM16 VerFileOS = wrappers.VOS__PM16
VerFilePM32 VerFileOS = wrappers.VOS__PM32
VerFileWindows32 VerFileOS = wrappers.VOS__WINDOWS32
VerFileDOSWindows16 VerFileOS = wrappers.VOS_DOS_WINDOWS16
VerFileDOSWindows32 VerFileOS = wrappers.VOS_DOS_WINDOWS32
VerFileOS216PM16 VerFileOS = wrappers.VOS_OS216_PM16
VerFileOS232PM32 VerFileOS = wrappers.VOS_OS232_PM32
VerFileNTWindows32 VerFileOS = wrappers.VOS_NT_WINDOWS32
)
type VerFileType uint32
const (
VerFileTypeUnknown VerFileType = wrappers.VFT_UNKNOWN
VerFileApp VerFileType = wrappers.VFT_APP
VerFileDLL VerFileType = wrappers.VFT_DLL
VerFileDriver VerFileType = wrappers.VFT_DRV
VerFileFont VerFileType = wrappers.VFT_FONT
VerFileVxD VerFileType = wrappers.VFT_VXD
VerFileStaticLib VerFileType = wrappers.VFT_STATIC_LIB
)
type VerFileSubtype uint32
const (
VerFileSubtypeUnknown VerFileSubtype = wrappers.VFT2_UNKNOWN
VerFileDriverPrinter VerFileSubtype = wrappers.VFT2_DRV_PRINTER
VerFileDriverKeyboard VerFileSubtype = wrappers.VFT2_DRV_KEYBOARD
VerFileDriverLanguage VerFileSubtype = wrappers.VFT2_DRV_LANGUAGE
VerFileDriverDisplay VerFileSubtype = wrappers.VFT2_DRV_DISPLAY
VerFileDriverMouse VerFileSubtype = wrappers.VFT2_DRV_MOUSE
VerFileDriverNetwork VerFileSubtype = wrappers.VFT2_DRV_NETWORK
VerFileDriverSystem VerFileSubtype = wrappers.VFT2_DRV_SYSTEM
VerFileDriverInstallable VerFileSubtype = wrappers.VFT2_DRV_INSTALLABLE
VerFileDriverSound VerFileSubtype = wrappers.VFT2_DRV_SOUND
VerFileDriverComm VerFileSubtype = wrappers.VFT2_DRV_COMM
VerFileDriverVersionedPrinter VerFileSubtype = wrappers.VFT2_DRV_VERSIONED_PRINTER
VerFileFontRaster VerFileSubtype = wrappers.VFT2_FONT_RASTER
VerFileFontVector VerFileSubtype = wrappers.VFT2_FONT_VECTOR
VerFileFontTrueType VerFileSubtype = wrappers.VFT2_FONT_TRUETYPE
)
type FileVersionNumber struct {
Major uint
Minor uint
Build uint
Revision uint
}
func (self *FileVersionNumber) String() string {
return fmt.Sprintf(
"%d.%d.%d.%d",
self.Major,
self.Minor,
self.Build,
self.Revision)
}
func StringToFileVersionNumber(s string) (FileVersionNumber, error) {
var version FileVersionNumber
parts := strings.Split(s, ".")
if len(parts) >= 1 {
n, err := strconv.ParseUint(parts[0], 10, 16)
if err != nil {
return FileVersionNumber{}, err
}
version.Major = uint(n)
}
if len(parts) >= 2 {
n, err := strconv.ParseUint(parts[1], 10, 16)
if err != nil {
return FileVersionNumber{}, err
}
version.Minor = uint(n)
}
if len(parts) >= 3 {
n, err := strconv.ParseUint(parts[2], 10, 16)
if err != nil {
return FileVersionNumber{}, err
}
version.Build = uint(n)
}
if len(parts) >= 4 {
n, err := strconv.ParseUint(parts[3], 10, 16)
if err != nil {
return FileVersionNumber{}, err
}
version.Revision = uint(n)
}
return version, nil
}
func CompareFileVersionNumbers(v1, v2 FileVersionNumber) int {
if v1.Major < v2.Major {
return -1
} else if v1.Major > v2.Major {
return 1
} else if v1.Minor < v2.Minor {
return -1
} else if v1.Minor > v2.Minor {
return 1
} else if v1.Build < v2.Build {
return -1
} else if v1.Build > v2.Build {
return 1
} else if v1.Revision < v2.Revision {
return -1
} else if v1.Revision > v2.Revision {
return 1
} else {
return 0
}
}
type FixedFileInfo struct {
FileVersion FileVersionNumber
ProductVersion FileVersionNumber
FileFlags VerFileFlags
FileOS VerFileOS
FileType VerFileType
FileSubtype VerFileSubtype
}
type FileVersionString string
const (
FileVersionComments FileVersionString = "Comments"
FileVersionCompanyName FileVersionString = "CompanyName"
FileVersionFileDescription FileVersionString = "FileDescription"
FileVersionFileVersion FileVersionString = "FileVersion"
FileVersionInternalName FileVersionString = "InternalName"
FileVersionLegalCopyright FileVersionString = "LegalCopyright"
FileVersionLegalTrademarks FileVersionString = "LegalTrademarks"
FileVersionOriginalFilename FileVersionString = "OriginalFilename"
FileVersionProductName FileVersionString = "ProductName"
FileVersionProductVersion FileVersionString = "ProductVersion"
FileVersionPrivateBuild FileVersionString = "PrivateBuild"
FileVersionSpecialBuild FileVersionString = "SpecialBuild"
)
type FileVersionTranslation struct {
Language Language
CodePage uint
}
type FileVersion struct {
data []byte
}
type StringFileInfo struct {
data []byte
translation FileVersionTranslation
}
func GetFileVersion(filename string) (*FileVersion, error) {
var handle uint32
size, err := wrappers.GetFileVersionInfoSize(syscall.StringToUTF16Ptr(filename), &handle)
if err != nil {
return nil, NewWindowsError("GetFileVersionInfoSize", err)
}
data := make([]byte, size)
if err := wrappers.GetFileVersionInfo(syscall.StringToUTF16Ptr(filename), handle, size, &data[0]); err != nil {
return nil, NewWindowsError("GetFileVersionInfo", err)
}
return &FileVersion{data: data}, nil
}
func (self *FileVersion) GetFixedFileInfo() (*FixedFileInfo, error) {
var ffi *wrappers.VS_FIXEDFILEINFO
var len uint32
err := wrappers.VerQueryValue(
&self.data[0],
syscall.StringToUTF16Ptr("\\"),
(**byte)(unsafe.Pointer(&ffi)),
&len)
if err != nil {
return nil, NewWindowsError("VerQueryValue", err)
}
return &FixedFileInfo{
FileVersion: FileVersionNumber{
Major: uint(wrappers.HIWORD(ffi.FileVersionMS)),
Minor: uint(wrappers.LOWORD(ffi.FileVersionMS)),
Build: uint(wrappers.HIWORD(ffi.FileVersionLS)),
Revision: uint(wrappers.LOWORD(ffi.FileVersionLS)),
},
ProductVersion: FileVersionNumber{
Major: uint(wrappers.HIWORD(ffi.ProductVersionMS)),
Minor: uint(wrappers.LOWORD(ffi.ProductVersionMS)),
Build: uint(wrappers.HIWORD(ffi.ProductVersionLS)),
Revision: uint(wrappers.LOWORD(ffi.ProductVersionLS)),
},
FileFlags: VerFileFlags(ffi.FileFlags),
FileOS: VerFileOS(ffi.FileOS),
FileType: VerFileType(ffi.FileType),
FileSubtype: VerFileSubtype(ffi.FileSubtype),
}, nil
}
func (self *FileVersion) GetTranslations() ([]FileVersionTranslation, error) {
type fileVersionTranslation struct {
Language uint16
CodePage uint16
}
var fit *fileVersionTranslation
var len uint32
err := wrappers.VerQueryValue(
&self.data[0],
syscall.StringToUTF16Ptr(`\VarFileInfo\Translation`),
(**byte)(unsafe.Pointer(&fit)),
&len)
if err != nil {
return nil, NewWindowsError("VerQueryValue", err)
}
result := make([]FileVersionTranslation, 0)
if len == 0 {
return result, nil
}
ti := fit
l := int(len / uint32(unsafe.Sizeof(*ti)))
for i := 0; i < l; i++ {
result = append(result, FileVersionTranslation{Language: Language(ti.Language), CodePage: uint(ti.CodePage)})
ti = (*fileVersionTranslation)(unsafe.Pointer(uintptr(unsafe.Pointer(ti)) + unsafe.Sizeof(*ti)))
}
return result, nil
}
func (self *FileVersion) GetStringFileInfo(translation FileVersionTranslation) *StringFileInfo {
return &StringFileInfo{data: self.data, translation: translation}
}
func (self *FileVersion) GetFirstStringFileInfo() (*StringFileInfo, error) {
tr, err := self.GetTranslations()
if err != nil {
return nil, err
}
if len(tr) == 0 {
return nil, errors.New("no translations")
}
return &StringFileInfo{data: self.data, translation: tr[0]}, nil
}
func (self *StringFileInfo) GetString(stringName FileVersionString) (string, error) {
var offset *uint16
var len uint32
err := wrappers.VerQueryValue(
&self.data[0],
syscall.StringToUTF16Ptr(fmt.Sprintf("\\StringFileInfo\\%04x%04x\\%s", self.translation.Language, self.translation.CodePage, stringName)),
(**byte)(unsafe.Pointer(&offset)),
&len)
if err != nil {
return "", NewWindowsError("VerQueryValue", err)
}
if len == 0 {
return "", nil
}
return LpstrToString(offset), nil
}
func GetStringFileInfo(fileName string, stringName FileVersionString) (string, error) {
fv, err := GetFileVersion(fileName)
if err != nil {
return "", err
}
si, err := fv.GetFirstStringFileInfo()
if err != nil {
return "", err
}
return si.GetString(stringName)
}