Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Microsoft Edge can't read and write to its data directory #340

Closed
ndreisg opened this issue Jul 16, 2020 · 9 comments
Closed

Microsoft Edge can't read and write to its data directory #340

ndreisg opened this issue Jul 16, 2020 · 9 comments

Comments

@ndreisg
Copy link

ndreisg commented Jul 16, 2020

When I try to run my Windows Forms application with WebView2 from program files dir I get the following error:

---------------------------
We couldn't create the data directory
---------------------------
Microsoft Edge can't read and write to its data directory:
    C:\Program Files (x86)\MyWebViewApplication\MyWebViewApplication.exe.WebView2\EBWebView

The application is not elevated so I guess it doesn't have access in program files.
How do I change the data directory to another location?

@cgeier
Copy link

cgeier commented Jul 16, 2020

In Windows 10, the "Program Files" (and "Program Files (x86)") folder is protected. See #324 and #297 for more information.

@ndreisg
Copy link
Author

ndreisg commented Jul 16, 2020

I now tried to change the userDataFolder like suggested here #324 (comment) but it had no effect at all :/

var env = await CoreWebView2Environment.CreateAsync(null, @"C:\Temp", null);
await webView.EnsureCoreWebView2Async(env);

@ndreisg
Copy link
Author

ndreisg commented Jul 16, 2020

I created my WebView in Forms Designer. The Source property of the WebView is set to about:blank by default.
Apparently clearing the property value is not supported since I get an error when I try to.
But the documentation of EnsureCoreWebView2Async function states the following:

Calling this method after initialization has been implicitly triggered by setting the Microsoft.Web.WebView2.WinForms.WebView2.Source property will have no effect (any specified environment is ignored) and simply return a Task representing that initialization already in progress.

Can you suggest any best practice how to change the userDataFolder when the WebView was created in Forms Designer?

@cgeier
Copy link

cgeier commented Jul 16, 2020

There is an example here.

The different versions of MS Edge that are installed can be found in
%ProgramFiles(x86)%\Microsoft\Edge\Application\version folder

The path to the "Edge\Application" folder can be found in the registry:
HKLM\Software\Microsoft\Windows\CurrentVersion\App Path\msedge.exe . Look at "Path"

MSEdgePathInRegistry

MSEdgeApplicationFolder

MSEdgeOtherVersions

However, the regular versionof MS Edge doesn't support WebView2 yet. So you have to use a canary/dev version.

It looks like the canary/dev version of MS Edge, is in:
%ProgramFiles(x86)%\Microsoft\Edge Dev\Application\version folder

DevMSEdgePathInRegistry

DevMSEdgeApplicationFolder

DevMSEdgeOtherVersions

You should be able to do something like the following. The name of the WebView2 control in the following code, is "webView2Ctl":

             ...
using System.Diagnostics;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using Microsoft.Web.WebView2;
private void Form1_Load(object sender, EventArgs e)
{
    InitializeWebView2Async();
    //InitializeWebView2Async(@"C:\temp");
}


private async void InitializeWebView2Async(string tempDir = "")
{
    CoreWebView2Environment webView2Environment = null;

    //set value
    string tempDir2 = tempDir;

    if (String.IsNullOrEmpty(tempDir2))
    {
        //get fully-qualified path to user's temp folder
        tempDir2 = Path.GetTempPath();
    }//if

    //add event handler for CoreWebView2Ready - before webView2Ctl is initialized
    //it's important to not use webViewCtrl until CoreWebView2Ready event is thrown
    webView2Ctl.CoreWebView2Ready += WebView2Ctl_CoreWebView2Ready;

    CoreWebView2EnvironmentOptions options = null;
    //options = new CoreWebView2EnvironmentOptions("--disk-cache-size=200");
    //options = new CoreWebView2EnvironmentOptions("–incognito ");

    //set webView2 temp folder. The temp folder is used to store webView2
    //cached objects. If not specified, the folder where the executable
    //was started will be used. If the user doesn't have write permissions
    //on that folder, such as C:\Program Files\<your application folder>\,
    //then webView2 will fail. 

    //webView2Environment = await CoreWebView2Environment.CreateAsync(@"C:\Program Files (x86)\Microsoft\Edge Dev\Application\85.0.564.8", tempDir2, options);
    webView2Environment = await CoreWebView2Environment.CreateAsync(null, tempDir2, options);

    //webView2Ctl must be inialized before it can be used
    //wait for coreWebView2 initialization
    //when complete, CoreWebView2Ready event will be thrown
    await webView2Ctl.EnsureCoreWebView2Async(webView2Environment);

    //add other event handlers - after webView2Ctrl is initialized
    //webView2Ctl.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted;
    //webView2Ctl.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;
    //webView2Ctl.NavigationCompleted += WebView2Ctl_NavigationCompleted;
    //webView2Ctl.NavigationStarting += WebView2Ctl_NavigationStarting;

}

private void WebView2Ctl_CoreWebView2Ready(object sender, EventArgs e)
{
     System.Diagnostics.Debug.Print("Info: WebView2Ctl_CoreWebView2Ready"); 

}

See #329 for more info.

To see the version of MS Edge, go/navigate to "edge://version/" (ie: enter "edge://version" in the address bar).

@cgeier
Copy link

cgeier commented Jul 17, 2020

I guess I must have missed that. It seems to me that one should be able to set the "Source" property to null (or an empty string), since setting CoreWebView2Environment doesn't work after the "Source" property is set (as you stated). I tested the following workaround. If you go into the form designer (ex: Form1.Designer.cs), and comment out the line that sets the "Source" property (as seen below)

            // 
            // webView2Ctl
            // 
            this.webView2Ctl.Dock = System.Windows.Forms.DockStyle.Fill;
            this.webView2Ctl.Location = new System.Drawing.Point(0, 0);
            this.webView2Ctl.Name = "webView2Ctl";
            this.webView2Ctl.Size = new System.Drawing.Size(705, 360);
            //this.webView2Ctl.Source = new System.Uri("about:blank", System.UriKind.Absolute);
            this.webView2Ctl.TabIndex = 0;
            this.webView2Ctl.Text = "webView";
            this.webView2Ctl.ZoomFactor = 1D;

Then set the "Source" property after "EnsureCoreWebView2Async", it will work.

        ...
await webView2Ctl.EnsureCoreWebView2Async(webView2Environment);

webView2Ctl.Source = new Uri("about:blank", UriKind.Absolute);
        ...

Here's the code from above with the modification. Also, as you can see, in the code below, I've changed it to use "C:\Temp" as the data directory.

private void Form1_Load(object sender, EventArgs e)
{
    //InitializeWebView2Async();
    InitializeWebView2Async(@"C:\Temp");
}


private async void InitializeWebView2Async(string tempDir = "")
{
    CoreWebView2Environment webView2Environment = null;

    //set value
    string tempDir2 = tempDir;

    if (String.IsNullOrEmpty(tempDir2))
    {
        //get fully-qualified path to user's temp folder
        tempDir2 = Path.GetTempPath();
    }//if

    //add event handler for CoreWebView2Ready - before webView2Ctl is initialized
    //it's important to not use webViewCtrl until CoreWebView2Ready event is thrown
    webView2Ctl.CoreWebView2Ready += WebView2Ctl_CoreWebView2Ready;

    CoreWebView2EnvironmentOptions options = null;
    //options = new CoreWebView2EnvironmentOptions("--disk-cache-size=200");
    //options = new CoreWebView2EnvironmentOptions("–incognito ");

    //set webView2 temp folder. The temp folder is used to store webView2
    //cached objects. If not specified, the folder where the executable
    //was started will be used. If the user doesn't have write permissions
    //on that folder, such as C:\Program Files\<your application folder>\,
    //then webView2 will fail. 

    //webView2Environment = await CoreWebView2Environment.CreateAsync(@"C:\Program Files (x86)\Microsoft\Edge Dev\Application\85.0.564.8", tempDir2, options);
    webView2Environment = await CoreWebView2Environment.CreateAsync(null, tempDir2, options);

    //webView2Ctl must be inialized before it can be used
    //wait for coreWebView2 initialization
    //when complete, CoreWebView2Ready event will be thrown
    await webView2Ctl.EnsureCoreWebView2Async(webView2Environment);

    webView2Ctl.Source = new Uri("about:blank", UriKind.Absolute);

    //add other event handlers - after webView2Ctrl is initialized
    //webView2Ctl.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted;
    //webView2Ctl.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;
    //webView2Ctl.NavigationCompleted += WebView2Ctl_NavigationCompleted;
    //webView2Ctl.NavigationStarting += WebView2Ctl_NavigationStarting;

}

private void WebView2Ctl_CoreWebView2Ready(object sender, EventArgs e)
{
     System.Diagnostics.Debug.Print("Info: WebView2Ctl_CoreWebView2Ready"); 

}

When setting CoreWebView2Environment works, it creates a folder named "EBWebView" at the path that has been specified (ex: C:\Temp\EBWebView).

EBWebViewFolder

EBWebViewFolderContents

Looks like this issue is mentioned in #177, as well as on StackOverflow-although in the StackOverflow post someone is using it with WPF.

@ndreisg
Copy link
Author

ndreisg commented Jul 17, 2020

Thanks @cgeier that seems to work.
Yes it seems it should be possible to clear the source property or maybe to have an additional property for the data directory.

@pagoe-msft
Copy link

Closing this issue - let me know if it should be re-opened.

@cgeier
Copy link

cgeier commented Jul 20, 2020

@pagoe-msft Setting "CoreWebView2Environment" when the WebView2 control was created with the Visual Studio Forms designer, doesn't appear to work. As described above, one needs to comment out (remove) the definition of the "Source" property because setting the "Source" property initializes the WebView2 control. Creating a new instance of WebView2 doesn't seem to have any effect. It seems, from my testing, that once the initialization has occurred, that initialization exists for all other WebView2 instances and can't be changed. This seems like undesirable behavior.

@cgeier
Copy link

cgeier commented Sep 13, 2020

@ndreisg It appears the code I posted above is flawed. InitializeWebView2Async should be "Task" not "void" and it should be called using "await". Without using "await" in the "Load" event handler, the code won't execute in the expected order.

Instead of:

private void Form1_Load(object sender, EventArgs e)
{
    ...
}

it should be:

private async void Form1_Load(object sender, EventArgs e)
{
    ...
}

Instead of:

private async void InitializeWebView2Async(string tempDir = "")
{
    ...
}

It should be:

private async Task InitializeWebView2Async(string tempDir = "")
{
    ...
}

Here's a corrected example:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.WinForms;
using System.Diagnostics;

namespace TestWebView2
{
    public partial class Form1 : Form
    {
        //create new instance of WebView2
        Microsoft.Web.WebView2.WinForms.WebView2 webView21 = new Microsoft.Web.WebView2.WinForms.WebView2();
        public Form1()
        {
            InitializeComponent();

            //set WebView2 properties and add to form
            webView21.Location = new System.Drawing.Point(10, 10);
            webView21.Size = new System.Drawing.Size(800, 600);

            //don't set Source property here

            this.Controls.Add(webView21);
        }

        private async void Form1_Load(object sender, EventArgs e)
        {
            // add event handler
            webView21.CoreWebView2Ready += webView21_CoreWebView2Ready;

            Debug.Print("Info: initializing CoreWebView2");

            //initialize CoreWebView2
            await InitializeAsync();

            Debug.Print("Info: after initializing CoreWebView2");

            if (webView21 != null && webView21.CoreWebView2 != null)
            {
                //navigate to website - option 1
                webView21.Source = new Uri("https://www.microsoft.com", UriKind.Absolute);

                //navigate to website - option 2
                //webView21.CoreWebView2.Navigate("https://www.microsoft.com");
            }//if
        }

        private void webView21_CoreWebView2Ready(object sender, EventArgs e)
        {
            Debug.Print("Info: webView21_CoreWebView2Ready");

            //subscribe to events (add event handlers) - CoreWebView2
            webView21.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted;

            //subscribe to events (add event handlers) - WebView2
            webView21.NavigationStarting += webView21_NavigationStarting;
            webView21.NavigationCompleted += webView21_NavigationCompleted;

            Debug.Print("Info: leaving webView21_CoreWebView2Ready");
        }

        private async Task InitializeAsync()
        {
            Debug.Print("Info: before EnsureCoreWebView2Async");

            // wait for coreWebView2 initialization
            await webView21.EnsureCoreWebView2Async();

            Debug.Print("Info: after EnsureCoreWebView2Async");
        }
        private void webView21_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
        {
            Debug.Print("Info: webView21_NavigationCompleted");
        }

        private void CoreWebView2_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
        {
            Debug.Print("Info: CoreWebView2_NavigationCompleted");
        }

        private void webView21_NavigationStarting(object sender, CoreWebView2NavigationStartingEventArgs e)
        {
            Debug.Print("Info: webView21_NavigationStarting - Navigating to '" + e.Uri.ToString() + "'");
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants