title: How can i detect in this class if the process finished?
body: <pre><code>using System;
  using System.Diagnostics;
  using System.Drawing;
  using System.Drawing.Imaging;
  using System.IO;
  using System.Runtime.InteropServices;
  using DirectShowLib;
  using System.Windows.Forms;
  using System.Collections.Generic;
  
  
  namespace Polkan.DataSource
  {
      internal class WmvAdapter : ISampleGrabberCB, IDisposable
      {
          #region Fields
          //public Image img;
          private IFilterGraph2 _filterGraph;
          private IMediaControl _mediaCtrl;
          private IMediaEvent _mediaEvent;
          private int _width;
          private int _height;
          private readonly string _outFolder;
          private int _frameId;
  
          #endregion
  
          #region Constructors and Destructors
  
          public WmvAdapter(string file, string outFolder)
          {
              _outFolder = outFolder;
              try
              {
                  SetupGraph(file);
              }
              catch
              {
                  Dispose();
                  MessageBox.Show("A codec is required to load this video file. Please use http://www.headbands.com/gspot/ or search the web for the correct codec");
                  throw;
              }
          }
  
          ~WmvAdapter()
          {
              CloseInterfaces();
          }
  
          #endregion
  
          public void Dispose()
          {
              CloseInterfaces();
          }
  
          public void Start()
          {
              int hr = _mediaCtrl.Run();
              WaitUntilDone();
              DsError.ThrowExceptionForHR(hr);
          }
  
          public void WaitUntilDone()
          {
              int hr;
              const int eAbort = unchecked((int)0x80004004);
  
              do
              {
  
                  System.Windows.Forms.Application.DoEvents();
                  EventCode evCode;
                  hr = _mediaEvent.WaitForCompletion(100, out evCode);
              } while 
                  (hr == eAbort);
  
              DsError.ThrowExceptionForHR(hr);
  
  
          }
  
          /// &lt;summary&gt; build the capture graph for grabber. &lt;/summary&gt;
          private void SetupGraph(string file)
          {
              ISampleGrabber sampGrabber = null;
              IBaseFilter capFilter = null;
              IBaseFilter nullrenderer = null;
  
              _filterGraph = (IFilterGraph2)new FilterGraph();
              _mediaCtrl = (IMediaControl)_filterGraph;
              _mediaEvent = (IMediaEvent)_filterGraph;
  
              var mediaFilt = (IMediaFilter)_filterGraph;
  
              try
              {
                  // Add the video source
                  int hr = _filterGraph.AddSourceFilter(file, "Ds.NET FileFilter", out capFilter);
                  DsError.ThrowExceptionForHR(hr);
  
                  // Get the SampleGrabber interface
                  sampGrabber = new SampleGrabber() as ISampleGrabber;
                  var baseGrabFlt = sampGrabber as IBaseFilter;
  
                  ConfigureSampleGrabber(sampGrabber);
  
                  // Add the frame grabber to the graph
                  hr = _filterGraph.AddFilter(baseGrabFlt, "Ds.NET Grabber");
                  DsError.ThrowExceptionForHR(hr);
  
                  // ---------------------------------
                  // Connect the file filter to the sample grabber
  
                  // Hopefully this will be the video pin, we could check by reading it's mediatype
                  IPin iPinOut = DsFindPin.ByDirection(capFilter, PinDirection.Output, 0);
  
                  // Get the input pin from the sample grabber
                  IPin iPinIn = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Input, 0);
  
                  hr = _filterGraph.Connect(iPinOut, iPinIn);
                  DsError.ThrowExceptionForHR(hr);
  
                  // Add the null renderer to the graph
                  nullrenderer = new NullRenderer() as IBaseFilter;
                  hr = _filterGraph.AddFilter(nullrenderer, "Null renderer");
                  DsError.ThrowExceptionForHR(hr);
  
                  // ---------------------------------
                  // Connect the sample grabber to the null renderer
  
                  iPinOut = DsFindPin.ByDirection(baseGrabFlt, PinDirection.Output, 0);
                  iPinIn = DsFindPin.ByDirection(nullrenderer, PinDirection.Input, 0);
  
                  hr = _filterGraph.Connect(iPinOut, iPinIn);
                  DsError.ThrowExceptionForHR(hr);
  
                  // Turn off the clock. This causes the frames to be sent
                  // thru the graph as fast as possible
                  hr = mediaFilt.SetSyncSource(null);
                  DsError.ThrowExceptionForHR(hr);
  
                  // Read and cache the image sizes
                  SaveSizeInfo(sampGrabber);
              }
              finally
              {
                  if (capFilter != null)
                  {
                      Marshal.ReleaseComObject(capFilter);
                  }
                  if (sampGrabber != null)
                  {
                      Marshal.ReleaseComObject(sampGrabber);
                  }
                  if (nullrenderer != null)
                  {
                      Marshal.ReleaseComObject(nullrenderer);
                  }
                  GC.Collect();
              }
          }
  
          private void SaveSizeInfo(ISampleGrabber sampGrabber)
          {
              // Get the media type from the SampleGrabber
              var media = new AMMediaType();
              int hr = sampGrabber.GetConnectedMediaType(media);
              DsError.ThrowExceptionForHR(hr);
  
              if ((media.formatType != FormatType.VideoInfo) || (media.formatPtr == IntPtr.Zero))
              {
                  throw new NotSupportedException("Unknown Grabber Media Format");
              }
  
              // Grab the size info
              var videoInfoHeader = (VideoInfoHeader)Marshal.PtrToStructure(media.formatPtr, typeof(VideoInfoHeader));
              _width = videoInfoHeader.BmiHeader.Width;
              _height = videoInfoHeader.BmiHeader.Height;
  
              DsUtils.FreeAMMediaType(media);
              GC.Collect();
          }
  
          private void ConfigureSampleGrabber(ISampleGrabber sampGrabber)
          {
              var media = new AMMediaType
              {
                  majorType = MediaType.Video,
                  subType = MediaSubType.RGB24,
                  formatType = FormatType.VideoInfo
              };
              int hr = sampGrabber.SetMediaType(media);
              DsError.ThrowExceptionForHR(hr);
  
              DsUtils.FreeAMMediaType(media);
              GC.Collect();
              hr = sampGrabber.SetCallback(this, 1);
              DsError.ThrowExceptionForHR(hr);
          }
  
          private void CloseInterfaces()
          {
              try
              {
                  if (_mediaCtrl != null)
                  {
                      _mediaCtrl.Stop();
                      _mediaCtrl = null;
                  }
              }
              catch (Exception ex)
              {
                  Debug.WriteLine(ex);
              }
  
              if (_filterGraph != null)
              {
                  Marshal.ReleaseComObject(_filterGraph);
                  _filterGraph = null;
              }
              GC.Collect();
          }
  
          int ISampleGrabberCB.SampleCB(double sampleTime, IMediaSample pSample)
          {
              Marshal.ReleaseComObject(pSample);
              return 0;
          }
  
          //add a boolean property to indicate the save-mode
          public bool SaveToDisc { get; set; }
          //the list for the bitmaps
          public List&lt;Bitmap&gt; Images { get; set; }
  
          int ISampleGrabberCB.BufferCB(double sampleTime, IntPtr pBuffer, int bufferLen)
          {
              using (var bitmap = new Bitmap(_width, _height, _width * 3, PixelFormat.Format24bppRgb, pBuffer))
              {
                  bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
                  if (SaveToDisc)
                  {
                      String tempFile = _outFolder + _frameId + ".bmp";
                      if (File.Exists(tempFile))
                      {
  
                      }
                      else
                      {
                          bitmap.Save(Path.Combine(_outFolder, _frameId.ToString("D6") + ".bmp"));
  
                      }
                      _frameId++;
                  }
                  else
                  {
                      if (Images == null)
                          Images = new List&lt;Bitmap&gt;();
                      Images.Add((Bitmap)bitmap.Clone());
                  }
              }
              return 0;
          }
  
  
      }
  }
  </code></pre>
  
  <p>The class is exctracting frames from video file to the hard disk.</p>
  
  <p>IN my case for example 47 files on hard disk.</p>
  
  <p>In the bottom of the class im doing the saving to the hard disk:</p>
  
  <pre><code>bitmap.Save(Path.Combine(_outFolder, _frameId.ToString("D6") + ".bmp"));
  </code></pre>
  
  <p>And above in the class there a function called: WaitUntillDone() wich dosent help me.</p>
  
  <p>What i want is that when its finishing to extract all the files to the hard disk it will give me a messagebox.show or just a message on a label or something say that " the process has done"</p>
  
  <p>In Form1 button click event im using the class like this:</p>
  
  <pre><code>wmv = new Polkan.DataSource.WmvAdapter(@"d:\VIDEO0040.3gp", sf);
              wmv.SaveToDisc = true;
              wmv.Start();
  </code></pre>
  
  <p>The video name , sf is the directory where to extract on hard disk.</p>
  
  <p>Then true so it will save it to the hard disk not ot memory the frames.</p>
  
  <p>And start.</p>
  
  <p>I can also do in Form1 in the button click event wmv.WaitUntillDone(); but it wont throw any message or something when done.</p>