I'm working on a Windows Store App which will access the device's microphone. However, when I attempt to work with the media devices, execution gets lost in the await somewhere. So far, I've noticed this on the following three scenarios:
var devices = await DeviceInformation.FindAllAsync(DeviceClass.AudioCapture); // never returns
var inputDeviceId = MediaDevice.GetDefaultAudioCaptureId(AudioDeviceRole.Communications);
var device = await DeviceInformation.CreateFromIdAsync(inputDeviceId); // never returns
var mediaCapture = new MediaCapture();
var settings = new MediaCaptureInitializationSettings
{
StreamingCaptureMode = StreamingCaptureMode.Audio,
AudioProcessing = AudioProcessing.Default,
MediaCategory = MediaCategory.Other,
AudioDeviceId = inputDeviceId,
VideoDeviceId = "",
};
try
{
await _mediaCapture.InitializeAsync(settings); // never returns
}
catch (Exception ex)
{
throw new Exception("Microphone is not available.", ex);
}
I am using Visual Studio 2013 Update 2 RC and developing on a Surface Pro 2. I have tried debugging in Local Machine and Simulator modes but both with the same results. I do have the Microphone capability selected in the App's manifest. I expect the OS should be prompting me for access to the microphone device but I am not presented with that dialog.
Any help is greatly appreciated! Thanks in advance.
I suspect that further up your call stack, you are blocking the UI thread by calling Task.Wait or Task<T>.Result. This will cause a deadlock, as I explain on my blog.
The reason this deadlocks is because await captures a "context" (in this case, the UI context) and will use that to resume its async method. However, if the UI thread is blocked, then the async method cannot continue.
Related
I'm developing a WPF application where I'm recording audio data with the Windows.Media.Capture.MediaCapture class. It's working if I initialize with or without parameters:
var mediaCapture = new MediaCapture();
await mediaCapture.InitializeAsync();
or I can add which microphone to use (if there are more than one):
var allAudioDevices = await DeviceInformation.FindAllAsync(DeviceClass.AudioCapture);
DeviceInformation microphone = allAudioDevices.FirstOrDefault();
MediaCaptureInitializationSettings mediaInitSettings = new MediaCaptureInitializationSettings {
AudioDeviceId = microphone.Id,
StreamingCaptureMode = StreamingCaptureMode.Audio
};
await _mediaCapture.InitializeAsync(mediaInitSettings);
The problem comes when I run my app as a UWP application (with desktop bridge). As a UWP app when it calls the InitializeAsync() method, it always throws an exception with the following (verbose :) ) error message: Element not found. The DeviceInformation object of the microphone is found correctly, so something happens during the initialization of the MediaCapture.
The Microphone capability is set in the manifest file of the bridge project.
What am I doing wrong? I'm also open to use other methods to record the voice.
I figured out that if I run the initialization on the UI thread, it works well:
await Application.Current.Dispatcher.InvokeAsync(async () => {
await mediaCapture.InitializeAsync();
});
What I want to do:
- synchronously (or even asynchronously) load settings from USB drive before first page loads
What I did:
- in OnLaunched method for App.xaml.cs I invoked this static function:
public static async void LoadSettings(string folderName = "Config", string fileName = "Settings.xml")
{
try
{
StorageFile configFile = null;
// scan through all devices
foreach (var device in await KnownFolders.RemovableDevices.GetFoldersAsync().AsTask().ConfigureAwait(false))
{
// folder that should have configuration
var configFolder = await device.GetFolderAsync(folderName).AsTask().ConfigureAwait(false);
if (configFile != null && configFolder != null && await configFolder.GetFileAsync(fileName).AsTask().ConfigureAwait(false) != null)
{
throw new Exception("More than one configuration file detected. First found configuration file will be used.");
}
else
configFile = await configFolder.GetFileAsync(fileName).AsTask().ConfigureAwait(false);
}
if (configFile == null)
throw new Exception("Configuration file was not found, please insert device with proper configuration path.");
string settingString = await FileIO.ReadTextAsync(configFile).AsTask().ConfigureAwait(false);
XmlSerializer serializer = new XmlSerializer(typeof(Settings));
using (TextReader reader = new StringReader(settingString))
{
AppSettings = (Settings)serializer.Deserialize(reader); // store settings in some static variable
}
}
catch (Exception e)
{
//return await Task.FromResult<string>(e.Message);
}
//return await Task.FromResult<string>(null);
}
As you can see right now it's async void method, so I don't even want to synchronize it in any way with UI thread. It should just fire and do something. With ConfigureAwait(false) I want to be sure that it will never try to return to context. These returns at the end are remnants of other things I tried (I wanted to do this better way, this is the most primitive solution and it still doesn't work).
Anyway, because that's where the fun begins: everything works well when I debug application on local machine with Win 10. And I get deadlocked thread on Win 10 IOT installed on Raspberry Pi 3 (I installed it from the scratch today, last version).
But deadlock is not the weirdest thing. Weirdest thing is when it appears.
Like I said, invocation of this method looks like that:
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Configuration.Settings.LoadSettings();
After that everything in this method goes normally, so I navigate to my first page somewhere below:
if (e.PrelaunchActivated == false)
{
if (rootFrame.Content == null)
{
rootFrame.Navigate(typeof(LogScreen), e.Arguments);
}
Window.Current.Activate();
}
Everything still works. User needs to write his code, I check if this code is available in settings and after that user can press "OK" to move to next page. Somewhere in LogScreenViewModel this method is responsible for that:
private void GoForward(bool isValid)
{
try
{
_navigationService.NavigateTo("MainPage"); // it's SimpleIoc navigation from MVVMLight
}
catch (Exception e)
{
Debug.WriteLine($"ERROR: {e.Message}");
}
}
And deadlock happens when _navigationService.NavigateTo("MainPage") is reached. Basically right now UI thread freezes. If I wait for long enough I will see catched exception in Output saying that messenger seemed occupied (I can't show the screen because I don't have access to that Raspberry right now) and after some timeout this thread was killed (like 30 seconds or something) - after that UI thread unlocks and application proceeds to MainPage. It doesn't happen on PC - MainPage appears immediately, no exceptions, no deadlocks.
I tried waiting on first page for like 1 minute to check if some deadlock exception would fire on it's own - but it doesn't. It will fire ONLY after I try to proceed to next page.
What else I tried instead of this fire-and-forget approach:
Making OnLaunched async and await LoadSettings returning Task - same thing happens in the same place, and no problem on PC.
Using:
Window.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, async () => await Configuration.Settings.LoadSettings(); ).AsTask().Wait(); If I remember correctly it deadlocked immediately on Wait(), even with ConfigureAwait(false) everywhere, but it also happened on PC.
Allowing LogScreen to load, make it's OnNavigatedTo method async and await LoadSettings - same deadlock in same place
Allowing LogScreen to load and use Dispatcher from there like in point 2. It deadlocked the same way after reaching Wait(), on PC too.
Trying to force LoadSettings to be fully synchronous by replacing every await with AsTask().GetAwaiter().GetResults(). It worked well on PC... and of course deadlock on Raspberry.
What am I missing? What else can I try? Because to be honest right now it looks to me that Win 10 IOT .NET runtime is bugged or something.
I think I resolved the issue. This code was generally speaking not mine and after some digging I noticed that someone before me tried to list some other external devices while navigating to MainPage. It was not really async-safe code (someone probably wasn't aware of synchronization context) and it worked on Win 10 only because on desktop it was looking for COM0 device and I only have COM2, so method causing trouble was not even invoked at all.
I still have no idea how related it was to my configuration (because it somehow was working without it), but after I fixed issues with this old not-async-safe code it started to behave as expected.
I'm trying to re-purpose a code-snippet for a UWP app using Windows.Media.SpeechRecognition's SpeechRecognizer class.
The problem is that it doesn't seem to utilize the timeout that I set for EndSilenceTimeout -- yet both BabbleTimeout and InitialSilenceTimeout appear to be working just fine. Basically, a 1-2 second pause will always end the session, and I'm trying to figure out how to fix or work around this.
I've tried with both RecognizeAsync and RecognizeWithUIAsync but neither made a difference.
private async void StartRecognizing()
{
var speechRecognizer = new SpeechRecognizer();
speechRecognizer.Timeouts.EndSilenceTimeout = TimeSpan.FromSeconds(10);
speechRecognizer.Timeouts.BabbleTimeout = TimeSpan.FromSeconds(10);
speechRecognizer.Timeouts.InitialSilenceTimeout = TimeSpan.FromSeconds(10);
await speechRecognizer.CompileConstraintsAsync();
SpeechRecognitionResult speechRecognitionResult = await speechRecognizer.RecognizeAsync();
var messageDialog = new Windows.UI.Popups.MessageDialog(speechRecognitionResult.Text, "Text spoken");
await messageDialog.ShowAsync();
}
To test this, just make a new UWP app in Visual Studio and grant yourself permission to access the microphone in the package app manifest; additionally you'll need to make sure your OS itself allows for this in the Speech, inking, & typing settings on Windows 10 v1803.
I'm having some files that I need to download on app startup (first run).
I am using the Background Downloader in windows 8.
This is how I use it:
BackgroundDownloader downloader = new BackgroundDownloader();
List<DownloadOperation> operations = new List<DownloadOperation>();
foreach (FileInfo info in infoFiles)
{
Windows.Storage.ApplicationData.Current.LocalFolder;
foreach (string folder in info.Folders)
{
currentFolder = await currentFolder.CreateFolderAsync(folder, CreationCollisionOption.OpenIfExists);
}
//folder hierarchy created, save the file
StorageFile file = await currentFolder.CreateFileAsync(info.FileName, CreationCollisionOption.ReplaceExisting);
DownloadOperation op = downloader.CreateDownload(new Uri(info.Url), file);
activeDownloads.Add(op);
operations.Add(op);
}
foreach (DownloadOperation download in operations)
{
//start downloads
await HandleDownloadAsync(download, true);
}
When I try to use BackgroundDownloader.GetCurrentDownloadsAsync(); I get only one background download discovered. So I removed the await operator above to get them to start asynchronously.
However I need to know when all files are finished so I can set a progress bar.
I need a way to download multiple files, discover all of them in BackgroundDownloader.GetCurrentDownloadsAsync(); and know when all downloads are completed.
private async Task HandleDownloadAsync(DownloadOperation download, bool start)
{
try
{
Debug.WriteLine("Running: " + download.Guid, NotifyType.StatusMessage);
// Store the download so we can pause/resume.
activeDownloads.Add(download);
Progress<DownloadOperation> progressCallback = new Progress<DownloadOperation>(DownloadProgress);
if (start)
{
// Start the download and attach a progress handler.
await download.StartAsync().AsTask(cts.Token, progressCallback);
}
else
{
// The download was already running when the application started, re-attach the progress handler.
await download.AttachAsync().AsTask(cts.Token, progressCallback);
}
ResponseInformation response = download.GetResponseInformation();
Debug.WriteLine(String.Format("Completed: {0}, Status Code: {1}", download.Guid, response.StatusCode),
NotifyType.StatusMessage);
}
catch (TaskCanceledException)
{
Debug.WriteLine("Canceled: " + download.Guid, NotifyType.StatusMessage);
}
catch (Exception ex)
{
if (!IsExceptionHandled("Execution error", ex, download))
{
throw;
}
}
finally
{
activeDownloads.Remove(download);
}
}
If you specifically need to re-download the file at every app startup, you shouldn't use BackgroundDownloader - it's intended for scenarios where you need downloads that keep on happening even when your app gets suspended and then restarted. If you're just going to re-download the files again when your app starts up again next time, you're just wasting battery by allowing the downloads to continue once your app goes away.
You have a few options for non-background-capable downloads in a Windows Store app. The most straightforward is Windows.Web.Http.HttpClient - here's a good primer on how to use it. However, it's only available in Windows 8.1 apps, not Windows 8.0. If you need Windows 8.0 compatibility, the distinct but similarly named System.Net.Http.HttpClient is probably the way to go. Harrison already provided a link to the relevant Quickstart documentation.
If you really do want background download capability, please revise your question to more accurately reflect your scenario. It's certainly possible to use BackgroundDownloader to start up several background downloads concurrently and attach progress and completion handlers to each of them, but that doesn't sound like the best solution for the scenario in your question.
Now that you've clarified your usage, the issue is more clear. Your HandleDownloadAsync method is attaching the progress handler you want to the download, but then it's awaiting the completion of that download. When you write
foreach (DownloadOperation download in operations)
{
//start downloads
await HandleDownloadAsync(download, true);
}
you are therefore awaiting on the completion of each download in turn before you start the next download.
The solution here is to not await the result of your HandleDownloadAsync calls until after you've performed all of the calls - this will start up each of the downloads immediately and attach the appropriate progress handlers without waiting for any of them to complete. A revised version might look something like this:
BackgroundDownloader downloader = new BackgroundDownloader();
List<Task> downloadCompletionTasks = new List<Task>();
foreach (FileInfo info in infoFiles)
{
StorageFile file = /* ... */;
DownloadOperation op = downloader.CreateDownload(new Uri(info.Url), file);
// Consider moving this line into HandleDownloadAsync so you won't have to repeat
// it in the code that's handling the GetCurrentDownloadsAsync logic.
activeDownloads.Add(op);
// Starting the download, but not awaiting its completion yet
Task downloadCompletionTask = HandleDownloadAsync(op, true);
downloadCompletionTasks.Add(downloadCompletionTask);
}
// Now that we've got all the downloads started concurrently, we can wait until all of them complete
await Task.WhenAll(downloadCompletionTasks);
I'm trying to launch a uri from my windows 8.1 app but can't get it to launch in UseHalf mode
I researched and found that it should be done like this
LauncherOptions lo = new LauncherOptions();
lo.DesiredRemainingView = ViewSizePreference.UseHalf;
lo.DisplayApplicationPicker = true;
await Windows.System.Launcher.LaunchUriAsync(new Uri("http://www.youtube.com"),lo);
but it keeps launching the web browser in full screen
I read that the launch depends on many variables and it is not insured that these settings will be applied
my question is there a way to insure that my app and the browser will open side by side in half screen mode?
It have to work:
Uri uri = new Uri("http://www.youtube.com");
LauncherOptions options = new LauncherOptions
{
DesiredRemainingView = ViewSizePreference.UseHalf
};
await Launcher.LaunchUriAsync(uri, options);
If not, please try this long code:
ApplicationView currentAppView = ApplicationView.GetForCurrentView();
CoreApplicationView newCoreAppView = CoreApplication.CreateNewView();
newCoreAppView.Dispatcher.RunAsync(
CoreDispatcherPriority.Normal,
async () =>
{
// This executes on the new dispatcher so I assume that Window.Current and so on
// reflects the new Window associated with that dispatcher rather than the original
// dispatcher.
Window newWindow = Window.Current;
ApplicationView newAppView = ApplicationView.GetForCurrentView();
await ApplicationViewSwitcher.TryShowAsStandaloneAsync(
newAppView.Id,
ViewSizePreference.UseHalf,
currentAppView.Id,
ViewSizePreference.UseHalf);
});
from Here.
Well after consulting the msdn developers forum it turns out this is the only way to do it
and there is no way for an app to force this kind of behavior the result of this code depends on the setting of the browser and many other variables