170 lines
4.5 KiB
Go
170 lines
4.5 KiB
Go
//go:build windows
|
|
// +build windows
|
|
|
|
package webview2runtime
|
|
|
|
import (
|
|
_ "embed"
|
|
"io"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"syscall"
|
|
"unsafe"
|
|
)
|
|
|
|
// Info contains all the information about an installation of the webview2 runtime.
|
|
type Info struct {
|
|
Location string
|
|
Name string
|
|
Version string
|
|
SilentUninstall string
|
|
}
|
|
|
|
// IsOlderThan returns true if the installed version is older than the given required version.
|
|
// Returns error if something goes wrong.
|
|
func (i *Info) IsOlderThan(requiredVersion string) (bool, error) {
|
|
var mod = syscall.NewLazyDLL("WebView2Loader.dll")
|
|
var CompareBrowserVersions = mod.NewProc("CompareBrowserVersions")
|
|
v1, err := syscall.UTF16PtrFromString(i.Version)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
v2, err := syscall.UTF16PtrFromString(requiredVersion)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
var result int = 9
|
|
_, _, err = CompareBrowserVersions.Call(uintptr(unsafe.Pointer(v1)), uintptr(unsafe.Pointer(v2)), uintptr(unsafe.Pointer(&result)))
|
|
if result < -1 || result > 1 {
|
|
return false, err
|
|
}
|
|
return result == -1, nil
|
|
}
|
|
|
|
func downloadBootstrapper() (string, error) {
|
|
bootstrapperURL := `https://go.microsoft.com/fwlink/p/?LinkId=2124703`
|
|
installer := filepath.Join(os.TempDir(), `MicrosoftEdgeWebview2Setup.exe`)
|
|
|
|
// Download installer
|
|
out, err := os.Create(installer)
|
|
defer out.Close()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
resp, err := http.Get(bootstrapperURL)
|
|
defer resp.Body.Close()
|
|
if err != nil {
|
|
err = out.Close()
|
|
return "", err
|
|
}
|
|
_, err = io.Copy(out, resp.Body)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return installer, nil
|
|
}
|
|
|
|
// InstallUsingEmbeddedBootstrapper will download the bootstrapper from Microsoft and run it to install
|
|
// the latest version of the runtime.
|
|
// Returns true if the installer ran successfully.
|
|
// Returns an error if something goes wrong
|
|
func InstallUsingEmbeddedBootstrapper() (bool, error) {
|
|
installer, err := WriteInstaller(os.TempDir())
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
result, err := runInstaller(installer)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return result, os.Remove(installer)
|
|
|
|
}
|
|
|
|
// InstallUsingBootstrapper will extract the embedded bootstrapper from Microsoft and run it to install
|
|
// the latest version of the runtime.
|
|
// Returns true if the installer ran successfully.
|
|
// Returns an error if something goes wrong
|
|
func InstallUsingBootstrapper() (bool, error) {
|
|
|
|
installer, err := downloadBootstrapper()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
result, err := runInstaller(installer)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return result, os.Remove(installer)
|
|
|
|
}
|
|
|
|
func runInstaller(installer string) (bool, error) {
|
|
// Credit: https://stackoverflow.com/a/10385867
|
|
cmd := exec.Command(installer)
|
|
if err := cmd.Start(); err != nil {
|
|
return false, err
|
|
}
|
|
if err := cmd.Wait(); err != nil {
|
|
if exiterr, ok := err.(*exec.ExitError); ok {
|
|
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
|
return status.ExitStatus() == 0, nil
|
|
}
|
|
}
|
|
}
|
|
return true, nil
|
|
}
|
|
|
|
// Confirm will prompt the user with a message and OK / CANCEL buttons.
|
|
// Returns true if OK is selected by the user.
|
|
// Returns an error if something went wrong.
|
|
func Confirm(caption string, title string) (bool, error) {
|
|
var flags uint = 0x00000001 // MB_OKCANCEL
|
|
result, err := MessageBox(caption, title, flags)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return result == 1, nil
|
|
}
|
|
|
|
// Error will an error message to the user.
|
|
// Returns an error if something went wrong.
|
|
func Error(caption string, title string) error {
|
|
var flags uint = 0x00000010 // MB_ICONERROR
|
|
_, err := MessageBox(caption, title, flags)
|
|
return err
|
|
}
|
|
|
|
// MessageBox prompts the user with the given caption and title.
|
|
// Flags may be provided to customise the dialog.
|
|
// Returns an error if something went wrong.
|
|
func MessageBox(caption string, title string, flags uint) (int, error) {
|
|
captionUTF16, err := syscall.UTF16PtrFromString(caption)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
titleUTF16, err := syscall.UTF16PtrFromString(title)
|
|
if err != nil {
|
|
return -1, err
|
|
}
|
|
ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
|
|
uintptr(0),
|
|
uintptr(unsafe.Pointer(captionUTF16)),
|
|
uintptr(unsafe.Pointer(titleUTF16)),
|
|
uintptr(flags))
|
|
|
|
return int(ret), nil
|
|
}
|
|
|
|
// OpenInstallerDownloadWebpage will open the browser on the WebView2 download page
|
|
func OpenInstallerDownloadWebpage() error {
|
|
cmd := exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://developer.microsoft.com/en-us/microsoft-edge/webview2/")
|
|
return cmd.Run()
|
|
}
|