Thursday, March 30, 2006

I have updated ITAPI3 to fix a couple of reported bugs -- the TE_FILETERMINAL event wasn't always being raised and the TCall.GenerateCustomTone didn't work properly.  Both of these issues are fixed in the latest drop.  Enjoy!

posted on 3/30/2006 5:16:09 PM (Central Standard Time, UTC-06:00)  #   
 Friday, March 24, 2006

Great news!  Rotor is an incredible tool for learning about how and why the CLR works the way it does.

For more info -- http://blogs.msdn.com/jasonz/archive/2006/03/23/559581.aspx

 

posted on 3/24/2006 8:38:31 AM (Central Standard Time, UTC-06:00)  #   
 Wednesday, March 22, 2006

A few months ago, I ran into a really weird bug with the System.Net.NetworkInformation.Ping class.  I was using it to monitor the network connectivity to a server and as my program ran, it appeared to leak handles.  I was calling Dispose (as I should) but it was still appeared to be leaking handles when called over and over. 

Here's the basic code I was running (simplified for this example):

Ping icmp = new Ping();
icmp.PingCompleted += delegate(object sender, PingCompletedEventArgs e)
   {
      PingReply reply = e.Reply;
      Console.WriteLine("Address: {0} - {1}", url, reply.RoundtripTime);
      icmp.Dispose();
  // Get rid of resources
   };

icmp.SendAsync(url, 100);

// Continue doing work

The code looked ok to me, so I started looking a little deeper.  In reality, I found that if I waited long enough, the GC process was cleaning up the unmanaged handles using the SafeHandle class, but I was confused because Dispose should have done that for me. When I looked at the ping class using Reflector, the problem became obvious and it's a warning to anyone building components that need to free up non-memory related resources.

In order to integrate with the Windows Forms and ASP.NET designer, the Ping component extends System.ComponentModel.Component.  This provides the design-time integration with Visual Studio .NET and it also provides some basic plumbing that used to cleanup the component - specifically it implements IDisposable for you and provides a nice virtual Dispose method which you are supposed to override and free your resources.  The code follows Microsoft's IDisposable pattern exactly - providing the IDisposable.Dispose method which delegates to an internal virtual void Dispose(bool isDisposing) method which is the method you should override.

The Ping component uses an internal socket and the ICMP W2K support under the covers to do its work and this socket needs to be cleaned up.  So, the author implemented IDisposable to indicate this -

public class Ping : Component, IDisposable
{
   private void InternalDispose()
   {
      if (!disposed)
      {
         // Cleanup socket and/or ICMP handle resources..
      }
   }

   IDisposable.Dispose()
   {
      this.InternalDispose();
   }
}

Note how the author used an explicit interface implementation for the Dispose method.  This means we will need to cast the object to an IDisposable interface in order to call the method (something I'm not doing above).  In fact, it won't even show up as a callable method inside VS.NET.  There is nothing wrong with this implementation (except of course it doesn't follow Microsoft's guidelines) until you add in the derivation from Component.  If we add it's implementation into the mix, and expand it out I get something like:

public class Ping : Component, IDisposable
{
   private void InternalDispose()
   {
      if (!disposed)
      {
         // Cleanup socket and/or ICMP handle resources..
      }
   }

   IDisposable.Dispose()
   {
      this.InternalDispose();
   }

   public void Dispose()
   {
      this.Dispose(true);
      GC.SupressFinalize(this);
   }

   protected virtual void Dispose(bool disposing)
   {
         // Remove object from component container
   }
}

See the problem?  I now have a public Dispose method available directly - and intellisense will show that.  This is the one I was calling my code when my async call was finished.  The problem is that it wasn't actually disposing the unmanaged resources - it was running the Component.Dispose code.

I need to back up and make another point on this.  I wouldn't have even seen the problem if I had been doing things synchronously.  In that case, I would have likely doing a using statement:

using (Ping icmp = new Ping())
{
   PingReply reply = icmp.Send(url, 100);
   if (reply.Status == IPStatus.Success)
   {
      Console.WriteLine("Address: {0} - {1}", url, reply.RoundtripTime);
   }
}

In this case, the C# compiler is nice enough to dispose of the object for me - and it does it by casting the object to IDisposable.  So, the generated code would really look like:

Ping icmp = new Ping();
try
{
   PingReply reply = icmp.Send(url, 100);
   if (reply.Status == IPStatus.Success)
   {
      Console.WriteLine("Address: {0} - {1}", url, reply.RoundtripTime);
   }
}
finally
{
   ((IDisposable)icmp).Dispose();
}

So, this would end up calling the correct implementation.  It was only because I was calling Dispose directly that I had a problem.  If the author had followed the IDisposable guidelines, this problem would have been found immediately because the C# compiler would have spit out a warning that public void Dispose() is hiding a base class implementation - cluing the author in that they need to hook into the base class implementation.  So, how did this happen?  My guess is that originally the Ping class didn't extend Component.  That derivation was added later in order to provide for design-time support.

If you are building components yourself, don't fall into this trap!  Always, always, always use Microsoft's stated guidelines - here's a simple example for those not familiar with it:

public class MyResource: IDisposable
{
   // Track whether Dispose has been called.
   protected bool disposed = false;

   // Implement IDisposable.
   // Do not make this method virtual.
   // A derived class should not be able to override this method.
   public void Dispose()
   {
      Dispose(true);
      // This object will be cleaned up by the Dispose method.
      // Therefore, you should call GC.SupressFinalize to
      // take this object off the finalization queue
      // and prevent finalization code for this object
      // from executing a second time.
      GC.SuppressFinalize(this);
   }

   // Dispose(bool disposing) executes in two distinct scenarios.
   // If disposing equals true, the method has been called directly
   // or indirectly by a user's code. Managed and unmanaged resources
   // can be disposed.
   // If disposing equals false, the method has been called by the
   // runtime from inside the finalizer and you should not reference
   // other objects. Only unmanaged resources can be disposed.
   protected virtual void Dispose(bool disposing)
   {
      // Check to see if Dispose has already been called.
      if(!this.disposed)
      {
         // If disposing equals true, dispose all managed
         // and unmanaged resources.
         if(disposing)
         {
           // Dispose all managed resources here.
         }
        
         // Call the appropriate methods to clean up
         // unmanaged resources here.
      }
      disposed = true;
   }
}

There's a bunch more information on this topic is section 9.3 of the Framework Design Guidelines - a must read for anyone building class libraries or reusable components.


 

posted on 3/22/2006 1:22:23 PM (Central Standard Time, UTC-06:00)  #   
 Monday, March 20, 2006

One issue that's always a struggle with building reusable components is managing asynchronous operations.  The problem is that depending on the type of application that is going to use the component, the thread used for callbacks and events may or may not be important.  For example, with Console based applications, callbacks on different threads might be ok - at least as long as the application itself ensures thread safety.  But, with a Windows Forms application, threading is critical - you are not allowed to touch UI controls from any thread other than the main one and so we end up with the Control.BeginInvoke logic in each of our callbacks which sucks.

Enter the Synchronization Context - a new feature of .NET 2.0.  Here's how it works:

You derive your component from System.ComponentModel.Component.Component and provide your typical asynchronous function -- in this example, we will build a PiCalculator:

public class PiCalculator : Component
{
   public PiCalculator();
   public object CalculatePi(int digits, object stateData);
   public void CancelAsync(object asyncTask);
}

Here, what we've done is create a new component with a default constructor and a method called CalculatePi which takes the number of digits, an optional piece of state data and returns an object.  With this component, I'd like to have multiple outstanding asynchronous operations and so I need some way to track each one to identify it when it completes, and also to cancel it if it runs too long.

Microsoft's general pattern for this is to use the stateData parameter and have the application pass in some unique value to identify the request. 

This is an ok way to do it, but it puts several restrictions on our implementation:

1) The state data must be supplied
2) The state data must be unique for each operation

In addition, there's the possibility of a race condition if I re-use the state data after canceling an operation.  So, to avoid all of these issues, I have the component return the task identifier as part of the request - this is the object return value coming back from CalculatePi.  I can then use that object to identify the results, and I can pass it into the component's CancelAsync method to cancel a pending request.

So, how do I get my results?  Through a delegate callback of course!  I need to create a delegate signature that uses it.  The general EventHandler callback signature is: void Method(object sender, EventArgs e) so, we'll use a derivative of this as our delegate:

public delegate void PiCalculationCompletedEventHandler(object sender, PiCalculationEventArgs e);

Then we'll create the class which will be used to report the results.  The class is pretty simple - just a data holder really:

public class PiCalculationEventArgs : EventArgs
{
   private int _digits;
   private string _value;
   private bool _canceled;
   private object _stateData;
   private object _taskId;

   public object TaskId
   {
      get { return _taskId; }
   }

   public object State
   {
      get { return _stateData; }
   }

   public bool Canceled
   {
      get { return _canceled; }
   }

   public int Digits
   {
      
get { return _digits; }
   
}

   public string Result
   {
      get { return _value; }
   }

   internal PiCalculationEventArgs(object taskId, int digits, string value, object stateData, bool canceled)
   {
      _digits = digits;
      _value = value;
      _canceled = canceled;
      _stateData = stateData;
      _taskId = taskId;
   }
}

Now we can implement out component.  First, we'll add a Completed event:

public event PiCalculationCompletedEventHandler CalculationComplete;

This will be used by the application to hook into the results of our component's calculation.  The next part is the key to all of this - AsyncOperation.  The AsyncOperation class is new to 2.0 and provides the facility to perform callbacks on the appropriate thread.  Essentially, it acts as a callback mediator - detecting the type of thread it was created on and then providing the appropriate marshaling code for it's internal delegate.  You utilize the callback through a new delegate type -- SendOrPostCallback.  We tie our callback to this delegate type and it will marshal us to the correct threading model.  We can then execute our own internal CalculationComplete event.  Here's the basic skeleton:  First, we will create an internal contained class which will be used to track the request.  This will actually be the object type we return from the CalculatePi method:

class AsyncStateData
{
   public AsyncOperation asyncOperation;
   public volatile bool canceled = false;
   public volatile bool running = true;

   public AsyncStateData(object stateData) 
   { 
      asyncOperation =
AsyncOperationManager.CreateOperation(stateData); 
   }
}

Notice the call to AsyncOperationManager.CreateOperation?  This is where we create our AsyncOperation class and this factory creator is the context detector.  We will have an AsyncOperation for each event we intend to fire back into the client - in our case one for each Pi calculation.  But, I could also create a single instance for the client as well - this is actually what is done in the ITapi3 component to allow it to be integrated onto a Windows Form even though events are being received on a background thread.  The Tapi events are always fired on the appropriate thread - the background one for non-WinForms apps and the UI thread for WinForms apps.

Next, we will create our internal callback - this is the callback that will actually be fired internally and then call the real application event, so we'll hook that up with an anonymous delegate in the constructor of our component:

private SendOrPostCallback completionMethodDelegate;

public PiCalculator()
{
   completionMethodDelegate =
delegate(object evt)
   {
      
// Called on the synchronization thread.
      
if (CalculationComplete != null)
         CalculationComplete(
this, (PiCalculationEventArgs)evt);
   };
}

Now, let's implement our CalculatePi method - it's pretty simple, we'll use an asynch delegate to our internal calculator, invoke it and return the AsyncStateData structure we create to identify each task submitted to the component.  Then the calculation will be performed on a thread pool thread and we'll callback to the client when it is finished.

public object CalculatePi(int digits, object stateData)
{
   PiDelegate piDel = InternalCalculatePi;
   
AsyncStateData asyncData = new AsyncStateData(stateData);
   piDel.BeginInvoke(digits, asyncData,
delegate(IAsyncResult ar) { piDel.EndInvoke(ar); }, null);
   
return asyncData;
}

CancelAsync is pretty simple too -- it will simply set the Canceled flag of the request:

public void CancelAsync(object asyncTask)
{
   
AsyncStateData asyncData = asyncTask as AsyncStateData;
   
if (asyncData != null && asyncData.running == true)
      asyncData.canceled =
true;
}

Now, we need to implement our PiCalculator.  We're going to cheese out here and just "pretend" to calculate Pi since that wasn't really the point of this component :-)  We'll define a delegate to use for the asynch execution and then implement our function:

private delegate void PiDelegate(int digits, AsyncStateData asyncData);
private void InternalCalculatePi(int digits, AsyncStateData asyncData)
{
   string PI_DIGITS = "3.141592637309238932482438234724782347234";
   
if (digits > PI_DIGITS.Length - 2)
      digits = PI_DIGITS.Length - 2;

   // This would be a real calculator here..
   
int completedDigits = 0;
   
for (; !asyncData.canceled && completedDigits < digits; completedDigits++)
   {
      
Thread.Sleep(1000);
   }

   asyncData.running = false;
   
string data = PI_DIGITS.Substring(0, completedDigits + 2);
   asyncData.asyncOperation.PostOperationCompleted(
      
completionMethodDelegate,
      
new PiCalculationEventArgs(asyncData, digits, data, asyncData.asyncOperation.UserSuppliedState, asyncData.canceled));
}

The key to the callback is the invocation of the PostOperationCompleted method from the AsyncOperation instance we created.  It is what calls our handler which will in turn call the client.  Once PostOperationCompleted is called, no further work may be done with the AsyncOperation.  So, it's not appropriate for progress reporting - instead you can use PostOperateration for that which allows for multiple calls. 

Now, when using this component, we can simply call it as expected:

static void Main(string[] args)
{
   ArrayList taskIds = new ArrayList();
   
   PiCalculator piCalc = new PiCalculator();
   piCalc.CalculationComplete +=
delegate(object sender, PiCalculationEventArgs e)
   {
      Console.WriteLine("[{0}] {1} = {2}, Canceled = {3}"
         
Thread.CurrentThread.ManagedThreadId, e.Digits, e.Result, e.Canceled);
   }         

   
foreach (string s in args)
   {
      taskIds.Add(piCalc.CalculatePi(
Convert.ToInt32(s), s));
   }

   Console.WriteLine("Waiting for results .. press ENTER to cancel.");
   
Console.ReadLine();

   foreach (object task in taskIds)
   {
      piCalc.CancelAsync(task);
   }

   Console.WriteLine("Press ENTER to terminate");
   
Console.ReadLine();
}

Here's the output:

Waiting for results .. press ENTER to cancel.
[3] 10 = 3.1415926373, Canceled = False
[4] 11 = 3.14159263730, Canceled = False
[5] 14 = 3.14159263730923, Canceled = False
[5] 15 = 3.141592637309238, Canceled = False
[5] 17 = 3.14159263730923893, Canceled = False

Press ENTER to terminate
[5] 20 = 3.1415926373092389324, Canceled = True

Not real exciting right?  Notice the thread id in the [brackets] is different for some of the callbacks.  This indicates we are getting called back on different threads - certainly not the main thread which is waiting for console input.  The cool part of this component is that I can use it exactly the same way in a Windows Forms application!  So, I don't have to know that the callback is on a different thread!  I don't have to worry about doing a BeginInvoke or Invoke to get back to the UI thread.  So, here's an example WinForm application:

public partial class CalcPiTestForm : Form
{
   ArrayList _pendingTasks = new ArrayList();

   public CalcPiTestForm()
   {
      InitializeComponent();
   }

   private void CalcPiTestForm_Load(object sender, EventArgs e)
   {
      piCalculator1.CalculationComplete += new UserMath.PiCalculationCompletedEventHandler(piCalculator1_CalculationComplete);
   }

   void piCalculator1_CalculationComplete(object sender, UserMath.PiCalculationEventArgs e)
   {
      lock(_pendingTasks)
      {
         _pendingTasks.Remove(e.TaskId);
      }

      // No need to do BeginInvoke here!!
      listBox1.Items.Add(string.Format("[{0}] {1} = {2}, Canceled = {3}",
         System.Threading.Thread.CurrentThread.ManagedThreadId, e.Digits, e.Result, e.Canceled));
   }

   private void btnCalculate_Click(object sender, EventArgs e)
   {
      if (maskedTextBox1.Text.Length > 0)
      {
         lock(_pendingTasks)
            _pendingTasks.Add(piCalculator1.CalculatePi(Convert.ToInt32(maskedTextBox1.Text)));
      }
   }

   private void btnCancel_Click(object sender, EventArgs e)
   {
      lock(_pendingTasks)
      {
         foreach (object task in _pendingTasks)
            piCalculator1.CancelAsync(task);
      }
   }
}

Notice the output here -- we haven't done anything special with the component's callbacks, but now the callback is always on thread [1].  This is the magic of AsyncOperation and synchronization contexts.  Now go out there and write some thread-aware components!  If you'd like this entire sample, here is the project: http://www.julmar.com/samples/asyncop.zip

posted on 3/20/2006 4:42:45 PM (Central Standard Time, UTC-06:00)  #   
 Friday, March 17, 2006

I love anonymous delegates - I think they are extremely useful and allow me to solve some problems in very elegant ways.  However, once you really get into them, you start to see the dark side of anonymous delegates and that is unregistration.

Here's the basic problem: when binding an instance delegate to an event to handle some activity, the delegate will cache off the instance reference - thereby keeping the reference alive.  So for example, if I had a form which wanted to process some activity from an object:

class Publisher
{
   public event EventHandler OnEvent;
   ...
}

class MainForm : Form
{
   Publisher _pub = new Publisher();

   void ActivateChildForm() 
   {
      ChildForm f = new ChildForm(_pub);
      f.Show();
   }
}

class ChildForm : Form
{
   public ChildForm(Publisher pub) 
   {
      pub.OnEvent += ProcessEvent;
   }
   void ProcessEvent(object sender, EventArgs e)
   {
      listBox1.Items.Add("Event was fired!");      
   }
}

When the ChildForm instance is closed, the form won't be collected because the Publisher (_pub) is holding a reference to it.  This is easily fixed by adding some code into the FormClosing event:

class ChildForm : Form
{
   Publisher _pub;

   public ChildForm(Publisher pub)
   {
      _pub = pub;
      _pub.OnEvent += ProcessEvent;
   }

   void FormClosing(object sender, FormClosingEventArgs e)
   {
      _pub.OnEvent -= ProcessEvent; 
   }
}

Now, the form will be cleaned up when it's closed.  This is pretty standard stuff, and most people that have been using .NET for a while know all this.  Here's the rub: with .NET 2.0, we can simplify the code using anonymous delegates which are really helpful for these single-line processing event handling functions.  So, I could recode my handler as:

class ChildForm : Form
{
   public ChildForm(Publisher pub) 
   {
      pub.OnEvent += delegate { listBox1.Items.Add("Event was fired!"); }
      this.OnClosing += delegate { pub.OnEvent -= ????? }

   }
}

The issue is that I don't have a reference to the delegate as it's typed.  Under the covers, the C# compiler has generated a temporary function (or possibly even pulled it out to a separate inner class) and there's no way for me to get to the underlying function.  So, what can I do?  Well, the easiest thing to do is to save off the function:

class ChildForm : Form
{
   public ChildForm(Publisher pub) 
   {
      EventHandler eh = delegate { listBox1.Items.Add("Event was fired!"); }
       
      pub.OnEvent += eh;      
     
this.OnClosing += delegate { pub.OnEvent -= eh; }
   }
}

Now my code will function properly -- and is significantly reduced in size.  Of course, I've lost the benefit of being able to hook up events through VS.NET because it always generates seperate functions and I would need to cache off the Publisher instance as well as my delegate in that case. 

So, rule #1, always unregister the event when you are finished.  Rule #2, remember that anonymous delegates may be keep your instance alive so unregister them as well, unless the event is to be hooked up throughout the lifetime of the application.

posted on 3/17/2006 10:56:40 AM (Central Standard Time, UTC-06:00)  #   

I've posted a new version of ATAPI.NET which supports consultation transfers and a simple phone sample that shows off how to use the features.  In addtiion, this version of ATAPI.NET has a couple of bug fixes that were rolled in from a production project over the past couple of weeks to fix some weird startup/shutdown issues when a LINE_REINIT is reported by TAPI.  You can get the new code from our samples link -- http://www.julmar.com/samples.htm

 

posted on 3/17/2006 9:59:23 AM (Central Standard Time, UTC-06:00)  #   
 Wednesday, March 15, 2006

Forwarding lines with ATAPI.NET is simple and easy (assuming, of course, that the underlying TSP supports it).

The first step is knowing whether a given line device even supports forwarding.  This is trivial:

TapiManager mgr = new TapiManager("ForwardingTest");

foreach (TapiLine line in mgr.Lines)
{
   
if
(line.Capabilities.SupportsForwarding)
   {
      Console.WriteLine("Line {0} supports forwarding!"
, line.Name);
   }
}

Once we've identified a specific line, we can look at each address and get more information such as the types of forwarding supported.  For example, we might be able to forward to different numbers based on specific conditions such as whether the call goes unanswered vs. whether the address is in use and returning a busy signal.  We might also be able to forward specific inbound callers (very useful to get rid of your bosses calls).  We can get this information from the Capabilities of the TapiAddress object:

foreach (TapiAddress addr in line.Addresses)
{
   
Console.WriteLine("Forwarding modes supported on {0} are {1}"
, addr.Address, addr.Capabilities.SupportedForwardingModes);
}

We can also retrieve any existing forwarding information through the Status of the TapiAddress:

foreach (ForwardInfo fwd in addr.Status.ForwardingInformatioin)
   
Console.WriteLine("\t{0} to {1}:{2}"
, fwd.ForwardMode, fwd.DestinationAddressType, fwd.DestinationAddress);

This outputs: "Unconditional to PhoneNumber:1234" on a forwarded line I setup.

Finally, the big question is how to change the forwarding information, this is pretty easy as well.  You can set forwarding information on two levels, the entire line (which impacts all addresses), or a specific address.  This is done through two methods present on both TapiAddress and TapiLine which are Forward and CancelForward.  So, to cancel all forwarding in effect on every line we could do the following:

Console.WriteLine("Canceling all forwards:");
foreach (TapiLine line in
mgr.Lines)
{
   
if
(line.Capabilities.SupportsForwarding)
   {
      
try
      
{
         line.CancelForward();
      }
      
catch (TapiException
ex)
      {
         
Console.WriteLine("{0} - {1}"
, line, ex.Message);
      }
   }
}

Or, to setup the forwarding as above, I can issue a call to the Forward method:

ForwardInfo[] fwdInfo = new ForwardInfo[] {
      
new ForwardInfo(ForwardingMode.Unconditional, 0, "1234"
)
};

foreach (TapiLine line in mgr.Lines)
{
   
if
(line.Capabilities.SupportsForwarding)
   {
      
try
      
{
         line.Forward(fwdInfo, 5,
null
);
      }
      
catch (TapiException
ex)
      {
         
Console.WriteLine("{0} - {1}"
, line.Name, ex.Message);
      }
   }
}

The ForwardInfo class describes a single forwarding instruction and you pass an array of these info the Forward method to indicate how things are to be managed.  Exceptions need to be handled because the TAPI service provider might not allow the particular forwarding at this point in time, or the destination might not be allowed, etc.

Under the covers this will issue a lineForward request with a LINEFORWARDLIST setup for each of the ForwardInfo structures.

That about covers it!  Ping me with any questions if you want.

 

posted on 3/15/2006 2:43:58 PM (Central Standard Time, UTC-06:00)  #   
 Monday, March 13, 2006

I got hit with this problem from two separate clients last week - a .NET 1.1 application ported to .NET 2.0 is now terminating abruptly for no apparent reason.  Well, of course there's a reason - and it's that both applications had "hidden" exceptions being thrown in some background thread that weren't being caught.  Under .NET 1.1, the CLR would print any exceptions that occurred on threadpool threads to the console and then return the thread to the pool.  In addition, the CLR would silently eat any exception thrown on the finalizer thread (again printing the stack trace to the console).  Under .NET 2.0, the behavior has changed and it will cause the AppDomain to be unloaded (yikes!).  So, for example:

class BadClass
{
  ~BadClass()
  {
    throw new Exception("I'm Bad"); 
  }
}

class BadClass
{
  static void Main()
  {
     for (; ;)
       
new BadClass();
  }
}

The above code would run forever under CLR 1.1, but will terminate immediately when run under CLR 2.0 with an unhandled exception. 

Unhandled Exception: System.Exception: I'm bad
   at BadClass.Finalize() in W:\Projects\TestApp\TestApp\Program.cs:line 11

There are a couple of ways to deal with this - the best is to do all your testing and close the holes.  You really shouldn't have unhandled exceptions loose in your program.  But for a really large program, or one where you don't know where the exception is happening, Microsoft has given you a couple of options.  The first one is the <legacyUnhandledExceptionPolicy>  tag which you can put into your app.config file:

<configuration
  <
runtime

    <
legacyUnhandledExceptionPolicy enabled="1"
/> 
  <runtime
>
<configuration>

This will essentially revert the CLR to the 1.1 behavior.  Consider it a short-term fix because it will ultimately mask issues with your application that are really bugs.
 
Along with the above, you can also be notified about unhandled exceptions prior to the AppDomain being unloaded.  This is done through several different methods based on the type of application.  The first basic method is through the AppDomain.UnhandledException event which is raised for Console applications:

static
void Main()
{
   
AppDomain.CurrentDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs
e)  
   { 
      
Console.WriteLine("{0} - IsTerminating = {1}"
, e.ExceptionObject.ToString(), e.IsTerminating); 
   
};

   for (; ;)
      
new BadClass
();
}

In this case, the application will still be terminated, but we will now get notified right before termination.  This allows our application to log the error (using the nifty System.Diagnostics.TraceSource support or through the EventLog) so we at least know what happened.

For Windows Forms applications, things are a little different -- instead of the AppDomain.UnhandledException event being raised, the Windows Forms infrastructure will raise the Application.ThreadException event for exceptions that occur on the primary thread.   The default behavior for this handler is to display the friendly System.Windows.Forms.ThreadException dialog:

If the exception occurs in the primary (message pumping) thread, the user will see the above dialog box and be given the choice to terminate the application or not.  The code behind it looks something like:

void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
{
   
try
   
{
      
// Call user override
      
if (this.threadExceptionHandler != null
)
      {
         
this.threadExceptionHandler(Thread.CurrentThread, new ThreadExceptionEventArgs
(e.Exception));
      }
      
else
      
{
         
using (ThreadExceptionDialog excptDlg = new ThreadExceptionDialog
(e.Exception))
         {
            
DialogResult
result = excptDlg.ShowDialog();
            
if (result == DialogResult
.Abort)
               
Application
.Exit();
         }
      }
   }
   
catch
   
{
   }
}

That's great for the main thread, but if the exception occurs in a secondary thread, then the application will still be terminated after raising the AppDomain.UnhandledException event.  So, for non-UI threads, you must still register the unhandled exception handler on the AppDomain, log the failure and then watch your application die.  Having two handlers can be a pain and if you want to have the application terminate on any unhandled exception, you can direct Windows Forms to not catch primary thread exceptions automatically by using the Application.SetUnhandledExceptionBehavior method:

[STAThread]
static void
Main()
{
   Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
   Application.Run(new Form1());
}

The above line will cause exceptions thrown in the main thread to be unhandled - thereby triggering the AppDomain.UnhandledException event.

For ASP.NET applications, things are also a bit different.  First, be aware that any exception thrown during a Page request (the normal Page rendering process) will be handled automatically by ASP.NET and rendered back to the client based on the error settings in web.config.  If I throw an exception while rendering the page, then I'll get an HTML response like:

Server Error in '/TestWebSite' Application.
Bye, Bye!

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

The important thing here is that the AppDomain continues to run.  I can tell ASP.NET to redirect to a specific page through the <customErrors> section of the web.config.  Or, I can catch these exceptions and handle them myself through the global.asax Application_Error method (which is hooking the HttpApplication.Error event):

void Application_Error(object sender, EventArgs e)
{
   Exception
ex = Server.GetLastError();
   Context.ClearError();
  // Stop default error reporting
   
Response.Write(
"Application Error:");
   Response.Write(
"" + Server.HtmlEncode(ex.ToString()) + ""
);
}

A more popular way to display errors is to cache off the last error in a session variable and then execute Server.Transfer to some error.aspx page:

void Application_Error(object sender, EventArgs e)
{
   Exception
ex = Server.GetLastError();
   Session["LastException"] = ex;

   
Server.Transfer("myerrorpage.aspx");

}

Unfortunately, just like Windows Forms, if an unhandled exception occurs on a separate thread (such as a Threadpool thread), or outside the request processing framework, then the AppDomain is terminated.  This has significant consequences for ASP.NET applications - because the website itself is cycled.  This means you have lost session state and your users are going to notice the website outage.  Most of the time, there isn't much information to work off of -- you end up with a cryptic Event Log entry like:

EventType clr20r3, P1 w3wp.exe, P2 6.0.3790.1830, P3 4333d6f1, P4 app_web_5hu0gabf, P5 0.0.0.0, P6 4415a8be, P7 7, P8 a, P9 system.exception, P10 NIL.

In order to get more information, we need to hook the AppDomain.UnhandledException event and then log the information, just like we have for Windows Forms and Console applications.  We could put a handler into our global.asax, but that would only handle that one web application.  It would be better to create a new HTTP Module with the event handler and hook that up through the web.config.  Luckily for me, Microsoft has already published a KB article which shows off this exact solution - http://support.microsoft.com/?id=911816.  In this article, Microsoft details creating a new HttpModule class which will log the exception information to the event log so you will know exactly where the exception occurred and can provide your own exception handling.  Unfortunately, without the above <legacyUnhandledExceptionPolicy> entry, the website will still be cycled.

Bottom line is: be aware of this new, breaking change -- look for places where you might not be handling exceptions properly and implement good audit trail mechanisms to log failures when/if they occur.  This will allow you to find and fix issues before they become production problems as you port your legacy code over to .NET 2.0!

posted on 3/13/2006 12:07:55 PM (Central Standard Time, UTC-06:00)  #   
 Tuesday, March 07, 2006

So, a question was asked "How do I determine what's happening in the TAPI3 wrapper"?  The answer is you turn on the internal trace source -- ITapi3 was built with a build in tracing facility to tell you when it had any underlying interface or COM failures and it's easy to activate.  First, add an Application Configuration File to your project.  Open that file and add the following lines:

<?xml version="1.0" encoding="utf-8" ?>
<
configuration>
   <
system.diagnostics>
      <
sources>
         <
source name="ITapiTrace" switchName="tapiSwitch" switchType="System.Diagnostics.SourceSwitch">
            <
listeners>
               
<add name="MyTraceLog" type="System.Diagnostics.TextWriterTraceListener" initializeData="MyTrace.txt" />
            </
listeners>
         </
source>
      </
sources>
      <
switches>
         <
add name="tapiSwitch" value="All" />
      </
switches>
   </
system.diagnostics>
</
configuration>

This will create a file called "MyTrace.txt" in your working directory.  The important line is the source tag which identifies the internal TraceSource object used by the ITapi3 library.  Inside this file will be the internal TAPI3 calls being made for your application.  As an example, the following trace shows me that several underlying COM errors occurred in the running of a simple TAPI3 application -- it was unable to retrieve the ITTerminal interface from the ITAddressEvent interface (which actually isn't really an error), failed to open the line (because Unimodem won't allow the media type VOICE to be passed for my modem), and failed to set the play list for this MSP -- [0x80040216] is actually a DirectShow error [VFW_E_NOT_FOUND].

ITapiTrace Verbose: 0 : Creating ITTAPI instance
ITapiTrace Verbose: 0 : Hooking up connection sink to ITTAPI interface
ITapiTrace Information: 0 : ITTapi::put_EventFilter(0x8001F) hr=0x0
ITapiTrace Error: 0 : COM Hresult 0x80040004 "The MEDIATYPE passed in to this method was invalid." generated 
   at JulMar.Tapi3.TapiException.ThrowExceptionForHR(Int32 hr)
   at JulMar.Tapi3.TTapi.RegisterCallNotifications(ITAddress* pitf, Int16 vbMonitor, Int16 vbOwner, Int32 supportedMediaTypes)
   at JulMar.Tapi3.TAddress.Open(TAPIMEDIATYPES supportedMediaTypes)
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 5, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 4, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 3, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 2, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 1, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 0, Terminal=
ITapiTrace Verbose: 0 : Processing TapiCallNotificationEventArgs: Event=CNE_OWNER, Call=TCall: 171360625 CS_OFFERING
ITapiTrace Verbose: 0 : Processing TapiCallStateEventArgs: Call=TCall: 171360625 CS_OFFERING, State=CS_OFFERING, Cause=CEC_NONE
ITapiTrace Error: 0 : COM Hresult 0x80040216 "" generated 
   at JulMar.Tapi3.TapiException.ThrowExceptionForHR(Int32 hr)
   at JulMar.Tapi3.TTerminal.set_MediaPlayList(String[] fileList)
   at AnsMachine.AutoAttendantForm.AnswerCall()
   at AnsMachine.AutoAttendantForm.OnCallState(Object sender, TapiCallStateEventArgs e)
ITapiTrace Verbose: 0 : Processing TapiCallStateEventArgs: Call=TCall: 171360625 CS_DISCONNECTED, State=CS_DISCONNECTED, Cause=CEC_DISCONNECT_NORMAL
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 5, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 4, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 3, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 2, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 1, Terminal=
ITapiTrace Error: 0 : ITAddressEvent::get_Terminal failed hr=0x80040055
ITapiTrace Verbose: 0 : Processing TapiAddressChangedEventArgs: Evt=AE_RINGING, Address=DSSP Line #1 - Address 0, Terminal=
ITapiTrace Error: 0 : ITTapi::Shutdown hr=0x0

 

posted on 3/7/2006 5:18:50 PM (Central Standard Time, UTC-06:00)  #   

Matthias Moetje (Tapi MVP) has graciously taken one of the VB6 Platform SDK samples and ported it to the ITapi3 library and allowed me to distribute it in the Tapi3 Wrapper sample set.  It allows you to select an address, place a call on it and then monitor or generate digits.  Great sample - thanks Matthias!

posted on 3/7/2006 5:04:47 PM (Central Standard Time, UTC-06:00)  #   
 Monday, March 06, 2006

I got an email question today asking about how to write a .WAV file out to an active call using our TAPI3 wrapper.  It's actually pretty easy to do and it follows along with the normal SDK method used in C++.  Here's the relevant code (for the full example, download the http://www.julmar.com/samples/tapi3wrapper.zip and look at the AutoAttendant sample).

First, when a new offering call shows up, get hold of the file playback terminal for it and set it up to play your .WAV files.  We cannot play the files until the call is answered, but this will essentially "queue" it up.

// Method called when TE_CALLSTATE == OFFERING raised
private void AnswerCall()
{
   // Get the playback terminal from the call
   try
   {
      playbackTerminal = activeCall.RequestTerminal(TTerminal.FilePlaybackTerminal, 
                  TAPIMEDIATYPES.AUDIO, TERMINAL_DIRECTION.TD_CAPTURE);
      if (playbackTerminal != null)
      {
         playbackTerminal.MediaPlayList = new string[] { "Hello.wav" };
         activeCall.SelectTerminalOnCall(playbackTerminal);
         activeCall.Answer();
      }
      else
      {
         MessageBox.Show("Failed to retrieve playback terminal.");
         activeCall.Disconnect(DISCONNECT_CODE.DC_REJECTED);
      }
   }
   catch (TapiException ex)
   {
   }
}

Next, watch for the call media to change indicating we have an active stream for our terminal.  When that happens, start the playback stream:

// Method called when TE_CALLMEDIA is raised
private void OnCallMedia(object sender, TapiCallMediaEventArgs e)
{
   try 
   {
      if (activeCall != null && e.Event == CALL_MEDIA_EVENT.CME_STREAM_ACTIVE &&
            e.Terminal.Direction == TERMINAL_DIRECTION.TD_CAPTURE &&
            playbackTerminal != null)
      {
         playbackTerminal.Start();
         SetStatusMessage("File Playback Terminal started ");
      }
   }
   catch (TapiException ex)
   {
   }
}

Finally, when the file terminal is finished, close and dispose the stream.  This is done just to cleanup the resources properly:

// Method called when TE_FILETERMINAL is raised
private
void OnFileTerminal(object sender, TapiFileTerminalEventArgs e)
{
   // We are interested in TMS_IDLE because we will un-select playback and 
   // select recording
   if (e.State == TERMINAL_MEDIA_STATE.TMS_IDLE)
   {