143 lines
3.7 KiB
Go
143 lines
3.7 KiB
Go
package dllver
|
|
|
|
import (
|
|
"fmt"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
var (
|
|
modversion = syscall.NewLazyDLL("version.dll")
|
|
procGetFileVersionInfoSize = modversion.NewProc("GetFileVersionInfoSizeW")
|
|
procGetFileVersionInfo = modversion.NewProc("GetFileVersionInfoW")
|
|
procVerQueryValue = modversion.NewProc("VerQueryValueW")
|
|
)
|
|
|
|
// VS_FIXEDFILEINFO 结构体
|
|
type VS_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
|
|
}
|
|
|
|
// initialize 初始化版本信息
|
|
func (i *Info) initialize() error {
|
|
// 转换路径为 UTF16
|
|
pathPtr, err := syscall.UTF16PtrFromString(i.FilePath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 获取版本信息大小
|
|
var handle uintptr
|
|
size, _, err := procGetFileVersionInfoSize.Call(
|
|
uintptr(unsafe.Pointer(pathPtr)),
|
|
uintptr(unsafe.Pointer(&handle)),
|
|
)
|
|
if size == 0 {
|
|
return fmt.Errorf("GetFileVersionInfoSize failed: %v", err)
|
|
}
|
|
|
|
// 分配内存
|
|
verInfo := make([]byte, size)
|
|
ret, _, err := procGetFileVersionInfo.Call(
|
|
uintptr(unsafe.Pointer(pathPtr)),
|
|
0,
|
|
size,
|
|
uintptr(unsafe.Pointer(&verInfo[0])),
|
|
)
|
|
if ret == 0 {
|
|
return fmt.Errorf("GetFileVersionInfo failed: %v", err)
|
|
}
|
|
|
|
// 获取固定的文件信息
|
|
var fixedFileInfo *VS_FIXEDFILEINFO
|
|
var uLen uint32
|
|
rootPtr, _ := syscall.UTF16PtrFromString("\\")
|
|
ret, _, err = procVerQueryValue.Call(
|
|
uintptr(unsafe.Pointer(&verInfo[0])),
|
|
uintptr(unsafe.Pointer(rootPtr)),
|
|
uintptr(unsafe.Pointer(&fixedFileInfo)),
|
|
uintptr(unsafe.Pointer(&uLen)),
|
|
)
|
|
if ret == 0 {
|
|
return fmt.Errorf("VerQueryValue failed: %v", err)
|
|
}
|
|
|
|
// 解析文件版本
|
|
i.FileVersion = fmt.Sprintf("%d.%d.%d.%d",
|
|
(fixedFileInfo.FileVersionMS>>16)&0xffff,
|
|
(fixedFileInfo.FileVersionMS>>0)&0xffff,
|
|
(fixedFileInfo.FileVersionLS>>16)&0xffff,
|
|
(fixedFileInfo.FileVersionLS>>0)&0xffff,
|
|
)
|
|
i.FileMajorVersion = int((fixedFileInfo.FileVersionMS >> 16) & 0xffff)
|
|
|
|
i.ProductVersion = fmt.Sprintf("%d.%d.%d.%d",
|
|
(fixedFileInfo.ProductVersionMS>>16)&0xffff,
|
|
(fixedFileInfo.ProductVersionMS>>0)&0xffff,
|
|
(fixedFileInfo.ProductVersionLS>>16)&0xffff,
|
|
(fixedFileInfo.ProductVersionLS>>0)&0xffff,
|
|
)
|
|
|
|
// 获取翻译信息
|
|
type langAndCodePage struct {
|
|
language uint16
|
|
codePage uint16
|
|
}
|
|
|
|
var lpTranslate *langAndCodePage
|
|
var cbTranslate uint32
|
|
transPtr, _ := syscall.UTF16PtrFromString("\\VarFileInfo\\Translation")
|
|
ret, _, _ = procVerQueryValue.Call(
|
|
uintptr(unsafe.Pointer(&verInfo[0])),
|
|
uintptr(unsafe.Pointer(transPtr)),
|
|
uintptr(unsafe.Pointer(&lpTranslate)),
|
|
uintptr(unsafe.Pointer(&cbTranslate)),
|
|
)
|
|
|
|
if ret != 0 && cbTranslate > 0 {
|
|
// 获取所有需要的字符串信息
|
|
stringInfos := map[string]*string{
|
|
"CompanyName": &i.CompanyName,
|
|
"FileDescription": &i.FileDescription,
|
|
"FileVersion": &i.FileVersion,
|
|
"LegalCopyright": &i.LegalCopyright,
|
|
"ProductName": &i.ProductName,
|
|
"ProductVersion": &i.ProductVersion,
|
|
}
|
|
|
|
for name, ptr := range stringInfos {
|
|
subBlock := fmt.Sprintf("\\StringFileInfo\\%04x%04x\\%s",
|
|
lpTranslate.language, lpTranslate.codePage, name)
|
|
|
|
subBlockPtr, _ := syscall.UTF16PtrFromString(subBlock)
|
|
var buffer *uint16
|
|
var bufLen uint32
|
|
|
|
ret, _, _ = procVerQueryValue.Call(
|
|
uintptr(unsafe.Pointer(&verInfo[0])),
|
|
uintptr(unsafe.Pointer(subBlockPtr)),
|
|
uintptr(unsafe.Pointer(&buffer)),
|
|
uintptr(unsafe.Pointer(&bufLen)),
|
|
)
|
|
|
|
if ret != 0 && bufLen > 0 {
|
|
*ptr = syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(buffer))[:bufLen:bufLen])
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|