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)
{ }
}
}
} '''
Related
I am implementing a run() function that can not be async. The goal is to send a get request to a server, and then download some files based on the result of the get request and return the number of files downloaded. I have written an async function to do that but I want to essentially "await" it before the rest of my main function continues. I am unable to achieve this behavior currently as the function just hangs. Not sure why its hanging :(
I think I just need some insights into Task and why this isn't working as expected. I am familiar with promises in JS so I thought this wouldn't be that difficult.
Thank you!
public int run(){
FilesManager test = new FilesManager();
string path = Path.Combine("C:\Users\username\Documents", "Temp");
Task<int> T_count = test.Downloadfiles(path); //TODO: trying to "await" this before the messageBoxes
Task.WaitAll(T_count);
int count = T_count.Result;
MessageBox.Show("File Downloaded");
MessageBox.Show(count.ToString());
}
public async Task<int> Downloadfiles(string path)
{
String[] response = await getClient.GetFromJsonAsync<string[]>("http://localhost:3000");
int counter = 0;
try
{
foreach (string url in response)
{
Uri uri = new Uri(url);
var response2 = await getClient.GetAsync(uri);
using (var fs = new FileStream(
path + counter.ToString(),
FileMode.Create))
{
await response2.Content.CopyToAsync(fs);
}
counter++;
}
return counter;
}catch(Exception e)
{
while (e != null)
{
MessageBox.Show(e.Message);
e = e.InnerException;
}
return 0;
}
}
EDIT:
Still not able to get the task.WaitAll(T_count) to work. With some more debugging, the execution jumps from the response2 = await getClient.GetAsync... straight into the waitAll, never hitting the copyToAsync or counter++.
Sync-over-async is a fundamentally difficult problem, because you need to guarantee that continuations never try to run on the thread you are blocking on, otherwise you will get a deadlock as you have seen. Ideally you would never block on async code, but sometimes that is not possible.
Task.Run(...)..GetAwaiter().GetResult() is normally fine to use for this purpose, although there are still some circumstances when it can deadlock.
Do not call the UI from inside the async function, therefore you must move the catch with MessageBox.Show to the outer function.
You can also make this more efficient, by using HttpCompletionOption.ResponseHeadersRead, and you are missing a using on the response2 object.
public int run()
{
FilesManager test = new FilesManager();
string path = Path.Combine("C:\Users\username\Documents", "Temp");
try
{
int count = Task.Run(() => test.Downloadfiles(path)).GetAwaiter().GetResult();
MessageBox.Show("File Downloaded");
MessageBox.Show(count.ToString());
return count;
}
catch(Exception e)
{
while (e != null)
{
MessageBox.Show(e.Message);
e = e.InnerException;
}
return 0;
}
}
public async Task<int> Downloadfiles(string path)
{
String[] response = await getClient.GetFromJsonAsync<string[]>("http://localhost:3000");
int counter = 0;
try
{
foreach (string url in response)
{
using (var response2 = await getClient.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
using (var fs = new FileStream(
path + counter.ToString(),
FileMode.Create))
{
await response2.Content.CopyToAsync(fs);
}
counter++;
}
return counter;
}
}
Another option is to remove and afterwards restore the SynchronizationContext, as shown in this answer.
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;
}
}
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);
}
}
I have a code which should take all pictures from folder, put them into object named "PhotoInspection", add some informations and put this object into list. See the code below
private async void btnSend_Click(object sender, RoutedEventArgs e)
{
bool isOn = tsOnOff.IsOn;
TextBlock tbChosen = new TextBlock();
tbChosen = lbInspections.SelectedItem as TextBlock;
string chosen = tbChosen.Text;
AllInspectionPhotos aip = new AllInspectionPhotos();
var folders = await ApplicationData.Current.LocalFolder.GetFoldersAsync();
if (isOn)
{
foreach (var item in folders)
{
if (item.Name == "ONLINE")
{
var inspectionFolders = await item.GetFoldersAsync();
foreach (var inspectionFolder in inspectionFolders)
{
if (inspectionFolder.Name == chosen)
{
aip.InspectionEan = chosen;
var photos = await inspectionFolder.GetFilesAsync();
foreach (var photo in photos)
{
using (Stream stream = await photo.OpenStreamForReadAsync())
{
PhotoInspection phtInsp = new PhotoInspection();
var bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
phtInsp.Photo = bytes;
phtInsp.InspectionEan = chosen;
phtInsp.PhotoName = photo.Name;
aip.Photos.Add(phtInsp);
}
}
}
}
}
}
}
else
{
foreach (var item in folders)
{
if (item.Name == "OFFLINE")
{
var inspectionFolders = await item.GetFoldersAsync();
foreach (var inspectionFolder in inspectionFolders)
{
if (inspectionFolder.Name == chosen)
{
aip.InspectionEan = chosen;
var photoset = await inspectionFolder.GetFilesAsync();
foreach (var photo in photoset)
{
PhotoInspection phtInsp = new PhotoInspection();
using (Stream stream = await photo.OpenStreamForReadAsync())
{
var bytes = new byte[(int)stream.Length];
stream.Read(bytes, 0, (int)stream.Length);
phtInsp.Photo = bytes;
phtInsp.InspectionEan = chosen;
phtInsp.PhotoName = photo.Name;
}
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//await App._client.SendPhotoAsync(phtInsp);
aip.Photos.Add(phtInsp); //fires error here
});
}
}
}
}
}
}
await App._client.SendAllPhotosAsync(aip);
}
However when I try to add object into list, I get "attempted to read write protected memory" error. PhotoInspection object is filled with relevant data and everything looks good before adding to list. Thanks for any help
The problem is due to the Dispatcher.RunAsync call, which returns control to the calling routine as soon as the aip.Photos.Add() lambda is queued, and not when it has completed. This results in the aip object being disposed before the lambda has actually run.
The MSDN page for CoreDispatcher.RunAsync says:
[...] it schedules the work on the UI thread and returns control to the caller immediately.
and:
To spin off a worker thread from the UI thread, do not use this method (CoreDispatcher::RunAsync). Instead, use one of the Windows::System::Threading::ThreadPool::RunAsync method overloads.
It also provides the following code example, and the comment is from Microsoft!
//DO NOT USE THIS CODE.
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await signInDialog.ShowAsync();
});
// Execution continues here before the call to ShowAsync completes.
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++;
}