Writing to a file using FileSavePicker - c#

I'm having a little difficulty figuring out what the cause of this error is. I've added FilePicker capabilities in the Manifest, and it's not like I'm trying to do anything crazy; just trying to save to a sub folder within the Documents folder...
Error: "An unhandled exception of type
'System.UnauthorizedAccessException' occurred in mscorlib.dll
Additional information: Access is denied. (Exception from HRESULT:
0x80070005 (E_ACCESSDENIED))"
I have confirmed that my User Account is Admin and that it has Full Control over folders and files. But I'm not sure what else I can try.
public void NewBTN_Click(object sender, RoutedEventArgs e)
{
var mbox = new MessageDialog("Would you like to save changes before creating a new Note?", "Note+ Confirmation");
UICommand YesBTN = new UICommand("Yes", new UICommandInvokedHandler(OnYesBTN));
UICommand NoBTN = new UICommand("No", new UICommandInvokedHandler(OnNoBTN));
mbox.Commands.Add(YesBTN);
mbox.Commands.Add(NoBTN);
mbox.DefaultCommandIndex = 1;
mbox.ShowAsync().Start();
}
async void OnYesBTN(object command)
{
this.Dispatcher.Invoke(Windows.UI.Core.CoreDispatcherPriority.Normal, (s, a) =>
{
// User clicked yes. Show File picker.
HasPickedFile = true;
}, this, null);
if (HasPickedFile)
{
FileSavePicker savePicker = new FileSavePicker();
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
// Dropdown of file types the user can save the file as
savePicker.FileTypeChoices.Add("Cascading Stylesheet", new List<string>() { ".css" });
savePicker.FileTypeChoices.Add("Hypertext Markup Language", new List<string>() { ".html" });
savePicker.FileTypeChoices.Add("Plain Text", new List<string>() { ".txt" });
// Default extension if the user does not select a choice explicitly from the dropdown
savePicker.DefaultFileExtension = ".txt";
// Default file name if the user does not type one in or select a file to replace
savePicker.SuggestedFileName = "New Note";
StorageFile savedItem = await savePicker.PickSaveFileAsync();
if (null != savedItem)
{
// Application now has read/write access to the saved file
StorageFolder sFolder = await StorageFolder.GetFolderFromPathAsync(savedItem.Path);
try
{
StorageFile sFile = await sFolder.GetFileAsync(savedItem.FileName);
IRandomAccessStream writeStream = await sFile.OpenAsync(FileAccessMode.ReadWrite);
IOutputStream oStream = writeStream.GetOutputStreamAt(0);
DataWriter dWriter = new DataWriter(oStream);
dWriter.WriteString(Note.Text);
await dWriter.StoreAsync();
oStream.FlushAsync().Start();
// Should've successfully written to the file that Windows FileSavePicker had created.
}
catch
{
var mbox = new MessageDialog("This file does not exist.", "Note+ Confirmation");
UICommand OkBTN = new UICommand("Ok", new UICommandInvokedHandler(OnOkBTN));
mbox.Commands.Add(OkBTN);
mbox.DefaultCommandIndex = 1;
mbox.ShowAsync().Start();
}
}
}
}
public void OnOkBTN(object command)
{
this.Dispatcher.Invoke(Windows.UI.Core.CoreDispatcherPriority.Normal, (s, a) =>
{
// Do something here.
}, this, null);
}
public void OnNoBTN(object command)
{
this.Dispatcher.Invoke(Windows.UI.Core.CoreDispatcherPriority.Normal, (s, a) =>
{
// Don't save changes. Just create a new blank Note.
Note.Text = String.Empty;
}, this, null);
}
How can I write to a file that was created by the FileSavePicker?

You don't need to call StorageFolder.GetFolderFromPathAsync(savedItem.Path) and sFolder.GetFileAsync(savedItem.FileName). You must remove these two lines because they throw exception.
You should use the StorageFile object which has been returned by method savePicker.PickSaveFileAsync(), because that object has all permissions. Then you can simply call savedItem.OpenAsync(FileAccessMode.ReadWrite).

You probably do not have "Document Library Access" enabled in the capabilities portion of the appxmanifest of your app. Without this capability, windows will restrict access to the file system. There are similar capabilities for music, video, and picture libraries.
You have already added "File Picker" to the declarations portion, which is probably not what you want. The "File Picker" declaration indicates that if some other application invokes the file picker, your app will be listed as a possible source of files.

I've also found that adding Video or Picture Libraries access capability in Manifest only takes effect after restarting Windows 10.
Maybe it is an issue with my computer, but I think it is worth to share.

Related

Directory.GetDirectories return empty string inside an async Task operation

I have a UWP application which perform to capture and process images from a camera. This project leverage Microsoft Cognitive Services Face Recognition API and I'm exploring the application's existing functionality for awhile now. My goal is that when the image of a person is identified by the camera (through Face Recognition API service), I want to show the associated image of that person.
With that, the images are captured and stored in a local directory of my machine. I want to retrieve the image file and render it on the screen once the person is identified.
The code below shows the async Task method ProcessCameraCapture
private async Task ProcessCameraCapture(ImageAnalyzer e)
{
if (e == null)
{
this.UpdateUIForNoFacesDetected();
this.isProcessingPhoto = false;
return;
}
DateTime start = DateTime.Now;
await e.DetectFacesAsync();
if (e.DetectedFaces.Any())
{
string names;
await e.IdentifyFacesAsync();
this.greetingTextBlock.Text = this.GetGreettingFromFaces(e, out names);
if (e.IdentifiedPersons.Any())
{
this.greetingTextBlock.Foreground = new SolidColorBrush(Windows.UI.Colors.GreenYellow);
this.greetingSymbol.Foreground = new SolidColorBrush(Windows.UI.Colors.GreenYellow);
this.greetingSymbol.Symbol = Symbol.Comment;
GetSavedFilePhoto(names);
}
else
{
this.greetingTextBlock.Foreground = new SolidColorBrush(Windows.UI.Colors.Yellow);
this.greetingSymbol.Foreground = new SolidColorBrush(Windows.UI.Colors.Yellow);
this.greetingSymbol.Symbol = Symbol.View;
}
}
else
{
this.UpdateUIForNoFacesDetected();
}
TimeSpan latency = DateTime.Now - start;
this.faceLantencyDebugText.Text = string.Format("Face API latency: {0}ms", (int)latency.TotalMilliseconds);
this.isProcessingPhoto = false;
}
In GetSavedFilePhoto, I passed the string names argument once the person is identified.
Code below for the GetSavedFilePhoto method
private void GetSavedFilePhoto(string personName)
{
if (string.IsNullOrWhiteSpace(personName)) return;
var directoryPath = #"D:\PersonImages";
var directories = Directory.GetDirectories(directoryPath);
var filePaths = Directory.GetFiles(directoryPath, "*.jpg", SearchOption.AllDirectories);
}
However, in GetSavedFilePhoto method the variable directories returned an empty string of array when using directoryPath string variable. Directory "D:\PersonImages" is a valid and existing folder in my machine and, it contains subfolders with images inside. I also tried Directory.GetFiles to retrieve the jpg images but still returned an empty string.
I think it should work because I have used Directory class several times but not inside an asyncTask method. Does using async caused the files not returned when using I/O operation?
Sorry for this stupid question, but I really don't understand.
Any help is greatly appreciated.
Using Directory.GetFiles or Directory.GetDirectories method can get the folder/file in the local folder of the Application by the following code. But it could not open D:\.
var directories = Directory.GetDirectories(ApplicationData.Current.LocalFolder.Path);
In UWP app you can only access two locations at default (local folder and install folder), others need capabilities setting or file open picker.Details please reference file access permission.
If you need access to all files in D:\, the user must manually pick the D:\ drive using the FolderPicker, then you have permissions to access to files in this drive.
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.Thumbnail;
picker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.ComputerFolder;
picker.FileTypeFilter.Add(".jpg");
picker.FileTypeFilter.Add(".jpeg");
picker.FileTypeFilter.Add(".png");
Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
// Application now has read/write access to the picked file
}
else
{
//do some stuff
}

Prompt for file on application launch

I am making an application that can potentially be used on multiple computers by the same user. I currently have implemented backup and restore functionality to keep the SQLite database synchronized. However, when I install a new copy of the application, I would like to prompt the user to select a backup file (if applicable).
Initially, I thought that I would just put the logic in MainPageViewModel.xaml in the OnNavigatedTo method. The problem with that is that it would run through that logic every time navigating back to MainView. So, I thought that it would make sense to put it in OnApplicationLaunchAsync to only run once when the application is launched.
Here is my code currently:
private async Task<StorageFile> SelectFileAsync()
{
StorageFile pickedFile = null;
var settings = Container.Resolve<ISettings>();
var picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".sqlite");
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
var file = await picker.PickSingleFileAsync();
if (file != null)
{
var pickedFileToken = StorageApplicationPermissions.FutureAccessList.Add(file);
settings.BackupFileToken = pickedFileToken;
pickedFile = file;
}
return pickedFile;
}
protected async override Task OnLaunchApplicationAsync(LaunchActivatedEventArgs args)
{
var navigationService = Container.Resolve<INavigationService>();
var dialogService = Container.Resolve<IDialogService>();
var settings = Container.Resolve<ISettings>();
if (!string.IsNullOrEmpty(settings.BackupFileToken))
{
var backupFile =
await StorageApplicationPermissions.FutureAccessList.GetFileAsync(settings.BackupFileToken);
var properties = await backupFile.GetBasicPropertiesAsync();
dialogService.Show(string.Format("Backup file found: {0}\r\n" +
"Modified date: {1}", backupFile.Path, properties.DateModified), "");
navigationService.Navigate(Experiences.Main);
}
else
{
navigationService.Navigate(Experiences.Blank);
//dialogService.Show("No backup file specified. Please choose a backup file location.", "");
// TODO: load blank page, then select backup file location, then navigate to main page
await SelectFileAsync();
}
// do not need to return Task.FromResult when OnLaunchApplicationAsync() is async
//return Task.FromResult<object>(null);
}
As you can see, if the BackupFileToken exists, it will check the properties of the file and display a dialog with the file path and last modified date. My problem lies in the situation where it is a new installation/the backup token doesn't exist.
If I try to display a dialog before the FileOpenPicker then the application crashes. I thought perhaps adding a blank view to navigate to, then display the dialog and FileOpenPicker. It just seems like there should be a simpler way to accomplish this (without the need for extra views).
Can someone recommend a way to prompt the user to select a file when the application launches, but let them know the purpose of the FileOpenPicker?
I am aware that my await SelectFileAsync() is losing its return value, this is a temporary situation for testing purposes. I will be sure to assign the return value to the relevant variable once I have found a solution.
Maybe you can use the override
protected override void OnWindowCreated(WindowCreatedEventArgs args) in App.xaml.cs
which is also called once for the creation of the main window.

Windows 8 GetFileAsync method gives UnAuthorized error

The below code an attempt to try and get get an Mp3 file from the MusicLibrary
It gives me,
A first chance exception of type
'System.UnauthorizedAccessException'
occurred in AccessingPictures.exe
This is my code:
public async void getFile()
{
StorageFolder folder = KnownFolders.MusicLibrary;
try
{
sampleFile = await folder.GetFileAsync("Test1.mp3");
}
catch (FileNotFoundException e)
{
// If file doesn't exist, indicate users to use scenario 1
Debug.WriteLine(e);
}
}
private void btnRead_Click(object sender, RoutedEventArgs e)
{
getFile();
}
Wouldn't we able to access the media files?
I am able to do this using the file picker.
But it does not work while i try to access it directly.
Am i missing anything here ?
To retrieve Pictures from Camera Roll
Void GetCameraPhotos()
{
using (var library = new MediaLibrary())
{
PictureAlbumCollection allAlbums = library.RootPictureAlbum.Albums;
PictureAlbum cameraRoll = allAlbums.Where(album => album.Name == "Camera Roll").FirstOrDefault();
var CameraRollPictures = cameraRoll.Pictures
}
}
You cannot access the files unless it is in response to a user request. i.e. the user must tap a button or something and that tap logic ends up calling your code that accesses the file. If you want to get at the file afterwards, you'll need to copy it in the app's data folder.
I finally figured the issue. It was because i hadn't enabled the capabilities in the Manifest file.
It works like a charm now.
Thanks everyone.

How to Use IsolatedStorage to Set Lock Screen Background in Windows Phone 8

I would like to be able to use an image from my IsolatedStorage to modify the lock screen background, but I am having trouble getting the correct syntax of the IsolatedStorage file path to set the lock screen background.
In following http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj206968(v=vs.105).aspx , I am calling the LockHelper method in a button click event once an image has been selected from a List named Recent (which has been populated from PictureRepository.cs which loads images from IsolatedStorage)
private void recent_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
capturedPicture = (sender as LongListSelector).SelectedItem as CapturedPicture;
if (capturedPicture != null)
{
//filename is the name of the image in IsolatedStorage
fileName = capturedPicture.FileName;
}
}
void setAsLockScreenMenuItem_Click(object sender, EventArgs e)
{
if (!String.IsNullOrEmpty(fileName))
{
//PictureRepository.IsolatedStoragePath is a string = "Pictures"
LockHelper("isostore:/" + PictureRepository.IsolatedStoragePath + "/" + fileName, false); //results in FileNotFoundException
LockHelper(PictureRepository.IsolatedStoragePath + "/" + fileName, false); //results in ArgumentException
}
else
{
MessageBoxResult result = MessageBox.Show("You must select an image to set it as your lock screen.", "Notice", MessageBoxButton.OK);
if (result == MessageBoxResult.OK)
{
return;
}
}
}
Once LockHelper is called, the event proceeds
private async void LockHelper(string filePathOfTheImage, bool isAppResource)
{
try
{
var isProvider = Windows.Phone.System.UserProfile.LockScreenManager.IsProvidedByCurrentApplication;
if (!isProvider)
{
// If you're not the provider, this call will prompt the user for permission.
// Calling RequestAccessAsync from a background agent is not allowed.
var op = await Windows.Phone.System.UserProfile.LockScreenManager.RequestAccessAsync();
// Only do further work if the access was granted.
isProvider = op == Windows.Phone.System.UserProfile.LockScreenRequestResult.Granted;
}
if (isProvider)
{
// At this stage, the app is the active lock screen background provider.
// The following code example shows the new URI schema.
// ms-appdata points to the root of the local app data folder.
// ms-appx points to the Local app install folder, to reference resources bundled in the XAP package.
var schema = isAppResource ? "ms-appx:///" : "ms-appdata:///Local/";
var uri = new Uri(schema + filePathOfTheImage, UriKind.Absolute);
//The Error Occurs Here!
// Set the lock screen background image.
Windows.Phone.System.UserProfile.LockScreen.SetImageUri(uri);
// Get the URI of the lock screen background image.
var currentImage = Windows.Phone.System.UserProfile.LockScreen.GetImageUri();
System.Diagnostics.Debug.WriteLine("The new lock screen background image is set to {0}", currentImage.ToString());
}
else
{
MessageBoxResult result = MessageBox.Show("Cannot update your lock screen background at this time.", "Notice", MessageBoxButton.OK);
if (result == MessageBoxResult.OK)
{
return;
}
}
}
catch (System.Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex.ToString());
}
}
The error occurs on Windows.Phone.System.UserProfile.LockScreen.SetImageUri(uri); within the LockHelper method. It is either a FileNotFoundException mentioning The filename, directory name, or volume label syntax is incorrect. (Exception from HRESULT: 0x8007007B) or an ArgumentException. Everywhere I've referenced says for using IsolatedStorage the Uri should be ms-appdata:///local/ and then the IsolatedStorage file path (in my case Pictures/imagename.jpg), and I believe my error lies with the Uri path, but I am unsure of the correct syntax? If it is not this, any other ideas?
This is what worked for me:
const string filePathOfTheImage = "/Shared/ShellContent/shot2.jpg"; //This is where my image is in isostore
var uri = new Uri("ms-appdata:///local" + filePathOfTheImage, UriKind.Absolute);
Also, I use WP Power Tools for tracking my app storage space. Hope this helps.
Try this at nokia developer. Hope you'll get the way to the solution.

multiple file download using SkyDrive API

I have the following code where I'm trying to download 3 different files from the users SkyDrive Account.
I'm using the SkyDrive API for Windows Phone development.
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompletedVI);
client.DownloadAsync(fileIdVehicleItems);
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompletedHI);
client.DownloadAsync(fileIdHistoryItems);
client.DownloadCompleted += new EventHandler<LiveDownloadCompletedEventArgs>(OnDownloadCompletedRI);
client.DownloadAsync(fileIdRepairItems);
When I run this, the only method that gets called is the OnDownloadCompletedVI. All the files that are being downloaded are running through this method which is causing an error.
What am I doing incorrectly?
Update
I have the following method, but I have 2 other similar methods that do the exact same thing except it loads different objects (based off of the downloaded files).
The error I'm currently receiving:
An exception of type 'System.ArgumentException' occurred in
mscorlib.ni.dll but was not handled in user code
void OnDownloadCompletedVI(object sender, LiveDownloadCompletedEventArgs e)
{
if (e.Result != null)
{
using (var stream_vi = e.Result)
{
StreamReader SRVI = new StreamReader(stream_vi);
string contentVI = "";
contentVI = SRVI.ReadToEnd();
StringReader rdr_vi = new StringReader(contentVI);
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<vehicle>));
ObservableCollection<vehicle> importedVehicles = new ObservableCollection<vehicle>();
importedVehicles = (ObservableCollection<vehicle>)serializer.Deserialize(rdr_vi);
StorageHelper.Save<ObservableCollection<vehicle>>(App.vehicleData, importedVehicles);
}
//e.Result.Close();
}
else
{
infoTextBlock.Text = "Error downloading file: " + e.Error.ToString();
}
}
Actually all three methods should be called. Of course, if the first method is called and throws an exception the other two won't trigger.
What you can do is either create a new client for each call, or download them in order, so when the OnDownloadCompletedVI method is complete, remove the event handler for OnDownloadCompletedVI and add the one for OnDownloadCompletedHI and then trigger the client.DownloadAsync(fileIdHistoryItems); at the end of the method.

Categories

Resources