返回 DeepSeek-Reasonix
syscall.go
1 //go:build windows
2
3 package webviewloader
4
5 import (
6 "fmt"
7 "syscall"
8 "unicode/utf16"
9 "unsafe"
10
11 "golang.org/x/sys/windows"
12 )
13
14 var (
15 modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
16 procGlobalAlloc = modkernel32.NewProc("GlobalAlloc")
17 procGlobalFree = modkernel32.NewProc("GlobalFree")
18
19 modversion = windows.NewLazySystemDLL("version.dll")
20 procGetFileVersionInfoSize = modversion.NewProc("GetFileVersionInfoSizeW")
21 procGetFileVersionInfo = modversion.NewProc("GetFileVersionInfoW")
22 procVerQueryValue = modversion.NewProc("VerQueryValueW")
23
24 modole32 = windows.NewLazySystemDLL("ole32.dll")
25 procCoTaskMemAlloc = modole32.NewProc("CoTaskMemAlloc")
26 )
27
28 func getFileVersionInfo(path string) ([]byte, error) {
29 lptstrFilename, err := syscall.UTF16PtrFromString(path)
30 if err != nil {
31 return nil, err
32 }
33
34 size, _, err := procGetFileVersionInfoSize.Call(
35 uintptr(unsafe.Pointer(lptstrFilename)),
36 0,
37 )
38
39 err = maskErrorSuccess(err)
40 if size == 0 && err == nil {
41 err = fmt.Errorf("GetFileVersionInfoSize failed")
42 }
43
44 if err != nil {
45 return nil, err
46 }
47
48 data := make([]byte, size)
49 ret, _, err := procGetFileVersionInfo.Call(
50 uintptr(unsafe.Pointer(lptstrFilename)),
51 0,
52 uintptr(size),
53 uintptr(unsafe.Pointer(&data[0])),
54 )
55
56 err = maskErrorSuccess(err)
57 if ret == 0 && err == nil {
58 err = fmt.Errorf("GetFileVersionInfo failed")
59 }
60
61 if err != nil {
62 return nil, err
63 }
64 return data, nil
65 }
66
67 func verQueryValueString(block []byte, subBlock string) (string, error) {
68 // Allocate memory from native side to make sure the block doesn't get moved
69 // because we get a pointer into that memory block from the native verQueryValue
70 // call back.
71 pBlock := globalAlloc(0, uint32(len(block)))
72 defer globalFree(unsafe.Pointer(pBlock))
73
74 // Copy the memory region into native side memory
75 copy(unsafe.Slice((*byte)(pBlock), len(block)), block)
76
77 lpSubBlock, err := syscall.UTF16PtrFromString(subBlock)
78 if err != nil {
79 return "", err
80 }
81
82 var lplpBuffer unsafe.Pointer
83 var puLen uint
84 ret, _, err := procVerQueryValue.Call(
85 uintptr(pBlock),
86 uintptr(unsafe.Pointer(lpSubBlock)),
87 uintptr(unsafe.Pointer(&lplpBuffer)),
88 uintptr(unsafe.Pointer(&puLen)),
89 )
90
91 err = maskErrorSuccess(err)
92 if ret == 0 && err == nil {
93 err = fmt.Errorf("VerQueryValue failed")
94 }
95
96 if err != nil {
97 return "", err
98 }
99
100 if puLen <= 1 {
101 return "", nil
102 }
103 puLen -= 1 // Remove Null-Terminator
104
105 wchar := unsafe.Slice((*uint16)(lplpBuffer), puLen)
106 return string(utf16.Decode(wchar)), nil
107 }
108
109 func globalAlloc(uFlags uint, dwBytes uint32) unsafe.Pointer {
110 ret, _, _ := procGlobalAlloc.Call(
111 uintptr(uFlags),
112 uintptr(dwBytes))
113
114 if ret == 0 {
115 panic("globalAlloc failed")
116 }
117
118 return unsafe.Pointer(ret)
119 }
120
121 func globalFree(data unsafe.Pointer) {
122 ret, _, _ := procGlobalFree.Call(uintptr(data))
123 if ret != 0 {
124 panic("globalFree failed")
125 }
126 }
127
128 func maskErrorSuccess(err error) error {
129 if err == windows.ERROR_SUCCESS {
130 return nil
131 }
132 return err
133 }
134
135 func coTaskMemAlloc(size int) unsafe.Pointer {
136 ret, _, _ := procCoTaskMemAlloc.Call(
137 uintptr(size))
138
139 if ret == 0 {
140 panic("coTaskMemAlloc failed")
141 }
142 return unsafe.Pointer(ret)
143 }
144
144 lines GO