Scott Hanselman

Yes, it WILL run differently under the Debugger

March 16, 2005 Comment on this post [1] Posted in ASP.NET | Web Services | Bugs
Sponsored By

It seems like an obvious thing to say, but I know that *I* totally forgot it. Running under the Debugger (not just when compiled in Debug Mode, but literally during Interactive Debugging) is a totally different universe than running 'normally.'

Recently my buddy Adam Cogan forwarded me an interesting thread that was going on at his company.

For an internal application, Mark Liu noticed when he called a Web Service in response to a WinForms event, say Key Down, but when that even had a 'partner event' on the way, like Key Up, that the Key Up would occur immediately, without waiting for the Synchronously-called Web Service to return.

He might see something like this in his call stack. Red is the event he expected to come later, and Purple is the Web Services call he was counting on blocking.

[<Non-user Code>]  
[<Non-user Code>]  
[<Non-user Code>]  

[<Non-user Code>]  
[<Non-user Code>]  

Without arguing the merits of the desired ordering of events, it appeared to everyone involved that the hidden "this.Invoke" that was doing the Web Services call was releasing control to the calling thread and allowing the KeyUp event to show up immediately.

I said I know that SoapHttpClientProtocol wouldn't be doing such evil since it's likely just calling BeginInvoke itself and waiting on the response. My less than useful answer was "don't do that." I figured that the UI thread wasn't going to block, but I was wrong. We all went back and forth and there was some scratching and biting. I only cried once.

Then George Doubinski from SolutionsNet said (wisely):

I think the debate somewhat degraded and everyone has forgotten the basics – get the simplest test case. I was very suspicious about this event processing business in a first place. So I whipped a bit of code of my own. Guess what – the thread blocks as it should and there is no funny KeyUp business (not that I would personally hook anything to KeyUp anyway). Did a bit of digging with Reflector and confirmed that Invoke in the end calls BeginWebResponse which is exactly what BeginInvoke calls (the difference is the first one does not provide a callback and waits on event explicitly). Started debugging – oops – got the described behaviour. Eureka!

Ah yes, back to basics. The VS.NET Stack Window has a context menu item called "Show Non-User Code." When he did this, the newly illuminated call stack showed:

WindowsApplication1.exe!WindowsApplication1.Form1.Form1_KeyUp(System.Object sender = {WindowsApplication1.Form1}, System.Windows.Forms.KeyEventArgs e = {KeyData=Return}) Line 168        C#
<snip>!ControlNativeWindow.OnMessage(System.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0x13 bytes!ControlNativeWindow.WndProc(System.Windows.Forms.Message m = {System.Windows.Forms.Message}) + 0xda bytes!System.Windows.Forms.NativeWindow.
  DebuggableCallback(int hWnd = 1445384, int msg = 257, int wparam = 13, int lparam = -1071906815) + 0x3d bytes!System.Web.Services.UnsafeNativeMethods.OnSyncCallOut
  (System.Web.Services.Interop.INotifySink2 sink = {System.__ComObject}, System.Web.Services.Interop.CallId callId = {System.Web.Services.Interop.CallId}, int out_ppBuffer = 0, int inout_pBufferSize = 0) + 0x37 bytes!System.Web.Services.Protocols.WebClientProtocol.GetWebRequest
  (System.Uri uri = {System.Uri}) + 0xdd bytes        
(string methodName = "listCountries", System.Object[] parameters = {Length=0}, System.AsyncCallback callback = <undefined value>, System.Object asyncState = <undefined value>) + 0x53 bytes    

The interesting stuff is of course the method DebuggableCallback appearing in the call stack. This is our first hint that something different is happening at Debug Time.

Here is an interesting snippet from GetWebRequest:

if (RemoteDebugger.IsClientCallOutEnabled())
   this.debugger = new RemoteDebugger();
   return request1;

Ah. What's IsClientCalloutEnabled?

internal static bool IsClientCallOutEnabled()
      bool flag1 = false;
            flag1 = (!CompModSwitches.DisableRemoteDebugging.Enabled && Debugger.IsAttached) && (RemoteDebugger.Connection != null);
      catch (Exception){}      
      return flag1;

So, message pumping is neccessary for Remote Debugging to occur. At this point, it's voodoo.

Moral: Debugging under VS.NET is amazing, but it is a different code path than at runtime. A good reminder from George.

Sample: Mark Liu thoughtfully includes sample code.

Now playing: DJ Bolivia - Memory Leak -

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Hosting By
Hosted in an Azure App Service
March 17, 2005 1:45
Scott, small update: there is no need for secondary thread mumbo-jumbo. See details on -- George

Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.