Application.DoEvents() triggers exception!

J

james.jdunne

System.ArgumentException: Item has already been added. Key in
dictionary: "-1" Key being added: "-1"
at System.Collections.Hashtable.Insert(Object key, Object nvalue,
Boolean add)
at System.Collections.Hashtable.Add(Object key, Object value)
at
System.Windows.Forms.ComponentManager.System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FRegisterComponent(IMsoComponent
component, MSOCRINFOSTRUCT pcrinfo, Int32& dwComponentID)
at System.Windows.Forms.ThreadContext.get_ComponentManager()
at System.Windows.Forms.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason,
ApplicationContext context)
at System.Windows.Forms.Application.DoEvents()

I'm working with the Windows Media Encoder object library, needing to
call Application.DoEvents() to process the message pump to be notified
when an asynchronous encoding job completes. Unfortunately I cannot
reproduce the problem in a small complete example because it requires
the Windows Media Encoder SDK, but I will post my code nonetheless.

What I can tell you is that I'm running a two-threaded Windows.Forms
application that invokes the WMEncoder.Transcode() call from the
non-GUI thread. Also, I'm writing out a lot of output to a ListBox,
which could have been overfilled beyond 64K items. The non-GUI thread
properly calls back to the GUI thread via the BeginInvoke() procedure.

The application is basically a batch WMA file transcoder that processes
thousands of files during one instance of the application.

The following is the complete code for my transcoder wrapper class.
The exception is generated from the call to Application.DoEvents() in
the while (!done) loop.

using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace WMEncoderWrapper {
/// <summary>
/// Summary description for WMEncoder.
/// </summary>
public class WMEncoder {
private const int WMENC_CONTENT_ONE_AUDIO = 1;
private const int
WMA9STD_FOURCC = 353,
WMA9PRO_FOURCC = 354,
WMA9LSL_FOURCC = 355,
WMSPEECH_FOURCC = 10,
PCM_FOURCC = 0;

private bool done = false;

public WMEncoder() {
}

/// <summary>
/// Handles an encoder state change
/// </summary>
/// <param name="state"></param>
private void OnStateChange(WMEncoderLib.WMENC_ENCODER_STATE state) {
if (state == WMEncoderLib.WMENC_ENCODER_STATE.WMENC_ENCODER_STOPPED)
done = true;
}

/// <summary>
/// Transcode the input audio file (MP3, WMA, etc.) to a WMA9STD file
at 64kbps
/// </summary>
/// <param name="inputFile"></param>
/// <param name="outputFile"></param>
public void Transcode(string inputFile, string outputFile) {
WMEncoderLib.WMEncoder enc = null;
WMEncoderLib.IWMEncSourceGroup srcGroup = null;
WMEncoderLib.IWMEncSource src = null;
WMEncoderLib.IWMEncProfile2 profile = null;
WMEncoderLib.IWMEncAudienceObj audience = null;

try {
done = false;

// Create encoder:
enc = new WMEncoderLib.WMEncoderClass();
enc.OnStateChange += new
WMEncoderLib._IWMEncoderEvents_OnStateChangeEventHandler(OnStateChange);

profile = new WMEncoderLib.WMEncProfile2Class();
profile.ContentType = WMENC_CONTENT_ONE_AUDIO;
profile.ProfileName = "Profile1";
audience = profile.AddAudience(10000000);

// Set the encoding codec to WMA9STD with 1-pass CBR encoding:
int idxCodec =
profile.GetCodecIndexFromFourCC(WMEncoderLib.WMENC_SOURCE_TYPE.WMENC_AUDIO,
WMA9STD_FOURCC);
//profile.EnumAudioCodec(idxCodec, out codecName);
audience.set_AudioCodec(0, idxCodec);

// 2 channels, 44kHz sampling rate, 64,040bps bitrate, 16-bit
samples
audience.SetAudioConfig(0, 2, 44100, 64040, 16);

// Create a source group:
srcGroup = enc.SourceGroupCollection.Add("SG_1");
srcGroup.set_Profile(profile);

// Set the input file:
src =
srcGroup.AddSource(WMEncoderLib.WMENC_SOURCE_TYPE.WMENC_AUDIO);
src.SetInput(inputFile, String.Empty, String.Empty);

// Set the output file:
enc.File.LocalFileName = outputFile;

// Transcode:
enc.AutoStop = true;
enc.PrepareToEncode(true);
enc.Start();

// Wait until encoding stops:
while (!done) {
Application.DoEvents();

// Yield to other threads.
System.Threading.Thread.Sleep(0);
}
enc.Stop();
} finally {
if (enc != null) Marshal.ReleaseComObject(enc);
if (srcGroup != null) Marshal.ReleaseComObject(srcGroup);
if (src != null) Marshal.ReleaseComObject(src);
if (profile != null) Marshal.ReleaseComObject(profile);
if (audience != null) Marshal.ReleaseComObject(audience);
}
}
}
}

I've seen others in this newsgroup post about similar problems but with
no resolve on the issue. Can someone investigate this problem?
 
D

David Browne

System.ArgumentException: Item has already been added. Key in
dictionary: "-1" Key being added: "-1"
at System.Collections.Hashtable.Insert(Object key, Object nvalue,
Boolean add)
at System.Collections.Hashtable.Add(Object key, Object value)
at
System.Windows.Forms.ComponentManager.System.Windows.Forms.UnsafeNativeMethods+IMsoComponentManager.FRegisterComponent(IMsoComponent
component, MSOCRINFOSTRUCT pcrinfo, Int32& dwComponentID)
at System.Windows.Forms.ThreadContext.get_ComponentManager()
at System.Windows.Forms.ThreadContext.RunMessageLoopInner(Int32
reason, ApplicationContext context)
at System.Windows.Forms.ThreadContext.RunMessageLoop(Int32 reason,
ApplicationContext context)
at System.Windows.Forms.Application.DoEvents()

I'm working with the Windows Media Encoder object library, needing to
call Application.DoEvents() to process the message pump to be notified
when an asynchronous encoding job completes. Unfortunately I cannot
reproduce the problem in a small complete example because it requires
the Windows Media Encoder SDK, but I will post my code nonetheless.

What I can tell you is that I'm running a two-threaded Windows.Forms
application that invokes the WMEncoder.Transcode() call from the
non-GUI thread. . . .

If Transcode is called from a background thread, why is it calling DoEvents
at all? If you need to wait for the job to complete, the Thread.Sleep
should do it. But don't sleep(0), or that thread will constantly get
scheduled: Sleep(100) at least.

David
 
S

Samuel R. Neff

Since you get event notification when the transcoding is complete you
don't need to poll for done. Just move the local variables to
instance variables, end Transcode() after Start() and then do the
finally stuff when the done event comes in.

HTH,

Sam
 
J

james.jdunne

David said:
If Transcode is called from a background thread, why is it calling DoEvents
at all? If you need to wait for the job to complete, the Thread.Sleep
should do it. But don't sleep(0), or that thread will constantly get
scheduled: Sleep(100) at least.

David

The Windows Media Encoder library's callback mechanism does not work
unless the Windows message pump is running. I've tried without the
DoEvents call and it does not ever fire the event, even after I know
the transcoding is done (watching the filesystem).

Besides, the real problem here is why DoEvents is throwing an exception
about Hashtable.
 
J

james.jdunne

Samuel said:
Since you get event notification when the transcoding is complete you
don't need to poll for done. Just move the local variables to
instance variables, end Transcode() after Start() and then do the
finally stuff when the done event comes in.

HTH,

Sam


------------------------------------------------------------
We're hiring! B-Line Medical is seeking Mid/Sr. .NET
Developers for exciting positions in medical product
development in MD/DC. Work with a variety of technologies
in a relaxed team environment. See ads on Dice.com.

I would do that if I wanted my Transcode() function to be asynchronous,
but I don't want that. I want it as part of a batch process and I want
the batch to run sequentially so I get meaningful log output.
 
S

Samuel R. Neff

If you don't want Transcode() to be async then don't use a separate
thread. Run the Transcode on the main message thread (although this
is very unusual in normal WinForm programming methodology--background
processes normally don't run on the UI thread and normally don't
require the message pump).

Sam


------------------------------------------------------------
We're hiring! B-Line Medical is seeking Mid/Sr. .NET
Developers for exciting positions in medical product
development in MD/DC. Work with a variety of technologies
in a relaxed team environment. See ads on Dice.com.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top