-1

I have a Word add-in that has a WPF window that is launched from a Ribbon button. The WPF window is used for capturing pictures from webcam. It has two windows: Live and Snap. It also has three buttons: Start (shows live video in Live), Capture (copies the current frame of Live into Snap) and Close (closes the form). It also has a dropdown for selecting the correct camera.

I have a working code as Windows Forms here:

private void StartButton_Click(object sender, EventArgs e)
    {
        FinalFrame = new VideoCaptureDevice(CaptureDevice[cboDevices.SelectedIndex].MonikerString);
        FinalFrame.NewFrame += FinalFrame_NewFrame;
        FinalFrame.Start();
    }

        private void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs)
    {
        pboLive.Image = (Bitmap)eventArgs.Frame.Clone();
    }

But with WPF I had a problem with threads. Now with help of people here: WPF Calling thread cannot access with eventargs I got it to work with WPF, but it works exactly once - if I try to press start after closing the form one the Live stays empty.

private FilterInfoCollection CaptureDevice;
private VideoCaptureDevice FinalFrame;
private void Grid_Loaded(object sender, RoutedEventArgs e)
{
    capturedpictures.Clear();

    CaptureDevice = new FilterInfoCollection(FilterCategory.VideoInputDevice);

    cboDevices.Items.Clear();
    foreach (FilterInfo Device in CaptureDevice)
    {
        cboDevices.Items.Add(Device.Name);
    }
    cboDevices.SelectedIndex = cboDevices.Items.Count - 1;
    FinalFrame = new VideoCaptureDevice();
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
    int capturedeviceindex = cboDevices.SelectedIndex;
    FilterInfo cd = CaptureDevice[cboDevices.SelectedIndex];
    string cdms = cd.MonikerString;
    FinalFrame = new VideoCaptureDevice(cdms);
    FinalFrame.NewFrame += FinalFrame_NewFrame;
    FinalFrame.Start();
}

And this handles new frames:

private void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
    this.Dispatcher.Invoke(
            new Action<Bitmap>(
                (bitmap) =>
                {
                    pboLive.Source = ImageSourceForBitmap(bitmap);
                    return;
                }
            ),
            (Bitmap)eventArgs.Frame.Clone()
            );
}

public ImageSource ImageSourceForBitmap(Bitmap bmp)
{
    var handle = bmp.GetHbitmap();
    try
    {
        return Imaging.CreateBitmapSourceFromHBitmap(handle, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
    }
    finally { DeleteObject(handle); }
}

And this closes the form:

private void CaptureWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
}

When debugging the re-start, code gets stuck at FinalFrame_NewFrame and displays nothing at pboLive.

  • Does the NewFrame event gets raised on a background thread? – mm8 Aug 29 '17 at 14:05
  • As already said in an answer to your previous question, `Frame.Clone()` is redundant. I'd also still suggest to create and `Freeze()` the ImageSource *before* calling Dispatcher.Invoke. It simply puts less load on your UI thread. – Clemens Aug 29 '17 at 14:20
  • @Clemens doing it like this: `private void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs) { var imageSource = ImageSourceForBitmap(eventArgs.Frame); imageSource.Freeze(); this.Dispatcher.Invoke( new Action( (bitmap) => { pboLive.Source = ImageSourceForBitmap(bitmap); return; } ) ); }` Gives me a Parameter count mismatch inside the Dispatcher. – Arto Kilponen Os Kilponen Sep 04 '17 at 23:23
  • That makes no sense at all. You are calling ImageSourceForBitmap twice. Why are you using `Action` at all instead of `Action` and use the already created imageSource inside the Action? Please go back to my answer on your previous question. – Clemens Sep 05 '17 at 04:57
  • @Clemens Well, if I use the original code you suggested: `var imageSource = ImageSourceForBitmap(eventArgs.Frame); imageSource.Freeze(); pboLive.Dispatcher.Invoke(() => pboLive.Source = imageSource);` it works exactly once. On the later uses, it doesn't work. – Arto Kilponen Os Kilponen Sep 05 '17 at 16:33
  • @Clemens If I use it as Action, with the imageSource, like this: `var imageSource = ImageSourceForBitmap(eventArgs.Frame); imageSource.Freeze(); this.Dispatcher.Invoke( new Action( () => { pboLive.Source = imageSource; return; } ) ); ` it also works exactly once. – Arto Kilponen Os Kilponen Sep 05 '17 at 16:37
  • The problem that it works only once hasn't got to do with that code. – Clemens Sep 05 '17 at 17:33
  • @Clemens Ok, I can believe that. Then the original question of this thread remains: Why does it work exactly once? – Arto Kilponen Os Kilponen Sep 05 '17 at 18:53

2 Answers2

0

This probably doesn't answer your question but a bit of a warning from working with Excel (same goes for Word or anything else using COM)

Any COM objects your using from Word need to be handled carefully. COM does reference counting, so each time an object is used there is a +1 to a counter in the background essentially. (yeah I know it's more than this but it's a nice way to envision it) When you are done with an object you need to tell it to -1, or else that object is never cleaned up. To do this:

Marshal.ReleaseComObject(o);

You must ensure that is called for every COM object you use or else it will not get released and linger in memory. If not handled correctly it could cause many issues.

I would ensure you're decrementing the references using the above and see if your problem still persists. I've seen many issues like this where the problem was previous instances still in memory causing problems with new instances working correctly.

Kelly
  • 6,992
  • 12
  • 59
  • 76
-1

Can't you create the Bitmap the same way as before and then call your ImageSourceForBitmap method?:

private void FinalFrame_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
    this.Dispatcher.BeginInvoke(new Action(() =>
    {
        Bitmap bitmap = (Bitmap)eventArgs.Frame.Clone()
        pboLive.Source = ImageSourceForBitmap(bitmap);
    }));
}
mm8
  • 163,881
  • 10
  • 57
  • 88
  • That "Bitmap..."-line gives me "Parameter is not valid" error on the first time running it. System.ArgumentException occurred HResult=0x80070057 Message=Parameter is not valid. Source=System.Drawing StackTrace: at DocCamera.DocCameraUserControl.<>c__DisplayClass8_0.b__0() in C:\Users\... and so on. – Arto Kilponen Os Kilponen Sep 04 '17 at 23:08
  • How so? It doesn't have BeginInvoke there and it doesn't have Bitmap variable there. Where it is marked as working? – Arto Kilponen Os Kilponen Sep 05 '17 at 16:41