diff --git a/website/docs/guides/troubleshooting.mdx b/website/docs/guides/troubleshooting.mdx index 1ce18f2d5f2..cf22c99070f 100644 --- a/website/docs/guides/troubleshooting.mdx +++ b/website/docs/guides/troubleshooting.mdx @@ -203,6 +203,153 @@ you can make the webview background transparent using the following config: }) ``` +## I get a "Microsoft Edge can't read or write to its data directory" error when running my program as admin on Windows + +You set your program to require admin permissions and it worked great! Unfortunately, some users are seeing a "Microsoft Edge can't read or write to its data directory" error when running it. + +When a Windows machine has two local accounts: + +- Alice, an admin +- Bob, a regular user + +Bob sees a UAC prompt when running your program. Bob enters Alice's admin credentials into this prompt. The app launches with admin permissions under Alice's account. + +Wails instructs WebView2 to store user data at the specified `WebviewUserDataPath`. It defaults to `%APPDATA%\[BinaryName.exe]`. + +Because the application is running under Alice's account, `%APPDATA%\[BinaryName.exe]` resolves to `C:\Users\Alice\AppData\Roaming\[BinaryName.exe]`. + +WebView2 [creates some child processes under Bob's logged-in account instead of Alice's admin account](https://github.com/MicrosoftEdge/WebView2Feedback/issues/932#issue-807464179). Since Bob cannot access `C:\Users\Alice\AppData\Roaming\[BinaryName.exe]`, the "Microsoft Edge can't read or write to its data directory" error is shown. + +Possible solution #1: + +Refactor your application to work without constant admin permissions. If you just need to perform a small set of admin tasks (such as running an updater), you can run your application with the minimum permissions and then use the `runas` command to run these tasks with admin permissions as needed: + +```go +//go:build windows + +package sample + +import ( + "golang.org/x/sys/windows" + "syscall" +) + +// Calling RunAs("C:\path\to\my\updater.exe") shows Bob a UAC prompt. Bob enters Alice's admin credentials. The updater launches with admin permissions under Alice's account. +func RunAs(path string) error { + verbPtr, _ := syscall.UTF16PtrFromString("runas") + exePtr, _ := syscall.UTF16PtrFromString(path) + cwdPtr, _ := syscall.UTF16PtrFromString("") + argPtr, _ := syscall.UTF16PtrFromString("") + + var showCmd int32 = 1 //SW_NORMAL + + err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd) + if err != nil { + return err + } + return nil +} +``` + +Possible solution #2: + +Run your application with extended permissions. If you absolutely must run with constant admin permissions, WebView2 will function correctly if you use a data directory accessible by both users and you also launch your app with the `SeBackupPrivilege`, `SeDebugPrivilege`, and `SeRestorePrivilege` permissions. Here's an example: + +```go +package main + +import ( + "embed" + "os" + "runtime" + + "github.com/fourcorelabs/wintoken" + "github.com/hectane/go-acl" + "github.com/wailsapp/wails/v2" + "github.com/wailsapp/wails/v2/pkg/options" + "github.com/wailsapp/wails/v2/pkg/options/assetserver" + "github.com/wailsapp/wails/v2/pkg/options/windows" +) + +//go:embed all:frontend/dist +var assets embed.FS + +const ( + fixedTokenKey = "SAMPLE_RANDOM_KEY" + fixedTokenVal = "with-fixed-token" + webviewDir = "C:\\ProgramData\\Sample" +) + +func runWithFixedToken() { + println("Re-launching self") + token, err := wintoken.OpenProcessToken(0, wintoken.TokenPrimary) //pass 0 for own process + if err != nil { + panic(err) + } + defer token.Close() + + token.EnableTokenPrivileges([]string{ + "SeBackupPrivilege", + "SeDebugPrivilege", + "SeRestorePrivilege", + }) + + cmd := exec.Command(os.Args[0]) + cmd.Args = os.Args + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, fmt.Sprintf("%v=%v", fixedTokenKey, fixedTokenVal)) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + cmd.SysProcAttr = &syscall.SysProcAttr{Token: syscall.Token(token.Token())} + if err := cmd.Run(); err != nil { + println("Error after launching self:", err) + os.Exit(1) + } + println("Clean self launch :)") + os.Exit(0) +} + +func main() { + if runtime.GOOS == "windows" && os.Getenv(fixedTokenKey) != fixedTokenVal { + runWithFixedToken() + } + + println("Setting data dir to", webviewDir) + if err := os.MkdirAll(webviewDir, os.ModePerm); err != nil { + println("Failed creating dir:", err) + } + if err := acl.Chmod(webviewDir, 0777); err != nil { + println("Failed setting ACL on dir:", err) + } + + app := NewApp() + + err := wails.Run(&options.App{ + Title: "sample-data-dir", + Width: 1024, + Height: 768, + AssetServer: &assetserver.Options{ + Assets: assets, + }, + Bind: []interface{}{ + app, + }, + Windows: &windows.Options{ + WebviewUserDataPath: webviewDir, + }, + }) + + if err != nil { + println("Error:", err.Error()) + } +} +``` + +If you use a data directory accessible by both users but not the extended privileges, you will receive a WebView2 `80010108 The object invoked has disconnected from its clients` error. + +Possible future solution #3: [run WebView2 using an in-memory mode if implemented](https://github.com/MicrosoftEdge/WebView2Feedback/issues/3637#issuecomment-1728300982). + ## WebView2 installation succeeded, but the wails doctor command shows that it is not installed If you have installed WebView2, but the `wails doctor` command shows that it is not installed, it is likely that the @@ -213,3 +360,4 @@ Source: https://github.com/wailsapp/wails/issues/2917 ## WebVie2wProcess failed with kind If your Windows app generates this kind of error, you can check out what the error means [here](https://docs.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2processfailedkind?view=webview2-winrt-1.0.2045.28). +