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 }