Windows phone 8 and Surface pro 2 OpenStreamForWriteAsync blocks the app - c#

I'm working on a project for windows phone and surface pro 2 app and I'm looking for reading a text from a .txt file.
I'm new in windows app development and I take over the project of an other guy.
I didn't change his code for the moment but there are some things I don't understand and the main problem is that when I use the app, this one is blocked when I call the method OpenStreamForWriteAsync().
Here is the code:
public async void FillIn()
{
Stream stream1 = null;
StorageFolder storageFolder = KnownFolders.DocumentsLibrary; // Gets Documents library
//The following structure code in comment is from me, is it necessary to
//do all these verification or "StorageFolder storageFolder =
//KnownFolders.DocumentsLibrary;" is enough ?
//try
//{
// folder = await KnownFolders.DocumentsLibrary.GetFolderAsync(AppFolderName);
//}
//catch (Exception)
//{
// // Folder does not exist, but we cannot await in a catch block, so we can not create it here.
//}
//if (folder == null)
//{
// folder = await KnownFolders.DocumentsLibrary.CreateFolderAsync(AppFolderName, CreationCollisionOption.OpenIfExists);
//}
// Test if the file exist
StorageFile report1 = null;
try
{
report1 = await storageFolder.GetFileAsync("report_final.txt"); // Take the .txt file.
}
catch { }
if (report1 == null)
{
// Could not find file
}
// PROBLEM IS HERE
stream1 = await report1.OpenStreamForWriteAsync(); // the app is blocked at this line
using (StreamReader sr1 = new StreamReader(stream1))
{
Texte = sr1.ReadToEnd();
sr1.Dispose(); // the guy before me use this but when we use "using"
//isn't it suppose to call "Dispose" at the end automatically ??
}
}
Thanks for your help and feel free to ask me more information if you don't understand something.

Related

How to load, edit and save xml in uwp app

Title says it all really. I've been stuck on this one for days and would appreciate some help. I've a main page and a settings page when the main page loads first time it tests for settings.xml in local folder and copies it if not found. Then when the user opens settings page it's supposed to load details from local folder allowing the user to edit before saving them back to the local folder from OnNavigatedFrom event.
Code to load from installation folder to local folder
// Has the file been copied already?
bool blFileExist = false;
try
{
await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
// No exception means it exists
blFileExist = true;
btnSettings.Foreground = new SolidColorBrush(Windows.UI.Colors.White);
}
catch (System.IO.FileNotFoundException)
{
// The file obviously doesn't exist
blFileExist = false;
btnSettings.Foreground = new SolidColorBrush(Windows.UI.Colors.Red);
}
catch (Exception)
{
}
if (!blFileExist)
{
try
{
// Cant await inside catch, but this works anyway
StorageFile stopfile = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync("settings.xml");
await stopfile.CopyAsync(ApplicationData.Current.LocalFolder);
}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception)
{
}
}
Code to load and save settings page
private void loadSettings()
{
try
{
doc = XElement.Load("settings.xml");
nAIPlayers = int.Parse(doc.Element("ai_players").Value);
strCardBack = doc.Element("back").Value;
comboBoxAIPlayers.SelectedIndex = nAIPlayers - 1;
}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception ex)
{
}
}
private async void saveSettings()
{
//try
//{
StorageFile file = await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
using (Stream fileStream = await file.OpenStreamForWriteAsync())
{
doc.SetElementValue("ai_players", nAIPlayers);
doc.SetElementValue("back", "Back_0");
doc.Save(fileStream);
}
/*}
catch (System.IO.FileNotFoundException)
{
}
catch (Exception ex)
{
}*/
}
I think the problem is I'm accessing the local file to save it and the installation file to load it. The result is no matter what I save it always reads the values in the original settings.xml
How do I load this from the local folder?
doc = XElement.Load("settings.xml");
Update
On the first iteration the code runs fine and the settings page code opens as it should. It's only after leaving the settings page and running saveSettings() method that it fails and throws an error when reloading the settings page and running loadSettings().
System.Xml.XmlException: Data at the root level is invalid. Line 5, position 12
You are doing it wrong because you are using XElement.Load(string) where string stands for URI, which in this case should be:
The Uri parameter must be a file system relative or absolute path.
and with that you will have a problem in UWP as normally you don't have the permission. It also won't work here with URIs like: "ms-appdata:///settings.xml".
Probably you can read a path to your LocalFolder and use it (may work, though haven't tested it), but much easier is to load the content from stream (or read string from file and then load XML from that string), for example like this:
var file = await ApplicationData.Current.LocalFolder.GetFileAsync("settings.xml");
using(var stream = await file.OpenStreamForReadAsync())
{
var doc = XElement.Load(stream);
// ...
}
Note also that there are other classes like XDocument or XmlDocument where you can load and manage you xml file. Everything depends on your needs.

Json serializer - how to create a stream

public static async Task Store(ObservableCollection<Product> list)
{
Uri path = new Uri("ms-appx:///ListCollection.json");
var store = await StorageFile.GetFileFromApplicationUriAsync(path);
var stream = File.OpenWrite(store.Path);
var serialize = new DataContractJsonSerializer(typeof(ObservableCollection<Product>));
serialize.WriteObject(stream, list);
}
Ok this is the piece of code that I used to serialize a collection , works very well , no problem with it , but what I want and tried and no success. I created a JSON file in my project. I want to store and stream data to that file. I tried some methods but no success , how do I open a stream to a file that is currently in my project?
EDITED : Commented the code that was working and wrote what I intend to do. Thanks for support.
When I get to this line
var stream = File.OpenWrite(store.Path); it says that is inaccesible.
What I intend to do is serialize some data to a file called ListCollection.json that is emtpy , that file is project file. It might be the stream or it might be the file that gives me that error. No idea.
My guess is that your project file is located in the installation directory of your application and as far as I know you can't just write to that directory.
You would have to put a deployment action in your solution that writes the desired project file to the application data directory. There you should be able to write it.
I looked through some of the documentation and came accross this:
MSDN
The app's install directory is a read-only location.
I found a Link which makes use of a little hack or so it seems.
I am not sure if this will work if the application is deployed etc.
but you can try this to write the file.
I am not sure if you need a stream or not but feel free to comment:
private void Button_Click(object sender, RoutedEventArgs e)
{
ObservableCollection<string> list = new ObservableCollection<string>();
list.Add("Hallo");
list.Add("Welt");
Task t = Store(list);
}
public static async Task Store(ObservableCollection<string> list)
{
StorageFile file = await GetStorageFileFromApplicationUriAsync();
if (file == null)
{
file = await GetStorageFileFromFileAsync();
}
if (file != null)
{
await file.DeleteAsync();
await CreateFileInInstallationLocation(list);
}
}
private static async Task<StorageFile> GetStorageFileFromFileAsync()
{
StorageFile file = null;
if (file == null)
{
try
{
StorageFolder folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
file = await folder.GetFileAsync("ListCollection.json");
}
catch
{ }
}
return file;
}
private static async Task<StorageFile> GetStorageFileFromApplicationUriAsync()
{
StorageFile file = null;
try
{
Uri path = new Uri("ms-appx:///ListCollection.json");
file = await StorageFile.GetFileFromApplicationUriAsync(path);
}
catch
{ }
return file;
}
private static async Task CreateFileInInstallationLocation(ObservableCollection<string> list)
{
var pkg = Windows.ApplicationModel.Package.Current;
var installedLocationFolder = pkg.InstalledLocation;
try
{
var file = await installedLocationFolder.CreateFileAsync("ListCollection.json", Windows.Storage.CreationCollisionOption.GenerateUniqueName);
var filePath = file.Path;
DataContractJsonSerializer serialize = new DataContractJsonSerializer(typeof(ObservableCollection<String>));
using (Stream stream = await file.OpenStreamForWriteAsync())
{
serialize.WriteObject(stream, list);
stream.Flush();
}
}
catch (Exception ex)
{
var msg = ex.Message;
}
}
What this basically does is:
Find the file
Delete the file
Create a new file
Write your JSON to the file
I am really not an expert on this matter and it even to me seems pretty hacky but it apparently does the job.
If you can avoid writing to the install directory do it and use the method Frank J proposed

StorageFolder File Not Found error

I am having some issues in my app that can download a list of music files. I'm trying to setup the following folder structure. Music Library > Artist(s) > Release Name. When starting the download, the first song's folder structure is setup properly. Once the second download starts, I always get a File Not found exception when trying to create the second sub folder (release name). Here is my code.
private async Task StartDownload(List<DownloadData> data)
{
foreach (DownloadData song in data)
{
// Set the source of the download
Uri source = new Uri(song.downloadUrl);
// Create folder stucture
StorageFolder artistFolder;
try
{
artistFolder = await KnownFolders.MusicLibrary.CreateFolderAsync(song.artistName, CreationCollisionOption.OpenIfExists);
}
catch
{
throw;
}
StorageFolder releaseFolder;
try
{
releaseFolder = await artistFolder.CreateFolderAsync(song.releaseName, CreationCollisionOption.OpenIfExists);
}
catch
{
throw; // Exception Thrown here
}
// Create file
StorageFile destinationFile;
try
{
destinationFile = await releaseFolder.CreateFileAsync(song.fileName, CreationCollisionOption.GenerateUniqueName);
}
catch
{
throw;
}
BackgroundDownloader downloader = new BackgroundDownloader();
DownloadOperation download = downloader.CreateDownload(source, destinationFile);
List<DownloadOperation> requestOperations = new List<DownloadOperation>();
requestOperations.Add(download);
await HandleDownloadAsync(download, true);
}
}
I have no idea why it works the first time around but fails on the second song.
According to the documentation for CreateFileAsync it will throw FileNotFoundExcption if
The folder name contains invalid characters, or the format of the folder name is incorrect.
So you likely need to replace invalid characters with something else like underscore.
var fixedFolderName = string.Join(
"_",
song.releaseName.Split(Path.GetInvaildFileNameChars()));

Mediaelement in Windows 8 Metro App

I have a scenario specific to my app. I am managing music file playlist in XML for my metro app. And its saving music files actual path like this
D:\MyMedia\1.mp3
I have media element in my XAML page and I am setting its Source like this.
mediaElement.Source = new Uri(#"D:\MyMedia\1.mp3", UriKind.Absolute);
mediaElement.Play();
but its not playing the media and its giving the error like this
MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED : HRESULT - 0x80070005
So someone tell me how I can play some media file in MediaElement of metro app with absoulte path. Or how I can get stream of my local file to play this media in my mediaElement of Metro app.
To open files on the local system, you can use the FileOpenPicker to get the file and SetSource to set the media source.
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.FileTypeFilter.Add(".mp3");
var file = await openPicker.PickSingleFileAsync();
var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
if (null != file)
{
mediaElement.SetSource(stream, file.ContentType);
mediaElement.Play();
}
There is only a limited number of locations on a user's PC that you can access. "D:\Mymedia" is not one of those. You will find all the necessary information in the Microsoft help. Check out these two articles:
Quickstart: Accessing files programmatically
File access and permissions in Windows Store apps
This can be done without a file picker. You just have to add Music Library capabilities to your app manifest, make sure the music is in My Music or on an SD Card, and use KnownFolders.MusicLibrary.
In short and in keeping with the music theme of the question:
"You might think file picker...
...but all I need is StorageFile"
//
// Opens and plays song titled "[Cars]You_Might_Think.mp3"
// in the 80s folder.
//
// IMPORTANT: the example song must be in the user's Music
// folder or SD card to be found. Also, the app manifest
// must have 'Music Library' capabilities enabled.
//
//
//
async void OpenAndPlayAwesomeSongFromThe80s()
{
Windows.Storage.StorageFolder folder = KnownFolders.MusicLibrary;
StorageFile song = await folder.GetFileAsync("80s\\[Cars]You_Might_Think.mp3");
if (song != null)
{
IRandomAccessStream stream = await song.OpenAsync(Windows.Storage.FileAccessMode.Read);
if (stream != null)
{
mediaElement = new MediaElement();
mediaElement.SetSource(stream, song.ContentType);
mediaElement.Play();
}
}
}
I know it's an old problem, I found a really simple solution..
Windows.Storage.StorageFile file = null;
mediaElement player = new MediaElement();
async Task PlayAsync()
{
if (file == null)
{
file = await OpenFileAsync();
try { player.MediaOpened -= Player_MediaOpenedAsync; } catch { }
try { player.MediaEnded -= Player_MediaEnded; } catch { }
try { player.MediaFailed -= Player_MediaFailed; } catch { }
player.MediaOpened += Player_MediaOpenedAsync;
player.MediaEnded += Player_MediaEnded;
player.MediaFailed += Player_MediaFailed;
player.SetPlaybackSource(MediaSource.CreateFromStorageFile(file)); //Works with media playback..
//player.Source = new Uri(mediasource, UriKind.RelativeOrAbsolute); //Doesn't work with media playback for some reason..
Playing = true;
Paused = false;
}
else
{
if (Paused)
{
player.Play();
Paused = false;
}
else
{
player.Pause();
Paused = true;
}
}
}
async Task<StorageFile> OpenFileAsync()
{
try
{
var ofd = new FileOpenPicker();
ofd.FileTypeFilter.Add("*");
return await ofd.PickSingleFileAsync();
}
catch { }
return null;
}

Open a file in Windows 8 Metro App C#

I have an array with file paths (like "C:\...") and I would like to open them with the default app, from my app. Let's say it's a list and when I click one of them, it opens.
This is the way to launch a file async:
await Windows.System.Launcher.LaunchFileAsync(fileToLaunch);
It requires a Windows.Storage.StorageFile type of file, which has a Path read-only property, so I cannot set the Path. How can I open them once they're tapped/clicked?
Copied from my link in the comments:
// Path to the file in the app package to launch
string exeFile = #"C:\Program Files (x86)\Steam\steamapps\common\Skyrim\TESV.exe";
var file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(exeFile);
if (file != null)
{
// Set the option to show the picker
var options = new Windows.System.LauncherOptions();
options.DisplayApplicationPicker = true;
// Launch the retrieved file
bool success = await Windows.System.Launcher.LaunchFileAsync(file, options);
if (success)
{
// File launched
}
else
{
// File launch failed
}
}
You can of course omit the var options = **-Part so the ApplicationPicker doesn't get opened
or you can use this:
StorageFile fileToLaunch = StorageFile.GetFileFromPathAsync(myFilePath);
await Windows.System.Launcher.LaunchFileAsync(fileToLaunch);
you should use this method
http://msdn.microsoft.com/en-US/library/windows/apps/windows.storage.storagefile.getfilefrompathasync
on the Type StorageFile
This method is used to get file if you have a path already
the answer is within this sample: http://code.msdn.microsoft.com/windowsapps/Association-Launching-535d2cec
short answer is :
// First, get the image file from the package's image directory.
string fileToLaunch = #"images\Icon.Targetsize-256.png";
var file = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFileAsync(fileToLaunch);
// Next, launch the file.
bool success = await Windows.System.Launcher.LaunchFileAsync(file);
Best solution is this:
string filePath = #"file:///C:\Somewhere\something.pdf";
if (filePath != null)
{
bool success = await Windows.System.Launcher.LaunchUriAsync(new Uri(filePath));
if (success)
{
// File launched
}
else
{
// File launch failed
}
}
Tha app launches it with the system default application, in this case with the Adobe Reader.

Categories

Resources