mirror of
https://github.com/wailsapp/wails.git
synced 2025-05-02 20:51:38 +08:00
[webviewloader] Use go implementation to retrieve the version of a fixed runtime (#1790)
* [webview2loader] Start porting of OpenWebView2Loader to go * [webviewloader] Use go implementation to retrieve the version of a fixed runtime This fixes a problem with the go-winloader and using GetAvailableCoreWebView2BrowserVersionString Fixes #1569 Co-authored-by: Lea Anthony <lea.anthony@gmail.com>
This commit is contained in:
parent
bbfb64a965
commit
3632ef9dc8
@ -0,0 +1,16 @@
|
||||
ISC License (ISC)
|
||||
|
||||
Copyright (c) 2020 John Chadwick
|
||||
Copyright (c) 2022 Wails Project Developers
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
@ -0,0 +1,15 @@
|
||||
# GoWebView2Loader
|
||||
|
||||
GoWebView2Loader is a port of [OpenWebView2Loader](https://github.com/jchv/OpenWebView2Loader) to Go.
|
||||
|
||||
It is intended to be feature-complete in the near future with the original WebView2Loader distributed with
|
||||
the WebView2 NuGet package.
|
||||
|
||||
## Status
|
||||
|
||||
- [ ] CompareBrowserVersions
|
||||
- [ ] CreateCoreWebView2Environment
|
||||
- [ ] CreateCoreWebView2EnvironmentWithOptions
|
||||
- [ ] GetAvailableCoreWebView2BrowserVersionString
|
||||
- [ ] Feature Complete
|
||||
- [x] Fixed Runtime support
|
@ -0,0 +1,128 @@
|
||||
package webview2loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
procGlobalAlloc = modkernel32.NewProc("GlobalAlloc")
|
||||
procGlobalFree = modkernel32.NewProc("GlobalFree")
|
||||
|
||||
modversion = windows.NewLazySystemDLL("version.dll")
|
||||
procGetFileVersionInfoSize = modversion.NewProc("GetFileVersionInfoSizeW")
|
||||
procGetFileVersionInfo = modversion.NewProc("GetFileVersionInfoW")
|
||||
procVerQueryValue = modversion.NewProc("VerQueryValueW")
|
||||
)
|
||||
|
||||
func getFileVersionInfo(path string) ([]byte, error) {
|
||||
lptstrFilename, err := syscall.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
size, _, err := procGetFileVersionInfoSize.Call(
|
||||
uintptr(unsafe.Pointer(lptstrFilename)),
|
||||
0,
|
||||
)
|
||||
|
||||
err = maskErrorSuccess(err)
|
||||
if size == 0 && err == nil {
|
||||
err = fmt.Errorf("GetFileVersionInfoSize failed")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := make([]byte, size)
|
||||
ret, _, err := procGetFileVersionInfo.Call(
|
||||
uintptr(unsafe.Pointer(lptstrFilename)),
|
||||
0,
|
||||
uintptr(size),
|
||||
uintptr(unsafe.Pointer(&data[0])),
|
||||
)
|
||||
|
||||
err = maskErrorSuccess(err)
|
||||
if ret == 0 && err == nil {
|
||||
err = fmt.Errorf("GetFileVersionInfo failed")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func verQueryValueString(block []byte, subBlock string) (string, error) {
|
||||
// Allocate memory from native side to make sure the block doesn't get moved
|
||||
// because we get a pointer into that memory block from the native verQueryValue
|
||||
// call back.
|
||||
pBlock := globalAlloc(0, uint32(len(block)))
|
||||
defer globalFree(unsafe.Pointer(pBlock))
|
||||
|
||||
// Copy the memory region into native side memory
|
||||
copy(unsafe.Slice((*byte)(pBlock), len(block)), block)
|
||||
|
||||
lpSubBlock, err := syscall.UTF16PtrFromString(subBlock)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var lplpBuffer unsafe.Pointer
|
||||
var puLen uint
|
||||
ret, _, err := procVerQueryValue.Call(
|
||||
uintptr(pBlock),
|
||||
uintptr(unsafe.Pointer(lpSubBlock)),
|
||||
uintptr(unsafe.Pointer(&lplpBuffer)),
|
||||
uintptr(unsafe.Pointer(&puLen)),
|
||||
)
|
||||
|
||||
err = maskErrorSuccess(err)
|
||||
if ret == 0 && err == nil {
|
||||
err = fmt.Errorf("VerQueryValue failed")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if puLen <= 1 {
|
||||
return "", nil
|
||||
}
|
||||
puLen -= 1 // Remove Null-Terminator
|
||||
|
||||
wchar := unsafe.Slice((*uint16)(lplpBuffer), puLen)
|
||||
return string(utf16.Decode(wchar)), nil
|
||||
}
|
||||
|
||||
func globalAlloc(uFlags uint, dwBytes uint32) unsafe.Pointer {
|
||||
ret, _, _ := procGlobalAlloc.Call(
|
||||
uintptr(uFlags),
|
||||
uintptr(dwBytes))
|
||||
|
||||
if ret == 0 {
|
||||
panic("globalAlloc failed")
|
||||
}
|
||||
|
||||
return unsafe.Pointer(ret)
|
||||
}
|
||||
|
||||
func globalFree(data unsafe.Pointer) {
|
||||
ret, _, _ := procGlobalFree.Call(uintptr(data))
|
||||
if ret != 0 {
|
||||
panic("globalFree failed")
|
||||
}
|
||||
}
|
||||
|
||||
func maskErrorSuccess(err error) error {
|
||||
if err == windows.ERROR_SUCCESS {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package webview2loader
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// GetAvailableCoreWebView2BrowserVersionString get the browser version info including channel name.
|
||||
func GetAvailableCoreWebView2BrowserVersionString(browserExecutableFolder string) (string, error) {
|
||||
if browserExecutableFolder != "" {
|
||||
clientPath, err := findEmbeddedClientDll(browserExecutableFolder)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return findEmbeddedBrowserVersion(clientPath)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("not implemented yet for empty browserExecutableFolder ")
|
||||
}
|
||||
|
||||
func findEmbeddedBrowserVersion(filename string) (string, error) {
|
||||
block, err := getFileVersionInfo(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
info, err := verQueryValueString(block, "\\StringFileInfo\\040904B0\\ProductVersion")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return info, nil
|
||||
}
|
||||
|
||||
func findEmbeddedClientDll(embeddedEdgeSubFolder string) (outClientPath string, err error) {
|
||||
if !filepath.IsAbs(embeddedEdgeSubFolder) {
|
||||
exe, err := os.Executable()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
embeddedEdgeSubFolder = filepath.Join(filepath.Dir(exe), embeddedEdgeSubFolder)
|
||||
}
|
||||
|
||||
return findClientDllInFolder(embeddedEdgeSubFolder)
|
||||
}
|
||||
|
||||
func findClientDllInFolder(folder string) (string, error) {
|
||||
arch := ""
|
||||
switch runtime.GOARCH {
|
||||
case "arm64":
|
||||
arch = "arm64"
|
||||
case "amd64":
|
||||
arch = "x64"
|
||||
case "386":
|
||||
arch = "x86"
|
||||
default:
|
||||
return "", fmt.Errorf("Unsupported architecture")
|
||||
}
|
||||
|
||||
dllPath := filepath.Join(folder, "EBWebView", arch, "EmbeddedBrowserWebView.dll")
|
||||
if _, err := os.Stat(dllPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return dllPath, nil
|
||||
}
|
@ -1,11 +1,15 @@
|
||||
package webviewloader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/jchv/go-winloader"
|
||||
"github.com/wailsapp/wails/v2/internal/frontend/desktop/windows/go-webview2/webview2loader"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@ -28,9 +32,10 @@ const (
|
||||
)
|
||||
|
||||
// CompareBrowserVersions will compare the 2 given versions and return:
|
||||
// Less than zero: v1 < v2
|
||||
// zero: v1 == v2
|
||||
// Greater than zero: v1 > v2
|
||||
//
|
||||
// Less than zero: v1 < v2
|
||||
// zero: v1 == v2
|
||||
// Greater than zero: v1 > v2
|
||||
func CompareBrowserVersions(v1 string, v2 string) (int, error) {
|
||||
_v1, err := windows.UTF16PtrFromString(v1)
|
||||
if err != nil {
|
||||
@ -62,6 +67,19 @@ func CompareBrowserVersions(v1 string, v2 string) (int, error) {
|
||||
// If path is empty, it will try to find installed webview2 is the system.
|
||||
// If there is no version installed, a blank string is returned.
|
||||
func GetWebviewVersion(path string) (string, error) {
|
||||
if path != "" {
|
||||
// The default implementation fails if CGO and a fixed browser path is used. It's caused by the go-winloader
|
||||
// which loads the native DLL from memory.
|
||||
// Use the new GoWebView2Loader in this case, in the future we will make GoWebView2Loader
|
||||
// feature-complete and remove the use of the native DLL and go-winloader.
|
||||
version, err := webview2loader.GetAvailableCoreWebView2BrowserVersionString(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
// Webview2 is not found
|
||||
return "", nil
|
||||
}
|
||||
return version, nil
|
||||
}
|
||||
|
||||
err := loadFromMemory()
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
Loading…
Reference in New Issue
Block a user