I've read about a thousand similar posts, and have followed the general advice but am still running into the issue. Here's my scenario:
I'm working on a Windows Phone 8 app that, when the user saves, serializes all of their data into XML then uses CreateFile to store it. The problem that I'm facing is that if a user hits save several times consecutively, IsolatedStorageException:Operation Not Permitted is thrown (I'm guessing that the serialization takes long enough that the file is still being used when I attempt to access it a second time). When save is tapped the second time, is there a way for me to abort the previous action, free up the isolated storage file, and initiate the new save? Or is there a better solution?
Here's the code for my Save method (the exception occurs on the isoStore.CreateFile(filename) line):
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = isoStore.CreateFile(filename))
{
XmlSerializer xml = new XmlSerializer(GetType());
xml.Serialize(stream, this);
}
}
Any help would be amazing, as I've been stuck here for weeks.
Thanks,
Ben:
You could go with something like this.
private async Task Save(string fileName)
{
Button.IsEnabled = false;
await Task.Run(() =>
{
using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = isoStore.CreateFile(filename))
{
XmlSerializer xml = new XmlSerializer(GetType());
xml.Serialize(stream, this);
}
}
});
Button.IsEnabled = true;
}
Why not disable the 'save' button when clicked then enable it again once the serialization completes?
Related
I am currently working on a project using the Windows Runtime and I have run into a roadblock, this was something that was always very easy to do and I feel very frustrated for not getting this right.
I have been sitting for hours and I just cannot seem to get it right. I get the "Access is denied error", also in some variations of my code when I did click on the button, nothing happened. I feel like the answer is staring me right in the face. Here is the code:
private async void btnDtlsSaveChanges(object sender, TappedRoutedEventArgs e)
{
StorageFile del = await Package.Current.InstalledLocation
.GetFileAsync("UserDetails.txt");
await del.DeleteAsync();
StorageFile file = await Package.Current.InstalledLocation.CreateFileAsync
("UserDetails.txt", CreationCollisionOption.OpenIfExists);
using (StreamWriter writer =
new StreamWriter(await file.OpenStreamForWriteAsync()))
{
await writer.WriteLineAsync("Hello World");
}
}
I also tried using ReplaceExisting instead of OpenIfExists:
StorageFile file = await Package.Current.InstalledLocation.CreateFileAsync
("UserDetails.txt", CreationCollisionOption.ReplaceExisting);
using (StreamWriter writer = new StreamWriter(await file.OpenStreamForWriteAsync()))
{
await writer.WriteLineAsync("Hello World");
}
I have tried in several ways, all leading down the same track, and I have looked at every related question on stack overflow, nothing is getting me there.
Any help would be greatly appreciated, thanks in advance.
EDIT: (Solved) Me in my stupidity and the learning of a new technology did not actually realise that there is a difference between the LocalStorage and the actual installed location, thanks to Rob Caplan for guiding me in the right direction.
You get access denied because apps don't have write access to Package.Current.InstalledLocation.
For write access you need to use your application data folders such as Windows.Storage.ApplicationData.Current.LocalFolder
If you want to ship your app with an initial data file and then update the app with user data at runtime you can copy it from InstalledLocation to LocalFolder on first run.
See App data (Windows Runtime apps) and Accessing app data with the Windows Runtime (Windows Runtime apps)
I don't know why you are using await, but the following code should be able to clear the file content for you
using (StreamWriter sw = new StreamWriter("UserDetails.txt", false))
{
sw.WriteLine("Hello world");
}
I used a button for it to test
After Clicking the button the file will be empty.
You need to use using FileMode.Create, It will create a new file or overwrite it.
private void button1_Click(object sender, EventArgs e)
{
using (FileStream NewFileStream = new FileStream(#"C:\Users\Crea\Documents\TCP.txt", FileMode.Create))
{ }
//using (StreamWriter sw = new StreamWriter(#"C:\Users\Crea\Documents\TCP.txt", true))
// {
// sw.WriteLine("Hello");
// }
}
I am using following code to create a file in isolated storage
mystorage = IsolatedStorageFile.GetUserStoreForApplication();
if (mystorage.FileExists(scanName))
{
mystorage.DeleteFile(scanName);
}
WriteableBitmap wb = new WriteableBitmap(canImage, null);
using (MemoryStream stream = new MemoryStream())
{
wb.SaveJpeg(stream, (int)canImage.Width, (int)canImage.Height, 0, 100);
using (IsolatedStorageFileStream local = new IsolatedStorageFileStream(scanName, FileMode.Create, mystorage))
{
local.Write(stream.GetBuffer(), 0, stream.GetBuffer().Length);
}
}
if (MessageBox.Show("Scan updated successfully") == MessageBoxResult.OK)
{
App.isTransformRequest = false;
NavigationService.Navigate(new Uri("/View/EditDocument.xaml?paramList=" + App.currentName, UriKind.RelativeOrAbsolute));
}
this code works fine. But I want to detect weather the file is completely created or not, and after that only I want to display the success message. The way I am currently working somtimes displays the success message before the complete creation of the file, I want the message to be displayed only after the file is created completely, ie the stream is written completely.
Using the BeginWrite Method of the IsolatedStorageFileStream (msdn) you can regsiter a ActionCallback to continue with the redirection options in your UI.
Please note, if your callback shows a MessageBox in the UI-Thread, you have to sync the ThreadContext using the Dispatcher (msdn)
So I've been working on a simple game and I wanted to implement a highscore system. Once the player loads up the main page for the first time a new text file is created ("hsc.txt") and some fake values are inserted which are later on split up by the program, however, currently my code throws a System.IO.IsolatedStorage.IsolatedStorageException and I can't seem to find the problem. I've looked up the error that I got from the message box which was "- operation not permitted" but all the solutions that were posted don't seem to work. I have tried closing the streams but it doesn't seem to work.
Any advice would be highly appreciated.
private void hasHighscores()
{
String fileName = "hsc.txt";
using (var isoStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!isoStorage.FileExists(fileName))
{
isoStorage.CreateFile(fileName);
using (var isoStream = new IsolatedStorageFileStream(fileName, FileMode.Append, FileAccess.Write, isoStorage))
{
using (var fileStream = new StreamWriter(isoStream))
{
fileStream.WriteLine("n1:666,n2:777,n3:888,h1:666,h2:777,h3:888");
fileStream.Close();
}
isoStream.Close();
}
}
}
}
So far I have: a) changed the FileMode b) changed the FileAccess and a few other "quickfixes" that I don't even remember.
The CreateFile method returns a stream to the created file, and keeps it open. Therefore, when you try to open a stream to that same file in the next line, it throws an exception because the file is already locked.
You can rewrite your code as follows:
private void hasHighscores()
{
String fileName = "hsc.txt";
using (var isoStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
if (!isoStorage.FileExists(fileName))
{
using (var isoStream = isoStorage.CreateFile(fileName))
{
using (var fileStream = new StreamWriter(isoStream))
{
fileStream.WriteLine("n1:666,n2:777,n3:888,h1:666,h2:777,h3:888");
}
}
}
}
}
I've also removed the stream.Close() instructions. The close method is automatically called when you enclose the stream in a using statement.
I have a strange problem in my Windows Phone 7 application. I need to read/write some xml file in my app and I'm using IsolatedStorage to collect data. My app sends/gets data from SkyDrive this is why I use it.
Ok, here is function which generate exception:
private void CreateFileIntoIsolatedStorage(List<Record> list)
{
isf = IsolatedStorageFile.GetUserStoreForApplication();
if(list.Count == 0)
list = new List<Record>() { new Record { Date = DateTime.Today, Value = 0 }};
if (isf.FileExists(fileName))
{
isf.DeleteFile(fileName);
}
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
using (IsolatedStorageFile myIsolatedStorage = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream stream = myIsolatedStorage.OpenFile(fileName, FileMode.Create))
{
XmlSerializer serializer = new XmlSerializer(typeof(List<Record>));
using (XmlWriter xmlWriter = XmlWriter.Create(stream, xmlWriterSettings))
{
serializer.Serialize(xmlWriter, list);
}
}
}
}
Problem:
My problem starts when I this function runs for the second time. Then isf.DeleteFile(fileName); throws IsolatedStorageException. And creating stream crashed application.
It's strange cause it happens every time I run it on my device, and rarely when I use the debugger.
So my question is how can I solve it or are there better ways to do this?
Any help would be appreciated.
Possibly it's because at the beginning of your method you have:
isf = IsolatedStorageFile.GetUserStoreForApplication();
And you never dispose of that. Then, later, you get it again in the using. But that one's disposed. And then the next time you call CreateFileIntoIsolatedStorage, you get it again, again without disposing.
Perhaps this is what you want:
using (var isf = IsolatedStorageFile.GetUserStoreForApplication())
{
if(list.Count == 0)
list = new List<Record>() { new Record { Date = DateTime.Today, Value = 0 }};
if (isf.FileExists(fileName))
{
isf.DeleteFile(fileName);
}
}
Although that class-scoped isf variable is troublesome. If you want to keep the store active, then just call it once and leave it open. Otherwise, ditch the class-scoped variable.
Or, it might be due to this, from the documentation for IsolatedStorageFile.DeleteFile?
File deletions are subject to intermittent failures because files can be in use simultaneously by operating system features such as virus scanners and file indexers. This is especially true for recently created files. Macintosh users should be aware of this issue because of its frequent indexing.
For these reasons, it is important to add code to the code block that handles the IsolatedStorageException to retry deleting the file or log a failure.
I would suggest something like:
int retryCount = 0;
while (retryCount < MaxRetryCount && isf.FileExists(fileName))
{
try
{
isf.DeleteFile(fileName);
}
catch (IsolatedStorageException)
{
++retryCount;
// maybe notify user and delay briefly
// or forget about the retry and log an error. Let user try it again.
}
}
I have simple page, that loads XML from filesystem, fills textboxes, these can be updated and saved. For serializing and deserializing I am using these methods:
private static readonly object FormDataLock = new object();
public static FormData getFormData(string filename)
{
FormData fd;
lock (FormDataLock)
{
XmlSerializer x = new XmlSerializer(typeof(FormData));
using (Stream s = new FileStream(filename, FileMode.Open, FileAccess.Read))
{
return (FormData)x.Deserialize(s);
}
}
}
public void saveFormData(string filename)
{
lock (FormDataLock)
{
XmlSerializer x = new XmlSerializer(typeof(FormData));
using (Stream s = new FileStream(filename, FileMode.Create, FileAccess.Write))
{
x.Serialize(s, this);
}
}
}
But the problem is, that I am gettig sometimes (as I have notticed when I click the "save" button too fast after PageLoad) the IOException:
IOException: The process cannot access the file ".." because it is being used by another process.
I was trying to lock the block with mutex, but it is still not working properly. The page form is quite simple, but I am using UpdatePanel on it (is it important?).
When the page is loaded and first save request was done OK, I can click the button as fast as I can and everything is OK (no exception).
XmlSerialization creates new dll's on the fly which are specific to the class you're trying to serialise in the temp directory. These are created to increase performance.
See http://msdn.microsoft.com/en-us/library/swxzdhc0.aspx
Instead of calling the GC.Collect etc... try creating the serializer as a static field on your class. This should improve performance and might solve your problem as it's only ever going to be created once.
This code will create a single xmlserializer in a thread safe way. Do NOT add a [ThreadStatic] attribute to this as this will ensure the code gets executed once per thread and make it thread unsafe again!
private static readonly XmlSerializer xmlSerializer =
new XmlSerializer(typeof(FormData));
I had similar problem and I hope this will help you too.
The problem was that garbage collector didn't clean up before your second click, so you should try to call it manually. Try to call GC before living using
GC.Collect();
GC.WaitForPendingFinalizers();