I have code in a WinForms application where I'm using the WinRT MediaCapture object to take a picture from the device's camera. The code is executed in a System.Threading.Timer call back. I'm trying to move this code into WPF but I'm running into problems. When I try to execute the MediaCapture.CapturePhotoToStreamAsync from the timer's callback in WPF, I receive the following exception:
A first chance exception of type 'System.Exception' occurred in MyAssembly.dll
Additional information: The request is invalid in the current state.
Started
I can create a for loop to execute the method 1000 times and it will work, however, it bombs if the method is called from within a Timer callback.
So to clarify, the code will work in WinForms in a Timer callback The code will bomb in WPF in a Timer callback The code will work in WPF if it's not executed in a timer callback..
I suspect that the issue has something to do with the thread that it's executing on, however, I've tried to use the Dispatcher to no avail.
here is the method that is called:
public async Task CapturePhoto(int width, int height)
{
Thread.Sleep(100);
var jpgProperties = ImageEncodingProperties.CreateJpeg();
jpgProperties.Width = (uint)width;
jpgProperties.Height = (uint)height;
using (var randomAccessStream = new InMemoryRandomAccessStream())
{
if (_MediaCaptureManager != null)
{
await _MediaCaptureManager.CapturePhotoToStreamAsync(jpgProperties, randomAccessStream);
randomAccessStream.Seek(0);
using (var ioStream = randomAccessStream.AsStream())
{
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = ioStream;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
// copy to byte array
int stride = bitmapImage.PixelWidth * 4;
byte[] buffer = new byte[stride * bitmapImage.PixelHeight];
bitmapImage.CopyPixels(buffer, stride, 0);
// create bitmap
System.Drawing.Bitmap bitmap =
new System.Drawing.Bitmap(
bitmapImage.PixelWidth,
bitmapImage.PixelHeight,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
// lock bitmap data
System.Drawing.Imaging.BitmapData bitmapData =
bitmap.LockBits(
new System.Drawing.Rectangle(0, 0, bitmap.Width, bitmap.Height),
System.Drawing.Imaging.ImageLockMode.WriteOnly,
bitmap.PixelFormat);
// copy byte array to bitmap data
System.Runtime.InteropServices.Marshal.Copy(
buffer, 0, bitmapData.Scan0, buffer.Length);
// unlock
bitmap.UnlockBits(bitmapData);
ImageBrush backgroundBrush = new ImageBrush();
backgroundBrush.ImageSource = bitmapImage;
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
PreviewPanel.Background = backgroundBrush;
}));
}
}
}
}
I have a small WPF project that I can share via OneDrive if that will help. I don't want to include the URL by default because I'm not sure if that is allowed on SO.
Why can't I use CapturePhotoToStreamAsync from a System.Threading.Timer call back in WPF?
You'll need to make sure that the
CapturePhotoToStreamAsync
is executed on the UI thread.You should use the
DispatcherTimer
rather than aThreading.Timer
class.