January, 2008

  • Never doubt thy debugger

    Need to print from a x64 machine? Can you wait 60 seconds?

    • 1 Comments

    I guess some of you might have developed a web application which, among other functionalities, prints some kind of report; and sooner or later you might consider to move the application to a 64 bit machine. At this is what this customer did.

    They had this ASP.NET application which allows the user to run some queries on a backend database and then print the result on a network printer connected to the web server; under some circumstances the application was hanging for 60 seconds (always 60 seconds) before timing out; of course everything was running fine on a 32 bit machine (i.e. we never managed to reproduce the issue there).

    At the beginning we thought to some kind of network timeout on the customer's network or a configuration problem on the server (the application is quite complex so was not possible to have a repro to run and debug on my machine), and this was partially confirmed by a hang dump which showed some error messages about the printer, but this was not consistent enough to get a clue. What was consistent was the use of winspool.dll

    ntdll!NtDelayExecution(void)+0x15
    kernel32!SleepEx(unsigned long dwMilliseconds = 0x3e8, int bAlertable = 0)+0x68
    kernel32!Sleep(unsigned long dwMilliseconds = 0x3e8)+0xf
    winspool!ConnectToLd64In32ServerWorker(void ** hProcess = 0x7309214c, int bThread = 1)+0x33e
    winspool!ConnectToLd64In32Server(void ** hProcess = 0x7309214c, int bThread = 1)+0x1b
    winspool!DeviceCapabilitiesWThunk(unsigned short * pDevice = 0x790d8858, unsigned short * pPort = 0x0ef69df8, unsigned short fwCapability = 0x12, unsigned short * pOutput = 0x00000000, struct _devicemodeW * pDevMode = 0x00000000)+0x77
    CLRStub[StubLinkStub]@1af41e5e(<Win32 error 0n318>)
    System_Drawing_ni!System.Drawing.Printing.PrinterSettings.FastDeviceCapabilities(<HRESULT 0x80004001>)+0x2e
    System.Drawing.Printing.PrinterSettings.DeviceCapabilities(<HRESULT 0x80004001>)+0x66
    System_Drawing_ni!System.Drawing.Printing.PrinterSettings.get_IsValid(<HRESULT 0x80004001>)+0x21
    App_Web_nqftqomb!_Default.Button4_Click(<HRESULT 0x80004001>)+0xf2

    As you can see, after the click on the button to print the report we're going down to the OS for the actual printing, we're passing through the ConnectToLd64In32ServerWorker method and then we're waiting for 1 second (dwMilliseconds = 0x3e8 = 1000 milliseconds = 1 second). The next logical step has been to have a look at the source code for that particular method in winspool and found that under under some circumstances we try to connect to an RPC resource and we retry to 60 seconds before exiting to avoid an infinite hang. Good, now we only have to understand why using NETWORK SERVICE works fine while impersonating the logged on user fails (clearly some kind of missing permission).

    Unfortunately the final customer was not interested in spending more time investigating the issue but they wanted a working solution right away, so we decided to use impersonation by code to switch to NETWORK SERVICE when ready to print and then switch back to the logged on user; we made a few changes to the same code I already used for another case (here) to do the trick:

    protected void Button1_Click(object sender, EventArgs e)
    {
        //This should be the authenticated user
        Label1.Text = "Process running as " + WindowsIdentity.GetCurrent().Name + " (before impersonating)";
    
        //Save a reference to the authenticated user token for later
        WindowsPrincipal p = (WindowsPrincipal)HttpContext.Current.User;
        WindowsIdentity id = (WindowsIdentity)p.Identity;
    
        //Revert to the worker process account (default is NETWORK SERVICE)
        RevertToSelf();
        Label3.Text = "Process running as " + WindowsIdentity.GetCurrent().Name + " (after impersonating)";
    
        try
        {
            //Print an to whatever you need to do under the NETWORK SERVICE context
        }
        catch (Exception ex)
        {
            Label2.Text = ex.Message;
        }
    
        //Impersonate back the authenticatd user
        WindowsImpersonationContext wic = id.Impersonate();
        Label4.Text = "Process running as " + WindowsIdentity.GetCurrent().Name + " (reverting back)";
    
        //VERY IMPORTANT: ALWAYS REMEMBER TO CALL THE UNDO METHOD WHEN DONE!
        wic.Undo();
    }

     

    While it's a shame that we didn't have the opportunity to fully understand what was going wrong, if you'll ever find yourself in this situation you at least should have a solution ready to have you application up and running quickly; but of course if you what to understand more feel free to open a call with CSS! smile_wink

     

    Carlo

    Quote of the day:
    Inspiration is wonderful when it happens, but the writer must develop an approach for the rest of the time... The wait is simply too long. - Leonard Bernstein
  • Never doubt thy debugger

    Ajax resource intermittently not accessible (http compression)

    • 6 Comments

    A few days before Christmas I had a case where the customer was intermittently getting troubles with his javascript/Ajax resource; as usual everything was working fine on the development machine, but when moved on the production server the application started throwing some client side javascript exceptions like "myVar is undefined" which means that the Ajax resource was not accessible by the browser.

    After some of the usual checks and discussions with the customer we found out that the production server was using HTTP Compression which is an old friend of mine (I already rambled about it here); they developed a custom HTTP Module to implement the .NET Framework compression classes and mechanism and disabling it was not an option, since their resource were quite large were affecting the applications' performance.

    Then I had a couple of weeks off for Christmas and my colleague Gunnar took over the case (isn't nice when you go on holiday and someone else takes care of the job for you? smile_wink) and with some further debugging they found this interesting method:

    private static bool IsCompressionEnabled(HttpContext context) { 
        return ScriptingScriptResourceHandlerSection.ApplicationSettings.EnableCompression && 
            ((context == null) || 
            !context.Request.Browser.IsBrowser("IE") || 
            (context.Request.Browser.MajorVersion > 6)); 
    }

     

    This means that Ajax does support compression for Internet Explorer 7 but it does not support compression for Internet Explorer 6. Why?

    Well, the fact is that IE6 has some serious troubles with compressed content, and those issues have been resolved in IE7:

    Another release containing several fixes is in the latest Windows Script 5.7 which contains updates for jscript parser:

    The problem for a public Internet side is that you don't know if advance if your users will have all those updated installed, and of course you cannot force them to update their systems; moreover there are some security updates released through Windows Update for IE6 have also updated the management for compressed resources. After some testing we found that customer's application was finally working with IE6 but HTTP compression was  not used by the runtime, so we decided to create an alias for IE7 (see clientTarget element) and then set Request.ClientTarget by code to one of those aliases to force the runtime consider the request as if it were coming from IE7 even if it was actually IE6; this enables both HTTP compression for Ajax resources and partial compressed postbacks.

     

    Carlo

    Quote of the day:
    The wages of sin are death, but by the time taxes are taken out, it's just sort of a tired feeling. - Paula Poundstone
Page 1 of 1 (2 items)