i have been working on a windows store project using c#
i have a method called
void TranscodeProgress(IAsyncActionWithProgress<double> asyncInfo, double percent)
{
pg1.Value=percent;
}
when i try to add a progress bar to this it gives me an error
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
please help me to correct this error
thanks
this is my entire code
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
Windows.Storage.StorageFile source;
Windows.Storage.StorageFile destination;
var openPicker = new Windows.Storage.Pickers.FileOpenPicker();
openPicker.SuggestedStartLocation = Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
openPicker.FileTypeFilter.Add(".mp4");
openPicker.FileTypeFilter.Add(".wmv");
source = await openPicker.PickSingleFileAsync();
var savePicker = new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.VideosLibrary;
savePicker.DefaultFileExtension = ".wmv";
savePicker.SuggestedFileName = "New Video";
savePicker.FileTypeChoices.Add("MPEG4", new string[] { ".wmv" });
destination = await savePicker.PickSaveFileAsync();
// Method to perform the transcoding.
TranscodeFile(source, destination);
}
async void TranscodeFile(StorageFile srcFile, StorageFile destFile)
{
MediaEncodingProfile profile =
MediaEncodingProfile.CreateWmv(VideoEncodingQuality.HD1080p);
MediaTranscoder transcoder = new MediaTranscoder();
PrepareTranscodeResult prepareOp = await
transcoder.PrepareFileTranscodeAsync(srcFile, destFile, profile);
if (prepareOp.CanTranscode)
{
var transcodeOp = prepareOp.TranscodeAsync();
transcodeOp.Progress +=
new AsyncActionProgressHandler<double>(TranscodeProgress);
// p1.Value = double.Parse(transcodeOp.Progress.ToString());
// txtProgress.Text = transcodeOp.Progress.ToString();
transcodeOp.Completed +=
new AsyncActionWithProgressCompletedHandler<double>(TranscodeComplete);
}
else
{
switch (prepareOp.FailureReason)
{
case TranscodeFailureReason.CodecNotFound:
MessageDialog md=new MessageDialog("Codec not found.");
await md.ShowAsync();
break;
case TranscodeFailureReason.InvalidProfile:
MessageDialog md1 = new MessageDialog("Invalid profile.");
await md1.ShowAsync();
break;
default:
MessageDialog md2 = new MessageDialog("Unknown failure.");
await md2.ShowAsync();
break;
}
}
//txtDisplay.Text = a;
}
void TranscodeProgress(IAsyncActionWithProgress<double> asyncInfo, double percent)
{
}
void TranscodeComplete(IAsyncActionWithProgress<double> asyncInfo, AsyncStatus status)
{
asyncInfo.GetResults();
if (asyncInfo.Status == AsyncStatus.Completed)
{
// Display or handle complete info.
}
else if (asyncInfo.Status == AsyncStatus.Canceled)
{
// Display or handle cancel info.
}
else
{
// Display or handle error info.
}
}
You should:
Avoid async void.
Use the TAP naming pattern (make your Task-returning methods end in "Async").
Use AsTask to do complex interop between TAP and WinRT asynchronous operations.
Something like this:
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
...
await TranscodeFileAsync(source, destination);
}
async Task TranscodeFileAsync(StorageFile srcFile, StorageFile destFile)
{
MediaEncodingProfile profile =
MediaEncodingProfile.CreateWmv(VideoEncodingQuality.HD1080p);
MediaTranscoder transcoder = new MediaTranscoder();
PrepareTranscodeResult prepareOp = await
transcoder.PrepareFileTranscodeAsync(srcFile, destFile, profile);
if (prepareOp.CanTranscode)
{
var progress = new Progress<double>(percent => { pg1.Value = percent; });
var result = await prepareOp.TranscodeAsync().AsTask(progress);
// Display result.
}
else
{
...
}
}
You are trying to access UI component from non UI Thread.
use:
void TranscodeProgress(IAsyncActionWithProgress<double> asyncInfo, double percent)
{
if(InvokeRequired)
{
Invoke(new MethodInvoker() => TranscodeProgress(asyncInfo, percent));
return;
}
pg1.Value=percent;
}
You cannot access UI components from non UI threads, Calling Invoke with a delegate passes the function call to thread that owns the component and than that thread call the passed delegate.
Related
I'm currently using a BarcodeScanner with a custom preview showed in a ContentDialog.
I genuinely used the code provided on the Microsoft Documentation, and the thing is working right, but only once.
Here's my whole BarcodeScanner service class:
public class BarcodeScannerUtil : IBarcodeScanner
{
private static Windows.Devices.PointOfService.BarcodeScanner _scanner = null;
private static ClaimedBarcodeScanner _claimedBarcodeScanner = null;
private static Action<string> _callback;
MediaCapture mediaCapture;
bool isPreviewing;
DisplayRequest displayRequest = new DisplayRequest();
CameraPreviewDialog preview = new CameraPreviewDialog();
public async Task ClaimScannerAsync()
{
string selector = Windows.Devices.PointOfService.BarcodeScanner.GetDeviceSelector();
DeviceInformationCollection deviceCollection = await DeviceInformation.FindAllAsync(selector);
if (_scanner == null)
_scanner = await Windows.Devices.PointOfService.BarcodeScanner.FromIdAsync(deviceCollection[0].Id);
if (_scanner != null)
{
if (_claimedBarcodeScanner == null)
_claimedBarcodeScanner = await _scanner.ClaimScannerAsync();
if (_claimedBarcodeScanner != null)
{
_claimedBarcodeScanner.DataReceived += ClaimedBarcodeScanner_DataReceivedAsync;
_claimedBarcodeScanner.ReleaseDeviceRequested += ClaimedBarcodeScanner_ReleaseDeviceRequested;
_claimedBarcodeScanner.IsDecodeDataEnabled = true;
_claimedBarcodeScanner.IsDisabledOnDataReceived = true;
await _claimedBarcodeScanner.EnableAsync();
//await _claimedBarcodeScanner.ShowVideoPreviewAsync();
await _claimedBarcodeScanner.StartSoftwareTriggerAsync();
await StartPreviewAsync();
Debug.WriteLine("Barcode Scanner claimed");
}
}
}
private MediaCaptureInitializationSettings InitCaptureSettings()
{
var _captureInitSettings = new MediaCaptureInitializationSettings();
_captureInitSettings.VideoDeviceId = _scanner.VideoDeviceId;
_captureInitSettings.StreamingCaptureMode = StreamingCaptureMode.Video;
_captureInitSettings.PhotoCaptureSource = PhotoCaptureSource.VideoPreview;
return _captureInitSettings;
}
private async Task StartPreviewAsync()
{
try
{
mediaCapture = new MediaCapture();
await mediaCapture.InitializeAsync(InitCaptureSettings());
displayRequest.RequestActive();
DisplayInformation.AutoRotationPreferences = DisplayOrientations.Landscape;
}
catch (UnauthorizedAccessException)
{
await new ErrorDialog("Impossible d'acceder à la caméra, veuillez vérifier les permissions de l'application.").ShowAsync();
}
try
{
preview.Source = mediaCapture;
await mediaCapture.StartPreviewAsync();
isPreviewing = true;
ContentDialogResult resPreview = await preview.ShowAsync();
//clic sur le bouton annuler
if (resPreview == ContentDialogResult.Secondary)
{
await CleanupCameraAsync();
await _claimedBarcodeScanner.StopSoftwareTriggerAsync();
await _claimedBarcodeScanner.DisableAsync();
}
}
catch (System.IO.FileLoadException)
{
mediaCapture.CaptureDeviceExclusiveControlStatusChanged += _mediaCapture_CaptureDeviceExclusiveControlStatusChanged;
}
}
private async Task CleanupCameraAsync()
{
if (mediaCapture != null)
{
if (isPreviewing)
{
await mediaCapture.StopPreviewAsync();
}
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
preview.Source = null;
if (displayRequest != null)
{
displayRequest.RequestRelease();
}
mediaCapture.Dispose();
mediaCapture = null;
});
}
}
public void Subscribe(Action<string> callback)
{
// it makes sense to have only one foreground barcode reader client at a time
_callback = callback;
}
/// <summary>
/// Retire une action callback du BarcodeScanner
/// </summary>
public void Unsubscribe()
{
_callback = null;
}
private async void ClaimedBarcodeScanner_DataReceivedAsync(ClaimedBarcodeScanner sender, BarcodeScannerDataReceivedEventArgs args)
{
await CleanupCameraAsync();
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
preview.Hide();
});
if (_callback == null)
return;
var barcode = CryptographicBuffer.ConvertBinaryToString(BinaryStringEncoding.Utf8, args.Report.ScanDataLabel);
_callback(barcode);
}
private void ClaimedBarcodeScanner_ReleaseDeviceRequested(object sender, ClaimedBarcodeScanner e)
{
// always retain the device
e.RetainDevice();
}
private async void _mediaCapture_CaptureDeviceExclusiveControlStatusChanged(MediaCapture sender, MediaCaptureDeviceExclusiveControlStatusChangedEventArgs args)
{
if (args.Status == MediaCaptureDeviceExclusiveControlStatus.SharedReadOnlyAvailable)
{
await new ErrorDialog("Impossible d'acceder à la caméra car elle est utilisée par une autre application.").ShowAsync();
}
else if (args.Status == MediaCaptureDeviceExclusiveControlStatus.ExclusiveControlAvailable && !isPreviewing)
{
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await StartPreviewAsync();
});
}
}
}
When I'm showing my preview I'm using IsDisabledOnDataReceived = true; but when I reopen my barcode scanner a second time, I get an exception saying System.Exception : 'The request is invalid in the current state. Started' at await mediaCapture.StopPreviewAsync(); in the CleanupCameraAsync()
That's really weird because when I click on the Cancel button of the ContentDialog showing the preview it's doing exactly the same thing and there's no problem after that.
I've been searching for an hour now and have no clue about what it could come from.
Finally found an answer by myself :
I moved the CleanupCameraAsync() from my OnReceived event to my content dialog results
ContentDialogResult resPreview = await preview.ShowAsync();
//clic sur le bouton annuler
if (resPreview == ContentDialogResult.Secondary)
{
await _claimedBarcodeScanner.StopSoftwareTriggerAsync();
await _claimedBarcodeScanner.DisableAsync();
await CleanupCameraAsync();
}
else
{
await CleanupCameraAsync();
}
So when I close the dialog with the button it works, and if it closes by any other way CleanupCameraAsync() will still fire.
Plus, I added some lines in the CleanupCameraAsync() to reset all the components of my BarcodeScanner
private async Task CleanupCameraAsync()
{
if (mediaCapture != null)
{
if (isPreviewing)
{
await mediaCapture.StopPreviewAsync();
}
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
preview.Source = null;
if (displayRequest != null)
{
displayRequest.RequestRelease();
}
mediaCapture.Dispose();
mediaCapture = null;
/*******added these lines*********/
displayRequest = new DisplayRequest();
preview = new CameraPreviewDialog();
_scanner = null;
_claimedBarcodeScanner = null;
});
}
}
It's not really clean but it forces the BarcodeScanner to have the default behavior everytime I call it.
If you have any better solution, let me know please.
I'm trying to get a TextBlock to show up on screen with the words "Loading..." with the number of dots changing every half a second to indicate that a file is currently being parsed. Unfortunately, it doesn't animate like I want it to when the file is being parsed. This is what I have currently:
private void MainWindow_MIDIBrowseClick(object sender, RoutedEventArgs e)
{
MIDIBrowseClick?.Invoke(this, e);
OpenFileDialog browseDialog = new OpenFileDialog
{
Filter = "MIDI files (*.mid)|*.mid|All files (*.*)|*.*"
};
if (browseDialog.ShowDialog() == true)
{
try
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { ShowLoadingText(tokenSource.Token); }));
MIDIParser midiParse = new MIDIParser(File.ReadAllBytes(browseDialog.FileName));
midiParse.fileName = browseDialog.SafeFileName;
midiParse.ParseFile();
NoteParser noteParse = new NoteParser(midiParse);
noteParse.ParseEvents();
tokenSource.Cancel();
DataContext = new PianoRollView(midiParse, noteParse);
}
catch (InvalidOperationException)
{
MessageBox.Show("Error parsing MIDI file!", "Error");
}
}
}
That calls the below method which animates the "Loading..." text:
private async void ShowLoadingText(CancellationToken token)
{
txtLoading.Visibility = Visibility.Visible;
try
{
while (!token.IsCancellationRequested)
{
txtLoading.Text = "Loading";
await Task.Delay(500, token);
txtLoading.Text = "Loading.";
await Task.Delay(500, token);
txtLoading.Text = "Loading..";
await Task.Delay(500, token);
txtLoading.Text = "Loading...";
await Task.Delay(500, token);
}
}
catch (TaskCanceledException)
{
txtLoading.Visibility = Visibility.Hidden;
}
}
Not sure what I'm doing wrong. Any help would be appreciated!
It seems like you try to invoke the parsing operation from the UI thread synchronously. That's why it locked UI and the text isn't animated. You should try to add an asynchronous invocation of your parsing operation. Something like this:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
CancellationTokenSource tokenSource = new CancellationTokenSource();
Dispatcher.Invoke(DispatcherPriority.Background, new ThreadStart(delegate { ShowLoadingText(tokenSource.Token); }));
await Task.Run(() => Thread.Sleep(5000)); // your parsing operation
tokenSource.Cancel();
}
private async void ShowLoadingText(CancellationToken token)
{
txtLoading.Visibility = Visibility.Visible;
try
{
while (!token.IsCancellationRequested)
{
txtLoading.Text = "Loading";
await Task.Delay(500, token);
txtLoading.Text = "Loading.";
await Task.Delay(500, token);
txtLoading.Text = "Loading..";
await Task.Delay(500, token);
txtLoading.Text = "Loading...";
await Task.Delay(500, token);
}
}
catch (TaskCanceledException)
{
txtLoading.Visibility = Visibility.Hidden;
}
}
}
}
In the sample above you will see Loading animation with 5 sec duration. This is achieved by asynchronous invocation of Thread.Sleep(5000). In your case you should write a method which will implement parsing operation:
private void Parse()
{
MIDIParser midiParse = new MIDIParser(File.ReadAllBytes(browseDialog.FileName));
midiParse.fileName = browseDialog.SafeFileName;
midiParse.ParseFile();
NoteParser noteParse = new NoteParser(midiParse);
noteParse.ParseEvents();
}
And replace Thread.Sleep(5000) with its invocation.
Application works when its connected to PC and run with debugger. The problem starts when I disconnect phone from PC, run app from phone and try to open gallery and set image to image control. I tried to write error in a file on try/catch but catch is never called, like there is no error on app executing.
This is code where i select img:
private async void galleryBtn_Click(object sender, RoutedEventArgs e)
{
try
{
FileOpenPicker filePicker = new FileOpenPicker();
filePicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
filePicker.ViewMode = PickerViewMode.Thumbnail;
// Filter to include a sample subset of file types
filePicker.FileTypeFilter.Clear();
filePicker.FileTypeFilter.Add(".bmp");
filePicker.FileTypeFilter.Add(".png");
filePicker.FileTypeFilter.Add(".jpeg");
filePicker.FileTypeFilter.Add(".jpg");
filePicker.PickSingleFileAndContinue();
view.Activated += viewActivated;
}
catch (Exception err)
{
string error = err.StackTrace.ToString();
await saveStringToLocalFile("test11", error);
}
}
And than it goes to :
private async void viewActivated(CoreApplicationView sender, IActivatedEventArgs args1)
{
try
{
FileOpenPickerContinuationEventArgs args = args1 as FileOpenPickerContinuationEventArgs;
if (args != null)
{
if (args.Files.Count == 0) return;
view.Activated -= viewActivated;
StorageFile storageFile = args.Files[0];
var stream = await storageFile.OpenAsync(Windows.Storage.FileAccessMode.Read);
var bitmapImage = new Windows.UI.Xaml.Media.Imaging.BitmapImage();
await bitmapImage.SetSourceAsync(stream);
var decoder = await Windows.Graphics.Imaging.BitmapDecoder.CreateAsync(stream);
var obj = App.Current as App;
obj.ImageToEdit = bitmapImage;
obj.fileTransfer = storageFile;
checkTorch = -1;
await newCapture.StopPreviewAsync();
Frame.Navigate(typeof(EditImage));
}
}
catch (Exception err) {
string error = err.StackTrace.ToString();
await saveStringToLocalFile("test11", error);
}
}
When img is selected i open screen for image editing and run this
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
try
{
var obj = App.Current as App;
slika = obj.ImageToEdit;
original = obj.ImageToEdit;
ImagePreview.Source = slika;
RotateTransform myRotateTransform = new RotateTransform();
myRotateTransform.Angle = 0;
ImagePreview.RenderTransform = myRotateTransform;
var localSettings = ApplicationData.Current.LocalSettings;
}
catch (Exception err)
{
string error = err.StackTrace.ToString();
await saveStringToLocalFile("test11", error);
}
}
That is all, any advice is appreciated;
Problem was with my MediaCapture. First use mediaCapture.stopPreviewAsync(); to stop preview and than you must release the mediaCapture.
Before you call fileOpener use this code:
newCapture.Dispose();
In order to catch unhandled exceptions you can use a global exception catcher,
in App.xaml.cs file define:
public App()
{
this.InitializeComponent();
this.Suspending += this.OnSuspending;
this.UnhandledException += UnhandledExceptionHandler;
}
private void UnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
{
log.Critical(e.Exception);
}
It's important to understand that not all exceptions can be caught using try\catch such as Corrupt State Exceptions:
https://msdn.microsoft.com/en-us/magazine/dd419661.aspx#id0070035
In this case you can debug the issue by viewing the .dmp file generated by your application found in: {Phone}\Documents\Debug
Looking to chain a task to a previous instance if it exists. Currently, both are executed at the same time.
Initial code that works for one task :
private async void MenuMediaAddFiles_OnClick(object sender, RoutedEventArgs e)
{
var dialog = GetDefaultOpenFileDialog();
using (dialog)
{
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var progress = new SimpleProgress(this))
{
int addFiles = await _context.AddFiles(dialog.FileNames, progress);
Console.WriteLine("Files added: {0}", addFiles);
}
}
}
}
A failed attempt to make it work :
Task<int> _files;
private async void MenuMediaAddFiles_OnClick(object sender, RoutedEventArgs e)
{
var dialog = GetDefaultOpenFileDialog();
using (dialog)
{
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var progress = new SimpleProgress(this))
{
int addFiles;
Task<int> files = _context.AddFiles(dialog.FileNames, progress);
if (_files == null)
{
_files = files;
}
else
{
var task1 = await _files.ContinueWith(task => _context.AddFiles(dialog.FileNames, new SimpleProgress(this)));
}
addFiles = await _files;
Console.WriteLine("Files added: {0}", addFiles);
}
}
}
}
You were pretty close, but there were a few things that needed to be modified:
private Task<int> previousTask = Task.FromResult(0);
private async void MenuMediaAddFiles_OnClick(object sender, RoutedEventArgs e)
{
var dialog = GetDefaultOpenFileDialog();
using (dialog)
{
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var progress = new SimpleProgress(this))
{
previousTask = previousTask.ContinueWith(t =>
_context.AddFiles(dialog.FileNames, progress))
.UnWrap(); ;
int addFiles = await previousTask;
Console.WriteLine("Files added: {0}", addFiles);
}
}
}
}
Things to note:
Rather than having the previous task be null sometimes, it was easier to initialize it to an already completed task (Task.FromResult(0)). This avoids the null check code.
You were calling AddFiles twice. You shouldn't have been calling it before the if, and you weren't ever assigning the task to the instance field inside the if.
I used UnWrap instead of await to turn the Task<Task<int>> into a Task<int>. Both work, but in this case I felt UnWrap made its intentions clearer.
Note that since the entire event handler will be running in the UI thread there's no need to synchronize access to previousTask, if it doesn't, you'd need to do some locking.
I have to load two large files in parallels
so far I have this code
The code below is click button method
private async void MILoadLogFile_Click(object sender, RoutedEventArgs e)
{
...
if (oFD.ShowDialog() == true)
{
await myLogSession.LoadCompassLogAsync(oFD.FileName);
await myLogSession.LoadCoreServiceLogAsync(oFD.FileName);
}
}
loading method:
public async Task LoadCompassLogAsync(String fileName)
{
StreamReader streamReader = new StreamReader(fileName);
if (fileName.Contains("Compass"))
{
...
try
{
using (streamReader)
{
//Console.Out.WriteLine("lineCount: " + lineCount);
while (((line = await streamReader.ReadLineAsync()) != null)
&& !CompassLogLoadCompleted)
{
...
loggingLvl = new LoggingLvl(eLoggingLvl);
CompassLogData cLD = new CompassLogData(id, dateTime, loggingLvl, threadId, loggingMessage);
await addRoCompassLogCollectionAsync(cLD);
}
}
}
catch (Exception e)
{
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
}
the LoadCoreServiceLogAsync is almost identical to LoadCompassLogAsync.
The two loading methods runs sequentially. I want them to run in parallel.
Your code will run one task after the other. To run the two tasks in parallel you can use the Task.WaitAll method:
var loadCompassLogTask = myLogSession.LoadCompassLogAsync(oFD.FileName);
var loadCoreServiceLogTask = myLogSession.LoadCoreServiceLogAsync(oFD.FileName);
Task.WaitAll(loadCompassLogTask, loadCoreServiceLogTask);
Or if you want to use await you can use Task.WhenAll:
var loadCompassLogTask = myLogSession.LoadCompassLogAsync(oFD.FileName);
var loadCoreServiceLogTask = myLogSession.LoadCoreServiceLogAsync(oFD.FileName);
await Task.WhenAll(loadCompassLogTask, loadCoreServiceLogTask);