返回 DeepSeek-Reasonix
native_module.go
1 //go:build windows && native_webview2loader
2
3 package webviewloader
4
5 import (
6 "errors"
7 "fmt"
8 "os"
9 "sync"
10 "unsafe"
11
12 "github.com/jchv/go-winloader"
13
14 "golang.org/x/sys/windows"
15 )
16
17 func init() {
18 preventEnvAndRegistryOverrides(nil, nil, "")
19 }
20
21 var (
22 memOnce sync.Once
23 memModule winloader.Module
24 memCreate winloader.Proc
25 memCompareBrowserVersions winloader.Proc
26 memGetAvailableCoreWebView2BrowserVersionString winloader.Proc
27 memErr error
28 )
29
30 const (
31 // https://referencesource.microsoft.com/#system.web/Util/hresults.cs,20
32 E_FILENOTFOUND = 0x80070002
33 )
34
35 // CompareBrowserVersions will compare the 2 given versions and return:
36 //
37 // Less than zero: v1 < v2
38 // zero: v1 == v2
39 // Greater than zero: v1 > v2
40 func CompareBrowserVersions(v1 string, v2 string) (int, error) {
41 _v1, err := windows.UTF16PtrFromString(v1)
42 if err != nil {
43 return 0, err
44 }
45 _v2, err := windows.UTF16PtrFromString(v2)
46 if err != nil {
47 return 0, err
48 }
49
50 err = loadFromMemory()
51 if err != nil {
52 return 0, err
53 }
54
55 var result int32
56 _, _, err = memCompareBrowserVersions.Call(
57 uint64(uintptr(unsafe.Pointer(_v1))),
58 uint64(uintptr(unsafe.Pointer(_v2))),
59 uint64(uintptr(unsafe.Pointer(&result))))
60
61 if err != windows.ERROR_SUCCESS {
62 return 0, err
63 }
64 return int(result), nil
65 }
66
67 // GetAvailableCoreWebView2BrowserVersionString returns version of the webview2 runtime.
68 // If path is empty, it will try to find installed webview2 is the system.
69 // If there is no version installed, a blank string is returned.
70 func GetAvailableCoreWebView2BrowserVersionString(path string) (string, error) {
71 if path != "" {
72 // The default implementation fails if CGO and a fixed browser path is used. It's caused by the go-winloader
73 // which loads the native DLL from memory.
74 // Use the new GoWebView2Loader in this case, in the future we will make GoWebView2Loader
75 // feature-complete and remove the use of the native DLL and go-winloader.
76 version, err := goGetAvailableCoreWebView2BrowserVersionString(path)
77 if errors.Is(err, errNoClientDLLFound) {
78 // WebView2 is not found
79 return "", nil
80 } else if err != nil {
81 return "", err
82 }
83
84 return version, nil
85 }
86
87 err := loadFromMemory()
88 if err != nil {
89 return "", err
90 }
91
92 var browserPath *uint16 = nil
93 if path != "" {
94 browserPath, err = windows.UTF16PtrFromString(path)
95 if err != nil {
96 return "", fmt.Errorf("error calling UTF16PtrFromString for %s: %v", path, err)
97 }
98 }
99
100 preventEnvAndRegistryOverrides(browserPath, nil, "")
101 var result *uint16
102 res, _, err := memGetAvailableCoreWebView2BrowserVersionString.Call(
103 uint64(uintptr(unsafe.Pointer(browserPath))),
104 uint64(uintptr(unsafe.Pointer(&result))))
105
106 if res != 0 {
107 if res == E_FILENOTFOUND {
108 // WebView2 is not installed
109 return "", nil
110 }
111
112 return "", fmt.Errorf("Unable to call GetAvailableCoreWebView2BrowserVersionString (%x): %w", res, err)
113 }
114
115 version := windows.UTF16PtrToString(result)
116 windows.CoTaskMemFree(unsafe.Pointer(result))
117 return version, nil
118 }
119
120 // CreateCoreWebView2EnvironmentWithOptions tries to load WebviewLoader2 and
121 // call the CreateCoreWebView2EnvironmentWithOptions routine.
122 func CreateCoreWebView2EnvironmentWithOptions(browserExecutableFolder, userDataFolder *uint16, environmentCompletedHandle uintptr, additionalBrowserArgs string) (uintptr, error) {
123 err := loadFromMemory()
124 if err != nil {
125 return 0, err
126 }
127
128 preventEnvAndRegistryOverrides(browserExecutableFolder, userDataFolder, additionalBrowserArgs)
129 res, _, _ := memCreate.Call(
130 uint64(uintptr(unsafe.Pointer(browserExecutableFolder))),
131 uint64(uintptr(unsafe.Pointer(userDataFolder))),
132 0,
133 uint64(environmentCompletedHandle),
134 )
135 return uintptr(res), nil
136 }
137
138 func loadFromMemory() error {
139 var err error
140 // DLL is not available natively. Try loading embedded copy.
141 memOnce.Do(func() {
142 memModule, memErr = winloader.LoadFromMemory(WebView2Loader)
143 if memErr != nil {
144 err = fmt.Errorf("Unable to load WebView2Loader.dll from memory: %w", memErr)
145 return
146 }
147 memCreate = memModule.Proc("CreateCoreWebView2EnvironmentWithOptions")
148 memCompareBrowserVersions = memModule.Proc("CompareBrowserVersions")
149 memGetAvailableCoreWebView2BrowserVersionString = memModule.Proc("GetAvailableCoreWebView2BrowserVersionString")
150 })
151 return err
152 }
153
154 func preventEnvAndRegistryOverrides(browserFolder, userDataFolder *uint16, additionalBrowserArgs string) {
155 // Setting these env variables to empty string also prevents registry overrides because webview2loader
156 // checks for existence and not for empty value
157 os.Setenv("WEBVIEW2_PIPE_FOR_SCRIPT_DEBUGGER", "")
158
159 // Set these overrides to the values or empty to prevent registry and external env overrides
160 os.Setenv("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", additionalBrowserArgs)
161 os.Setenv("WEBVIEW2_RELEASE_CHANNEL_PREFERENCE", "0")
162 os.Setenv("WEBVIEW2_BROWSER_EXECUTABLE_FOLDER", windows.UTF16PtrToString(browserFolder))
163 os.Setenv("WEBVIEW2_USER_DATA_FOLDER", windows.UTF16PtrToString(userDataFolder))
164 }
165
166 func goGetAvailableCoreWebView2BrowserVersionString(browserExecutableFolder string) (string, error) {
167 clientPath, err := findEmbeddedClientDll(browserExecutableFolder)
168 if err != nil {
169 return "", err
170 }
171
172 return findEmbeddedBrowserVersion(clientPath)
173 }
174
174 lines GO