UWP File Management - c#

Why does saving a file fail so often on UWP?
I open and read the contents of a specified file when the application is initially launched (with the help of a BackgroundWorker). Then when the application is suspending I try to save new content to the same file, however this results almost always in the following exception:
Unable to remove the file to be replaced. (Exception from HRESULT: 0x80070497)
My loading method:
private async static void SymbolLoader_DoWork(object sender, DoWorkEventArgs e)
{
IStorageItem file = null;
if (!UseDefault)
{
var folder = await StorageFolder.GetFolderFromPathAsync(SavePath);
file = await folder.TryGetItemAsync(FileName);
}
if (file == null)
{
file = await Package.Current.InstalledLocation.GetFileAsync(#"Assets\Default.txt");
}
var text = await FileIO.ReadTextAsync((IStorageFile)file);
Data = DoSomething(text);
}
My OnSuspending method:
private async void OnSuspending(object sender, SuspendingEventArgs e)
{
var tries = 0;
var maxTries = 10;
var deferral = e.SuspendingOperation.GetDeferral();
while (tries < maxTries)
{
var completed = await Saver.Save();
if (completed)
break;
else
{
tries++;
await Task.Delay(TimeSpan.FromSeconds(0.1));
}
}
deferral.Complete();
}
Save method:
public static async Task<bool> Save()
{
//some formatting etc.
var folder = await StorageFolder.GetFolderFromPathAsync(SavePath);
var file = await folder.CreateFileAsync(FileName, CreationCollisionOption.ReplaceExisting);
try
{
await FileIO.WriteLinesAsync(file, lines);
return true;
}
catch(Exception e)
{
return false;
}
}
It is critical that the data is saved, which is why I "solved" the problem by trying to save the file multiple times with some delay. However my solution, presented in the OnSuspending method, does not seem like the correct way to do this.
What is the correct approach? Thanks in advance!

Related

What are your suggestions for a different logging method in UWP?

During the UWP conversion of an application that was previously designed as a winform, errors occurred in my logging method and I had to change the System.IO reference to Storage. As a result of this change, when the synchronous method started to work asynchronously, I got errors about the request to use the file at the same time. Then I came up with a simple solution by creating the class I mentioned below but this quick amateurish approach do you think is correct?
public class Logging_UWP
{
private List<string> CompletedLog = new List<string>();
private string machineCode = string.Empty;
public Logging_UWP(string MachineCode)
{
machineCode = MachineCode;
Task.Factory.StartNew(()=> WriteLog());
}
public void WriteLogSync(string NewLog)
{
CompletedLog.Add(NewLog);
}
private async void WriteLog()
{
while (true)
{
try
{
foreach (string NewLog in CompletedLog)
{
StorageFolder storageFolder = ApplicationData.Current.LocalFolder;
StorageFile sampleFile = await storageFolder.CreateFileAsync(machineCode + ".log", CreationCollisionOption.OpenIfExists);
var stream = await sampleFile.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
using (var outputStream = stream.GetOutputStreamAt(stream.Size + 1))
{
using (var dataWriter = new Windows.Storage.Streams.DataWriter(outputStream))
{
dataWriter.WriteString(String.Format("\n[{0}-{1}] >>> {2}", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString(), NewLog));
await dataWriter.StoreAsync();
await outputStream.FlushAsync();
}
}
}
if (CompletedLog.Count > 0)
CompletedLog.Clear();
await Task.Delay(500);
}
catch (Exception)
{ }
}
}
} '''

c# UWP ReadTextAsync fails at 2 time

So i'm trying to import 2 configs from the localfolder with Windows.Storage. But at the second time it fails with no exception.
This is my Code:
public async Task<string> ImportLines(string filename)
{
try
{
Windows.Storage.StorageFile importFile = await StorageFolder.GetFileAsync(filename);
string savedString = await Windows.Storage.FileIO.ReadTextAsync(importFile);
return savedString;
}
catch (Exception)
{
//log
}
}
I call this methode with:
public async void LoadConfig()
{
if (File.Exists(_textDataHandler.StorageFolder.Path + #"\" + PluginsFilename))
{
string tmp = await _textDataHandler.ImportLines(PluginsFilename);
Plugins = JsonConvert.DeserializeObject<PluginConfiguration>(tmp);
}
else
{
CreateDefaultPluginsConfiguration();
//log
_textDataHandler.CreateFile(_pluginsFilename);
string export = JsonConvert.SerializeObject(Plugins, Formatting.Indented);
_textDataHandler.ExportText(_pluginsFilename, export);
//log
}
if (File.Exists(_textDataHandler.StorageFolder.Path + #"\" + _settingsFilename))
{
string tmp = await _textDataHandler.ImportLines(Settingsfilename);
Config = JsonConvert.DeserializeObject<Configuration>(tmp);
_textDataHandler.CreateFile(Config.DatabaseFilename);
}
else
{
CreateDefaultSettingsConfiguration();
//log
_textDataHandler.CreateFile(_settingsFilename);
string export = JsonConvert.SerializeObject(Config, Formatting.Indented);
_textDataHandler.ExportText(_settingsFilename, export);
//Log
}
}
If one config does not exist its fine but if both exist it fails at the second time
If you have created async method. please avoid synchronous invoke with GetResult. You could add await key word in front of calling line.
private async void Button_Click(object sender, RoutedEventArgs e)
{
string tmp = await ImportLines(Filename);
}
Update
Please try use dataReader to read file content.
public async Task<string> ImportLines(string filename)
{
try
{
StorageFile importFile = await ApplicationData.Current.LocalFolder.GetFileAsync(filename);
var buffer = await FileIO.ReadBufferAsync(importFile);
using (var dataReader = Windows.Storage.Streams.DataReader.FromBuffer(buffer))
{
return dataReader.ReadString(buffer.Length);
}
}
catch (Exception ex)
{
return ex.Message;
}
}

winforms async await task UI lockup

I am trying to read the contents of a directory that has over 100k+ files. Each file will be parsed eventually. Since this is a long time consuming task I want to provide feedback to the end user that the program has not "frozen", This is an MVP pattern project setup.
I am using async Task and IProgress to provide feedback to the UI, but either methods below lock up the UI and I can't figure out why..
private async void MainFormViewOnStartProcessingFiles(object sender, EventArgs eventArgs)
{
// The Progress<T> constructor captures our UI context,
// so the lambda will be run on the UI thread.
var asyncProgress = new Progress<int>(percent =>
{
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarMessage($"Async found {percent} files..."));
});
// GetDirectoryFiles is run in the thread pool
await Task.Run(() => GetDirectoryFilesAsync(asyncProgress));
var syncProgress = new Progress<int>(percent =>
{
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarMessage($"Sync found {percent} files..."));
});
GetDirectoryFiles(syncProgress);
}
This updates the UI
private void DisplayWaitingBarMessage(DisplayMainFormWaitBarMessage obj)
{
_mainFormView.DisplayWaitBarMessage(obj.DisplayText);
}
This is my Async and Non Async code to read the files into a queue
private async Task GetDirectoryFilesAsync(IProgress<int> taskProgress)
{
await Task.Run(() =>
{
try
{
var directoryPath = Directory.GetCurrentDirectory() + #"\..\..\..\FUG_2017\";
var _listOfDirectoryFiles = Directory.GetFiles(directoryPath).Take(100);
var fileCount = 0;
foreach (var filePath in _listOfDirectoryFiles)
{
_filesToProcessQueue.Enqueue(filePath);
fileCount++;
taskProgress?.Report(fileCount);
}
}
catch (Exception exc)
{
FlushAndExit(exc);
}
});
}
private void GetDirectoryFiles(IProgress<int> taskProgress)
{
try
{
var directoryPath = Directory.GetCurrentDirectory() + #"\..\..\..\FUG_2017\";
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarWithMessage(ElementVisibility.Visible, $"Inspecting path {directoryPath}"));
var _listOfDirectoryFiles = Directory.GetFiles(directoryPath).Take(1000);
var fileCount = 0;
foreach (var filePath in _listOfDirectoryFiles)
{
_filesToProcessQueue.Enqueue(filePath);
fileCount++;
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarWithMessage(ElementVisibility.Visible, $"Loaded file {fileCount} of {_listOfDirectoryFiles.Count()} to Queue..."));
taskProgress?.Report(fileCount);
}
EventAggregator.Instance.Publish(new DisplayMainFormWaitBarWithMessage(ElementVisibility.Visible, $"Loaded {_listOfDirectoryFiles.Count()} files to Queue..."));
}
catch (Exception exc)
{
FlushAndExit(exc);
}
}

Xamarin Forms Play a sound Asynchronously

I can successfully play sounds using Xamarin forms (Android and iOS) however I also need to achieve the following:
I need to await so that if multiple sounds are 'played', one will complete before the next.
I need to return a boolean to indicate whether operation was a success.
Here is my current simplified code (for the iOS platform):
public Task<bool> PlayAudioTask(string fileName)
{
var tcs = new TaskCompletionSource<bool>();
string filePath = NSBundle.MainBundle.PathForResource(
Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
var url = NSUrl.FromString(filePath);
var _player = AVAudioPlayer.FromUrl(url);
_player.FinishedPlaying += (object sender, AVStatusEventArgs e) =>
{
_player = null;
tcs.SetResult(true);
};
_player.Play();
return tcs.Task;
}
To test the method, I have tried calling it like so:
var res1 = await _audioService.PlayAudioTask("file1");
var res2 = await _audioService.PlayAudioTask("file2");
var res3 = await _audioService.PlayAudioTask("file3");
I had hoped to hear the audio for file1, then file2, then file3. However I only hear file 1 and the code doesn't seem to reach the second await.
Thankyou
I think your issue here is that the AVAudioPlayer _player was being cleared out before it was finished. If you were to add debugging to your FinsihedPlaying, you'll notice that you never hit that point.
Try these changes out, I made a private AVAudioPlayer to sit outside of the Task
(I used the following guide as a reference https://developer.xamarin.com/recipes/ios/media/sound/avaudioplayer/)
public async void play()
{
System.Diagnostics.Debug.WriteLine("Play 1");
await PlayAudioTask("wave2.wav");
System.Diagnostics.Debug.WriteLine("Play 2");
await PlayAudioTask("wave2.wav");
System.Diagnostics.Debug.WriteLine("Play 3");
await PlayAudioTask("wave2.wav");
}
private AVAudioPlayer player; // Leave the player outside the Task
public Task<bool> PlayAudioTask(string fileName)
{
var tcs = new TaskCompletionSource<bool>();
// Any existing sound playing?
if (player != null)
{
//Stop and dispose of any sound
player.Stop();
player.Dispose();
}
string filePath = NSBundle.MainBundle.PathForResource(
Path.GetFileNameWithoutExtension(fileName), Path.GetExtension(fileName));
var url = NSUrl.FromString(filePath);
player = AVAudioPlayer.FromUrl(url);
player.FinishedPlaying += (object sender, AVStatusEventArgs e) =>
{
System.Diagnostics.Debug.WriteLine("DONE PLAYING");
player = null;
tcs.SetResult(true);
};
player.NumberOfLoops = 0;
System.Diagnostics.Debug.WriteLine("Start Playing");
player.Play();
return tcs.Task;
}

The process cannot access the file because it is being used by another process

I am running a program where a file gets uploaded to a folder in IIS,and then is processed to extract some values from it. I use a WCF service to perform the process, and BackgroundUploader to upload the file to IIS. However, after the upload process is complete, I get the error "The process cannot access the file x because it is being used by another process." Based on similar questions asked here, I gathered that the file concerned needs to be in a using statement. I tried to modify my code to the following, but it didn't work, and I am not sure if it is even right.
namespace App17
{
public sealed partial class MainPage : Page, IDisposable
{
private CancellationTokenSource cts;
public void Dispose()
{
if (cts != null)
{
cts.Dispose();
cts = null;
}
GC.SuppressFinalize(this);
}
public MainPage()
{
this.InitializeComponent();
cts = new CancellationTokenSource();
}
public async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
Uri uri = new Uri(serverAddressField.Text.Trim());
FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add("*");
StorageFile file = await picker.PickSingleFileAsync();
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
GlobalClass.filecontent = file.Name;
GlobalClass.filepath = file.Path;
BackgroundUploader uploader = new BackgroundUploader();
uploader.SetRequestHeader("Filename", file.Name);
UploadOperation upload = uploader.CreateUpload(uri, file);
await HandleUploadAsync(upload, true);
stream.Dispose();
}
}
catch (Exception ex)
{
string message = ex.ToString();
var dialog = new MessageDialog(message);
await dialog.ShowAsync();
Log(message);
}
}
private void CancelAll(object sender, RoutedEventArgs e)
{
Log("Canceling all active uploads");
cts.Cancel();
cts.Dispose();
cts = new CancellationTokenSource();
}
private async Task HandleUploadAsync(UploadOperation upload, bool start)
{
try
{
Progress<UploadOperation> progressCallback = new Progress<UploadOperation>(UploadProgress);
if (start)
{
await upload.StartAsync().AsTask(cts.Token, progressCallback);
}
else
{
// The upload was already running when the application started, re-attach the progress handler.
await upload.AttachAsync().AsTask(cts.Token, progressCallback);
}
ResponseInformation response = upload.GetResponseInformation();
Log(String.Format("Completed: {0}, Status Code: {1}", upload.Guid, response.StatusCode));
cts.Dispose();
}
catch (TaskCanceledException)
{
Log("Upload cancelled.");
}
catch (Exception ex)
{
string message = ex.ToString();
var dialog = new MessageDialog(message);
await dialog.ShowAsync();
Log(message);
}
}
private void Log(string message)
{
outputField.Text += message + "\r\n";
}
private async void LogStatus(string message)
{
var dialog = new MessageDialog(message);
await dialog.ShowAsync();
Log(message);
}
private void UploadProgress(UploadOperation upload)
{
BackgroundUploadProgress currentProgress = upload.Progress;
MarshalLog(String.Format(CultureInfo.CurrentCulture, "Progress: {0}, Status: {1}", upload.Guid,
currentProgress.Status));
double percentSent = 100;
if (currentProgress.TotalBytesToSend > 0)
{
percentSent = currentProgress.BytesSent * 100 / currentProgress.TotalBytesToSend;
}
MarshalLog(String.Format(CultureInfo.CurrentCulture,
" - Sent bytes: {0} of {1} ({2}%), Received bytes: {3} of {4}", currentProgress.BytesSent,
currentProgress.TotalBytesToSend, percentSent, currentProgress.BytesReceived, currentProgress.TotalBytesToReceive));
if (currentProgress.HasRestarted)
{
MarshalLog(" - Upload restarted");
}
if (currentProgress.HasResponseChanged)
{
MarshalLog(" - Response updated; Header count: " + upload.GetResponseInformation().Headers.Count);
}
}
private void MarshalLog(string value)
{
var ignore = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
Log(value);
});
}
}
}
After this is done, the file name is sent to a WCF service which will access and process the uploaded file to extract certain values. It is at this point I receive the error. I would truly appreciate some help.
public async void Extract_Button_Click(object sender, RoutedEventArgs e)
{
ServiceReference1.Service1Client MyService = new ServiceReference1.Service1Client();
string filename = GlobalClass.filecontent;
string filepath = #"C:\Users\R\Documents\Visual Studio 2015\Projects\WCF\WCF\Uploads\"+ filename;
bool x = await MyService.ReadECGAsync(filename, filepath);
}
EDIT: Code before I added the using block
try
{
Uri uri = new Uri(serverAddressField.Text.Trim());
FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add("*");
StorageFile file = await picker.PickSingleFileAsync();
GlobalClass.filecontent = file.Name;
GlobalClass.filepath = file.Path;
BackgroundUploader uploader = new BackgroundUploader();
uploader.SetRequestHeader("Filename", file.Name);
UploadOperation upload = uploader.CreateUpload(uri, file);
await HandleUploadAsync(upload, true);
}
When you work with stream writers you actually create a process, which you can close it from task manager. And after stream.Dispose() put stream.Close().
This should solve your problem.
You should also close the stream that writes the file to disk (look at your implementation of CreateUpload).
i got such error in DotNet Core 2 using this code:
await file.CopyToAsync(new FileStream(fullFileName, FileMode.Create));
counter++;
and this is how I managed to get rid of message (The process cannot access the file x because it is being used by another process):
using (FileStream DestinationStream = new FileStream(fullFileName, FileMode.Create))
{
await file.CopyToAsync(DestinationStream);
counter++;
}

Categories