github.com/AlpineAIO/wails/v2@v2.0.0-beta.32.0.20240505041856-1047a8fa5fef/internal/webview2runtime/webview2runtime.go (about)

     1  //go:build windows
     2  // +build windows
     3  
     4  package webview2runtime
     5  
     6  import (
     7  	_ "embed"
     8  	"io"
     9  	"net/http"
    10  	"os"
    11  	"os/exec"
    12  	"path/filepath"
    13  	"syscall"
    14  	"unsafe"
    15  )
    16  
    17  // Info contains all the information about an installation of the webview2 runtime.
    18  type Info struct {
    19  	Location        string
    20  	Name            string
    21  	Version         string
    22  	SilentUninstall string
    23  }
    24  
    25  // IsOlderThan returns true if the installed version is older than the given required version.
    26  // Returns error if something goes wrong.
    27  func (i *Info) IsOlderThan(requiredVersion string) (bool, error) {
    28  	var mod = syscall.NewLazyDLL("WebView2Loader.dll")
    29  	var CompareBrowserVersions = mod.NewProc("CompareBrowserVersions")
    30  	v1, err := syscall.UTF16PtrFromString(i.Version)
    31  	if err != nil {
    32  		return false, err
    33  	}
    34  	v2, err := syscall.UTF16PtrFromString(requiredVersion)
    35  	if err != nil {
    36  		return false, err
    37  	}
    38  	var result int = 9
    39  	_, _, err = CompareBrowserVersions.Call(uintptr(unsafe.Pointer(v1)), uintptr(unsafe.Pointer(v2)), uintptr(unsafe.Pointer(&result)))
    40  	if result < -1 || result > 1 {
    41  		return false, err
    42  	}
    43  	return result == -1, nil
    44  }
    45  
    46  func downloadBootstrapper() (string, error) {
    47  	bootstrapperURL := `https://go.microsoft.com/fwlink/p/?LinkId=2124703`
    48  	installer := filepath.Join(os.TempDir(), `MicrosoftEdgeWebview2Setup.exe`)
    49  
    50  	// Download installer
    51  	out, err := os.Create(installer)
    52  	defer out.Close()
    53  	if err != nil {
    54  		return "", err
    55  	}
    56  	resp, err := http.Get(bootstrapperURL)
    57  	defer resp.Body.Close()
    58  	if err != nil {
    59  		err = out.Close()
    60  		return "", err
    61  	}
    62  	_, err = io.Copy(out, resp.Body)
    63  	if err != nil {
    64  		return "", err
    65  	}
    66  
    67  	return installer, nil
    68  }
    69  
    70  // InstallUsingEmbeddedBootstrapper will download the bootstrapper from Microsoft and run it to install
    71  // the latest version of the runtime.
    72  // Returns true if the installer ran successfully.
    73  // Returns an error if something goes wrong
    74  func InstallUsingEmbeddedBootstrapper() (bool, error) {
    75  	installer, err := WriteInstaller(os.TempDir())
    76  	if err != nil {
    77  		return false, err
    78  	}
    79  	result, err := runInstaller(installer)
    80  	if err != nil {
    81  		return false, err
    82  	}
    83  
    84  	return result, os.Remove(installer)
    85  
    86  }
    87  
    88  // InstallUsingBootstrapper will extract the embedded bootstrapper from Microsoft and run it to install
    89  // the latest version of the runtime.
    90  // Returns true if the installer ran successfully.
    91  // Returns an error if something goes wrong
    92  func InstallUsingBootstrapper() (bool, error) {
    93  
    94  	installer, err := downloadBootstrapper()
    95  	if err != nil {
    96  		return false, err
    97  	}
    98  
    99  	result, err := runInstaller(installer)
   100  	if err != nil {
   101  		return false, err
   102  	}
   103  
   104  	return result, os.Remove(installer)
   105  
   106  }
   107  
   108  func runInstaller(installer string) (bool, error) {
   109  	// Credit: https://stackoverflow.com/a/10385867
   110  	cmd := exec.Command(installer)
   111  	if err := cmd.Start(); err != nil {
   112  		return false, err
   113  	}
   114  	if err := cmd.Wait(); err != nil {
   115  		if exiterr, ok := err.(*exec.ExitError); ok {
   116  			if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
   117  				return status.ExitStatus() == 0, nil
   118  			}
   119  		}
   120  	}
   121  	return true, nil
   122  }
   123  
   124  // Confirm will prompt the user with a message and OK / CANCEL buttons.
   125  // Returns true if OK is selected by the user.
   126  // Returns an error if something went wrong.
   127  func Confirm(caption string, title string) (bool, error) {
   128  	var flags uint = 0x00000001 // MB_OKCANCEL
   129  	result, err := MessageBox(caption, title, flags)
   130  	if err != nil {
   131  		return false, err
   132  	}
   133  	return result == 1, nil
   134  }
   135  
   136  // Error will an error message to the user.
   137  // Returns an error if something went wrong.
   138  func Error(caption string, title string) error {
   139  	var flags uint = 0x00000010 // MB_ICONERROR
   140  	_, err := MessageBox(caption, title, flags)
   141  	return err
   142  }
   143  
   144  // MessageBox prompts the user with the given caption and title.
   145  // Flags may be provided to customise the dialog.
   146  // Returns an error if something went wrong.
   147  func MessageBox(caption string, title string, flags uint) (int, error) {
   148  	captionUTF16, err := syscall.UTF16PtrFromString(caption)
   149  	if err != nil {
   150  		return -1, err
   151  	}
   152  	titleUTF16, err := syscall.UTF16PtrFromString(title)
   153  	if err != nil {
   154  		return -1, err
   155  	}
   156  	ret, _, _ := syscall.NewLazyDLL("user32.dll").NewProc("MessageBoxW").Call(
   157  		uintptr(0),
   158  		uintptr(unsafe.Pointer(captionUTF16)),
   159  		uintptr(unsafe.Pointer(titleUTF16)),
   160  		uintptr(flags))
   161  
   162  	return int(ret), nil
   163  }
   164  
   165  // OpenInstallerDownloadWebpage will open the browser on the WebView2 download page
   166  func OpenInstallerDownloadWebpage() error {
   167  	cmd := exec.Command("rundll32", "url.dll,FileProtocolHandler", "https://developer.microsoft.com/en-us/microsoft-edge/webview2/")
   168  	return cmd.Run()
   169  }