c# WebView2 user directory "Access Denied" - c#

I am having issues with the .NET WebView2 control. I thought I had it fixed but it is not working. I have read numerous posts to no avail.
I have a WPF C# application that runs on a server. Various people log into the server via a web browser and run the app.
Within this app, I open up a WebView2 browser, setting the user data directory to a unique directory for each person.
When I set the user data directory and call EnsureCoreWebView2Async(), I get an error in the exception code "Access is denied. (Exception from HRESULT: 0x80070005 (E_ACCESSDENIED)).
Below is the code:
public static async void InitializeWebView(WebView2 browser, string path)
{
Directory.CreateDirectory(path);
browser.CreationProperties = new CoreWebView2CreationProperties()
{
UserDataFolder = path
};
try
{
await browser.EnsureCoreWebView2Async();
}
catch( Exception ex)
{
Log.LogString("Ensure error: " + ex.Message);
}
}
I have tried various things without success. What am I doing wrong? Any suggestions?

I'm not all that familiar with CoreWebView2CreationProperties, but according to the documentation.
Its main purpose is to be set to CreationProperties in order to
customize the environment used by a WebView2 during implicit
initialization...If you need complete control over the environment used by a WebView2 control then you'll need to initialize the control explicitly by creating your own environment with CreateAsync(String, String, CoreWebView2EnvironmentOptions) and passing it to EnsureCoreWebView2Async(CoreWebView2Environment) before you set the Source property to anything.
As mentioned in the documentation referenced above, implicit initialization occurs when the Source property is set and CoreWebView2 hasn't been explicitly initialized.
To explicitly initialize CoreWebView2, try the following:
public async Task InitializeCoreWebView2Async(WebView2 wv, string userDataFolder = null)
{
//initialize CoreWebView2
CoreWebView2EnvironmentOptions options = null;
CoreWebView2Environment cwv2Environment = null;
//it's recommended to create the userDataFolder in the same location
//that your other application data is stored (ie: in a folder in %APPDATA%)
//if not specified, we'll create a folder in %TEMP%
if (String.IsNullOrEmpty(userDataFolder))
userDataFolder = Path.Combine(Path.GetTempPath(), System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
//create WebView2 Environment using the installed or specified WebView2 Runtime version.
//cwv2Environment = await CoreWebView2Environment.CreateAsync(#"C:\Program Files (x86)\Microsoft\Edge Dev\Application\1.0.1054.31", userDataFolder, options);
cwv2Environment = await CoreWebView2Environment.CreateAsync(null, userDataFolder, options);
//initialize
await wv.EnsureCoreWebView2Async(cwv2Environment);
System.Diagnostics.Debug.WriteLine("UserDataFolder: " + userDataFolder);
}
Note: If one desires to explicitly initialize CoreWebView2, it must be done prior to setting the Source property for the WebView2 control.
Usage:
await InitializeCoreWebView2Async(webView21, Path.Combine(#"C:\Temp", System.Reflection.Assembly.GetExecutingAssembly().GetName().Name));
Resources:
CoreWebView2 Class
CoreWebView2CreationProperties
CoreWebView2Environment Class
CoreWebView2Environment.CreateAsync
WebView2.EnsureCoreWebView2Async(CoreWebView2Environment) Method

Thank you for your continued help. Due to character limitations, I am responding to your questions in an Answer section. In response to your comments...
If the Source was being set prior to initialization, I would be getting a different error (which I have had in the past).
Here is the xaml for the WebView2 control:
<Wpf:WebView2 Name="Browser" Margin="20,20,20,20" CoreWebView2InitializationCompleted="Browser_CoreWebView2InitializationCompleted" />
As you can see, I am subscribed to the InitializationComplete event, which is not being called due to the error.
Here is the source for the form that hosts the WebView2 control:
private async void _DoIt()
{
try
{
var env = await CoreWebView2Environment.CreateAsync(null, Global._GetServerDataDirectory(_Person));
await Browser.EnsureCoreWebView2Async(env);
}
catch( Exception ex)
{
Log.LogString("DoIt error: " + ex.Message);
}
}
public PayPal(Person person)
{
InitializeComponent();
_Person = person;
_DoIt();
Log.LogString("After PayPal constructor");
}
It is erroring in _DoIt().

Okay, after many hours of trying to track this down, the end issue is that Thinfinity's VirtualUI product does not support an application using WebView2. This was my core issue and has been confirmed by the vendor.
Thank you once again for everyone's kind help with this.

Related

Winforms WebView2 with fixed runtime not working from shared folder

I'm using a fixed version of WebView2 runtime like described in WebView2 working without WebView2 Runtime.
When I save this fixed runtime folder on my local machine it works fine. But as soon as I try to access the exact same folder on a shared folder/server it does not work. Any ideas?
public Form1()
{
InitializeComponent();
this.InitWeb();
}
public async void InitWeb()
{
var webEnv = await CoreWebView2Environment.CreateAsync(#"\\SharedFolder\FixedVersionFolder", Path.GetTempPath());
//var webEnv = await CoreWebView2Environment.CreateAsync(#"C:\FixedVersionFolder", Path.GetTempPath());
await this.webView21.EnsureCoreWebView2Async(webEnv);
webView21.Source = new Uri("https://www.microsoft.com");
}
In this case the webview does not show but if I replace the webEnv init line with the commented one, which points to my local folder it works just fine. I have tried multiple shared folders including creating one on my computer and it does not work.
The solution that worked for me was setting the WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS to --no-sandbox before the initialization of webview. I'm aware that this can cause some security risk, so use at your own risk.
Environment.SetEnvironmentVariable("WEBVIEW2_ADDITIONAL_BROWSER_ARGUMENTS", "--no-sandbox");

How to save data to network path on .NET?

I'm really struggling with saving data to my local network NAS (a Synology DS214 if that matters).
I need to store some files in my network folders after creating them in another part of my program, but I haven't been able to handle the authentication/permissions properly.
My code atm is this:
WrapperImpersonationContext WIContext =
new WrapperImpersonationContext("\\\\DiskStation", "admin", "admin");
try
{
WIContext.Enter();
// code to select the final path simplified.
string fileName = "file.txt";
string originalPath = Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments);
originalPath= Path.Combine(new string[] {originalPath, fileName});
string finalPath = "\\\\DiskStation\\Virtual\\DestFolder";
if (!Directory.Exists(finalPath))
{
// This goes well for whatever reason
Directory.CreateDirectory(finalPath);
}
finalPath = Path.Combine(new string[] {finalPath, fileName});
// This fails for wrong username/password
File.Move(originalPath, finalPath);
} catch (Exception ex)
{
// Exception showing simplified here
MessageBox.Show(ex.ToString());
throw;
} finally
{
WIContext.Leave();
}
The code used for the WrapperImpersonationContext I found here:
WindowsImpersonationContext made easy
As written in my code when I try to move the file I get an UnauthorizedAccessException: Access to the path is denied. I also tried to create a new file in the network folder with the same results.
While looking at the Michiel Vanotegem's code linked above, I discovered that I get an authentication error calling the LogonUser function (error code 1326 that gets me a Win32Exception (0x80004005): The user name or password is incorrect).
I tried to use the WNetUseConnection function looking at this and this pages but while I get no error from the function (after substituting it in the Michiel code), when I try to move the file I get the same UnauthorizedAccessException: Access to the path is denied.
I also tried to fiddle with the domain passed to the Impersonation Wrapper but I couldn't seem to make it work. I feel like I'm missing something... Can someone kindly point me to the right direction or help me with this issue?
Ty all who contributes in advance.
Edit 15/12/2017 11:52: I discovered that if I try to rerun the LogonUser function immediately after the first error I get a different exception (error 87 Win32Exception (0x80004005): The parameter is incorrect)
I followed on #LennartStoop suggestion, so I enclosed my code in a using block instead of a try finally using the code I borrowed from this answer:
using (NetworkConnection netConn =
new NetworkConnection("\\\\DiskStation", new NetworkCredential("admin", "admin")))
{
// My code here
}
Using this I've been able to establish a connection the network folder and perform all the IO operation I needed so ty very much for the tip Lennart :)

Xamarin Android - Exception when calling Environment.GetExternalStorageState(File path)

I’m unable to use the Android.OS.Environment.GetExternalStorageState method as outlined here.
The Android.OS.Environment.GetExternalStorageState property works. However in order to query the state the secondary external SD card’s state I need to use the method (not the property) and pass in the path to the secondary external SD card. When calling this method I receive a .NET exception as follows:
No static method with name=’getExternalStorageState’
signature=’(Ljava/io/File;)Ljava/lang/String;’ in class
Landroid/os/Environment;
A sample app that reliably reproduces the problem can be found here.
Note that this is on a device that has 2 SD cards. One is internal (not removable) and is the default SD card path. The other SD card is an external SD card which is removable. This test app assumes the external secondary removable SD card is at a root path of ‘sdcard1’ and the default internal is ‘sdcard0’. You may need to change this if your device is different.
Here is a trimmed down version of the CheckState from the sample app.
private bool CheckState(string pathStr)
{
try
{
var path = new File(pathStr);
var otherState = Environment.ExternalStorageState;
var state = Environment.GetExternalStorageState(path);
// Good state - set values and be done
return state.Equals(Environment.MediaMounted);
}
catch (Java.Lang.Exception ex)
{
Debug(Tag, $"Java Ex: {ex.Message}");
}
catch (Exception ex)
{
Debug(Tag, $".Net Ex: {ex.Message}");
}
return false;
}
In the example code above, the otherState variable is set with the state of the internal SD card. However when the next line with the state variable executes an exception is thrown.
Also, as an FYI - it is the .Net System.Exception that is thrown, no the Java.Lang.Exception.
If this code is correct and there is in fact a bug in the framework code somewhere (Xamarin and/or Android) then is there a recommended work around to determine if an SD card is available for reading & writing?
No static method with name=’getExternalStorageState’ signature=’(Ljava/io/File;)Ljava/lang/String;’ in class Landroid/os/Environment;
String getExternalStorageState (File path) was added in API 21.
re: https://developer.android.com/reference/android/os/Environment.html#getExternalStorageState(java.io.File)

CaptureSource.Start() throws System.UnauthorizedAccessException in Windows Phone Silverlight 8.1

I have problem with using camera in Windows Phone Silverlight 8.1 application. I want just to initialize camera and see its preview (for now I don't need any photos or video capture). I have found nice and simple example on MSDN and
private CaptureSource captureSource;
private VideoCaptureDevice videoCaptureDevice;
private void InitializeVideoRecorder()
{
try
{
if (captureSource == null)
{
captureSource = new CaptureSource();
var a = captureSource.VideoCaptureDevice;
videoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
captureSource.CaptureFailed += OnCaptureFailed;
if (videoCaptureDevice != null)
{
VideoRecorderBrush = new VideoBrush();
VideoRecorderBrush.SetSource(captureSource);
captureSource.Start();
CameraStatus = "Tap record to start recording...";
}
else
{
CameraStatus = "A camera is not supported on this phone.";
}
}
}
catch (Exception ex)
{
CameraStatus = "ERROR: " + ex.Message.ToString();
}
}
The code stops at captureSource.Start(); throwing System.UnauthorizedAccessException: Attempted to perform an unauthorized operation..
First of all I found information (on the same page) that ID_CAP_ISV_CAMERA capability is needed in `WMAppManifest.xml'. But I have problem with adding it, because:
I can't find this capability in designer
I get error when I add it manualy to .xml file
Error reproduced below:
Warning 1 The 'Name' attribute is invalid - The value 'ID_CAP_ISV_CAMERA' is invalid according to its datatype 'http://schemas.microsoft.com/appx/2010/manifest:ST_Capabilities' - The Enumeration constraint failed.
Error 3 App manifest validation failed. Value 'ID_CAP_ISV_CAMERA' of attribute '/Package/Capabilities/Capability/#Name' must be a valid capability.
I have even found the same solution on SO WP8.1 SilverLight Microsoft.Devices.PhotoCamera Access Denied
Can somebody tell me why can't I use original MSDN solution to this problem?
First, it looks like you're trying to add that capability to Package.appxmanifest instead of WMAppManifest.xml. You should be able to find WMAppManifest.xml under Solution Explorer -> <your project> -> Properties:
Opening that file should give you the option to add ID_CAP_* capabilities.
Second, you need to specify both ID_CAP_ISV_CAMERA and ID_CAP_MICROPHONE in order to use CaptureSource.Start(), even if you're only using one of the devices.

What is the best practice for ensuring permissions are correct during the launch of an ASP.NET application

I have an ASP.NET application which requires write access on the App_Data subfolder. The MSI used to deploy the application tries to set the permissions correctly, but in spite of this, it seems the permissions are sometimes wrong. Most of the application works fine without this permission. I would prefer that the application fails to start if the permissions are wrong.
What is the best practice for ensuring that the necessary permissions are correct for the IIS user context? Ideally I want to display some simple instructions for fixing whatever is wrong. And I want the message to appear in as many incorrect configurations as possible.
The following describes what I've tried so far, until I realised there's a probably a better or standard way.
I tried putting this in Application_Start()
protected void Application_Start(Object sender, EventArgs e)
{
// Assert permissions on writeable folders are correct
var permissionsChecker = new AppDataPermissionsChecker();
permissionsChecker.AssertFolderIsWriteable(
HttpContext.Current.Server.MapPath("~/App_Data"));
// remainder of Application_Start()...
}
where AppDataPermissionsChecker is defined as follows:
public class AppDataPermissionsChecker
{
private bool CanWriteAccessToFolder(string folderPath)
{
try
{
// Attempt to get a list of security permissions from the folder.
// This will raise an exception if the path is read only or do not have access to view the permissions.
DirectorySecurity directorySecurity = Directory.GetAccessControl(folderPath);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
}
public void AssertFolderIsWriteable(string folderPath)
{
if (!Directory.Exists(folderPath))
throw new Exception(String.Format("The {0} folder does not exist.", folderPath));
if (!CanWriteAccessToFolder(folderPath))
throw new Exception(String.Format("The ASPNET user does not have "
+ "access to the {0} folder. Please ensure the ASPNET user has "
+ "read/write/delete access on the folder. See 'The App_Data folder' "
+ "here: http://msdn.microsoft.com/en-us/library/06t2w7da.aspx'",
folderPath));
}
}
I thought this would throw an ugly exception if the rights are incorrect (which is better than nothing), but in some situations I just get an HTTP Error 503.
I found this implementation of a diagnostics page which does exactly what I was looking for (and more besides).

Categories

Resources