November, 2006

  • Never doubt thy debugger

    Code Access Security hosting control in IE

    • 13 Comments

     

    PROBLEM
    You have a Windows Forms component hosted in Internet Explorer, and you want to catch events raised by this control from client side scripting; to avoid security errors at runtime, the control must have "Allow calls to unmanaged assemblies" permission. if you do this, you'll notice that this works only if you  give this permission to the whole Zone or Site, but does not work if you give it just to the Assembly or the URL.

    REASON
    The reasoning behind the security exception is AppDomains. Before IE can load your assembly, it must create an AppDomain to load the assembly into. When it creates this AppDomain, it assigns all the evidence it knows without loading your assembly, the Site and Zone that it is loading from. Since the AppDomain itself does not get any evidence about the signature that your assembly has (it can't since the assembly is not loaded yet), it will not match the code group that you created giving extra trust. Now when a security demand occurs, a stack walk begins. When your assembly is checked for correct permissions, it passes, and the stack walk continues until it gets to the AppDomain. Since the AppDomain doesn't have the permissions that are required by this demand, it causes the SecurityException to be thrown.

    SOLUTION
    If you want to bypass the above problem and make your application to work only giving permission to the Assembly or the URL, then you have to Assert the permissions you need in the control code, before rising the event. Thanks to asserting, the security check can be done again at runtime after the AppDomain is created.
    The following sample should do the trick:

    using System;
    using System.Drawing;
    using System.Security;
    using System.Security.Permissions;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    [assembly: AllowPartiallyTrustedCallers]
    [ComSourceInterfaces(typeof(ISimpleEvents))]
    public sealed class SimpleControl : Control
    {
        private Button button1;
    
        public SimpleControl()
        {
            this.BackColor = Color.Green;
            button1 = new Button();
            button1.Text = "Click Me!";
            button1.Width = 100;
            button1.Click += new EventHandler(HandleButtonClick);
            Controls.Add(button1);
        }
    
        private void HandleButtonClick(object sender, EventArgs e)
        {
            try
            {
                MethodInvoker h = ButtonClicked;
                if (null != h)
                {
                    ButtonClicked();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }
    
        public event MethodInvoker ButtonClicked;
    
        public void SayHello()
        {
            MessageBox.Show("Hello from Windows Forms");
        }
    
        public string ThemedBackgroundColor
        {
            get { return ColorTranslator.ToHtml(this.BackColor); }
            set { this.BackColor = ColorTranslator.FromHtml(value); }
        }
    }
    
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch)]
    public interface ISimpleEvents
    {
        //Each event must have a unique DispId
        [DispId(1)]
        void ButtonClicked();
    }

     

    Cheers
    Carlo

  • Never doubt thy debugger

    Don't slide your expiration without cookies!

    • 6 Comments

    This week I've been working on a problem which at the beginning seemed to be just a matter of configuration but that then took some more time that I expected to resolve... so, here it is. In my ASP.NET 2.0 application I edit my web.config to set slidingExpiration=”true” and cookieless=”true” in Forms authentication. Basically what happens is that after half of the timeout set in <forms> element is elapsed, the user is redirected to the login page; e.g. I set <forms timeout="3" cookieless="UseUri" enableCrossAppRedirects="true" /> (timeout 3 minutes), if I postback the page after 1 minute and 35 seconds, I’m redirected to the login page (but the Forms authentication ticked should still be valid…). This can’t be a cookie problem because we are not using cookies…

    First, a quick review of sliding expiration: "When the SlidingExpiration is set to true, the time interval during which the authentication cookie is valid is reset to the expiration Timeout property value. This happens if the user browses after half of the timeout has expired. For example, if you set an expiration of 20 minutes by using sliding expiration, a user can visit the site at 2:00 PM and receive a cookie that is set to expire at 2:20 PM. The expiration is only updated if the user visits the site after 2:10 PM. If the user visits the site at 2:09 PM, the cookie is not updated because half of the expiration time has not passed. If the user then waits 12 minutes, visiting the site at 2:21 PM, the cookie will be expired".

    You can easilly reproduce the behavior yourself creating an ASP.NET 2.0 application (I used aspnetdb for authentication) with a DropDownList and a Submit button and assure you have the following in your web.config:

    <authentication mode="Forms">
         <forms 
               name=".MYASPXAUTHTEST" 
               loginUrl="login.aspx" 
               enableCrossAppRedirects="true" 
               protection="All" 
               timeout="3" 
               cookieless="UseUri" 
               path="/" 
               requireSSL="false" 
               slidingExpiration="true" />
    </authentication>

    You can use the following code for your Default.aspx page:

    <%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb"
    Inherits="_Default" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Default test</title> </head> <body> <form id="form1" runat="server"> <div> <table> <tr> <td><asp:DropDownList ID="ddlTest" runat="server"> </asp:DropDownList></td> </tr> <tr><td>&nbsp;</td></tr> <tr> <td><asp:Button ID="btnSubmit" runat="server" Text="Button" />
    </
    td> </tr> </table> </div> </form> </body> </html>

    And in your code file:

    Partial Class _Default
        Inherits System.Web.UI.Page
    
        Protected Sub Page_Load(ByVal sender As Object, _
         ByVal e As System.EventArgs) Handles Me.Load
    
            Response.Write("Submit at: " & DateTime.Now.ToString() & "<br>")
            Response.Write("Next submit at: " & _
             DateTime.Now.AddSeconds(90).ToString() & "<br>")
            Response.Write("IsPostBack = " & IsPostBack)
    
            If Not IsPostBack Then
                ddlTest.Items.Add("<select>")
                ddlTest.Items.Add("test1")
                ddlTest.Items.Add("test2")
                ddlTest.Items.Add("test3")
            End If
        End Sub
    End Class
    

    Remember you also need a login.aspx page with a standard input form to provide username and password; then to reproduce the behavior:

    1. Browse the Default.aspx (your should be redirected to login.aspx
    2. Enter your credentials and submit the page
    3. In Default.aspx choose random value for the DropDownList
    4. Wait at least 91 seconds but no more that 3 minutes (you can check the "Next Submit" value I added to the top of the page
    5. Click “Submit” button again… and you should see the DropDownList value is reset to the default; moreover IsPostBack returns false even if that’s a postback! This does not happen if we use cookies…

    Well... it turned out that the combination of cookieless authentication and sliding expirations means that users will be randomly redirected back to their site anytime we detect that less than 50% of the TTL remains on the ticket.  Because the ticket is cookieless, the only way we can refresh the ticket is to force a redirect with the new ticket placed in the Url used for the redirect. 

    Nice,  but I still have a doubt: if we can renew the authentication ticked during a postback without going through the authentication process, why we can’t do it by changing the ticket in the Url? Isn’t this the same principle? Moreover, why the IsPostBack returns false even if that is a real postback?

    The problem is that with cookieless forms authentication, the ticket is actually part of the Url.  When the timeout is updated in the ticket, that means the serialized text representation of the ticket also changes.  As a result we need to change the Url itself to reflect the new value.  Unlike cookies, you can’t rewrite a Url on the server and have the client transparently pick up the new Url.  Instead you have to force a redirect because the redirect forces the browser to reload the document and update the Url that you see in the address bar. That redirect process is also why IsPostBack returns false.  The redirect is not a postback triggered by a client-side ASP.NET control.  As a result, then the GET request for the redirect comes back to the server, the server doesn’t recognize it as a postback.

    Thanks to Stefan Schackow for discussing this with me!

     

    Cheers
    Carlo

  • Never doubt thy debugger

    Visual Studio 2005 hotfixes made public for download

    • 2 Comments

    After reading this post a while ago regarding the availability Policy for some hotfixes (e.g., not all hotfixes are public for download, and you have to call Microsoft Support to obtain the one you need), I had an internal discussion with my team on this topic.
    Basically those are some of the reasons (at least the one I know about) for this controversial choice:

    • As stated in every KB article, those fixes have not gone through the same accurate testing as public fixes or Service Packs, so it's possible that with a particular configuration the fix a customer request is not the right approach for him
    • We keep track of the hotfixes we send to customers, and if a bug or problem is later discovered in that particular fix, thanks for those records we know who has installed it and we are able to contact those customers to send them an updated version of the patch, or in any case inform them of the newly discovered problem and help them to avoid it
    • More often than you can imagine, we receive clearly wrong hotfix requests: for instance, a particular exception in Framework 1.0 has been fixed and the KB article is still public, but a customer with Framework 1.1 (which is not affected by that bug) requests that fix anyway... In such a case we usually try to help the customer to diagnose the real problem anyway (we say in best effort, i.e. spending a reasonable amount of time and effort for a free call such those ones)
    • Unfortunately, often the same customers who say "Hey, I'm an adult, I know what I'm doing, just give me that fix!" are the same who then complain Microsoft for not properly testing his software... we'd also like to avoid this unsatisfactory situations
    • Etc (for sure I'm missing something important, I apologize...)

    Maybe the problem is that we internally know the reasons behind the above, but if well prepared and technically skilled people have difficulties to understand it, maybe we are not that good at explaining it, despite what is written in our KB articles.

    We'll after all the nice thing is that Microsoft took your feedback on this topic and made a decision!   (direct link to DevDiv Hotfix Public Availability Pilot Program)

    Here is an except from an internal email I received on the subject:

    I'm thrilled to say that today we've launched the Devdiv hotfix Public Availability Pilot Program.  This pilot program will allow you to download the most frequently requested Visual Studio 2005 hotfixes without having to contact Microsoft Developer Support.  For more information about this pilot and a list of hotfixes that are available for download please visit the pilot home page.

    The current set of hotfixes that are available for download are the top 10 most requested ones for Visual Studio 2005, but we plan to expand the list dramatically in the next few weeks. 

    If you have suggestions for the program you can submit them via the connect web site.  I also welcome comments on my blog.

    So... don't miss this chance to shape this new service, and let us know what you think!

     

    Cheers
    Carlo

  • Never doubt thy debugger

    Help, my application crashes! No, it hangs... Uhm... we get a strange error... What...?!? (the importance of a proper problem description)

    • 0 Comments

    Dealing on a daily basis with customers and the problems they encounter using Microsoft technologies and products, and having to help them troubleshoot and resolve those problems, specially at the beginning of a new support call (after I pick the case up from the incoming queue and put it in my wipbin, as it's called my personal queue in our CRM software) I  call the customer to discuss about the problem and to get a detailed overview of the faulting application, the environment where it's running (or it is supposed to run), the error they get, under which circumstances they get it (under load picks, any specific operations going on at that time on the server, any reproducible behavior by one of the clients...), side effects (high memory, high CPU, performance slowness...), how they (at least temporarily) get rid of it (recycle the application pool, iisreset, recompile...). in those situations, quite often I end up asking almost the same questions to every customer to start building a picture of the situation, and when I think I have this quite clear in my mind I can elaborate an action plan to start digging into the situation questing for the bad guy.
    No need to say that those first steps are very important to start with the right foot, because a misunderstanding at this stage could direct our work in the wrong direction and we could easily end up searching the problem in the wrong direction and be diverted by some useless facts which have nothing to do with the problem we need to solve; this as you can imagine brings frustration (in both the customer and me) and makes us waste precious time while the users are still affected by the problem... not a nice situation!

    Sometimes I found that even after what I considered a satisfactory first call with a customer and I thought I had everything I needed to start working on the problem, I then find that something very important is still missing... if a customer tells me "My application is crashing randomly" I tend to trust him, and I can then suggest an adequate action plan for a process crash (i.e. capture a crash dump with adplus). Well... the problem is that sometimes the worker process is not really crashing, and what customer refers to as a crash is some other kind of exception which affects the application and of course give the users a bad working experience, but that need a different troubleshooting approach... I learnt this lesson myself the hard way, where the customer and I spent 2-3 days desperately trying to understand why we were not able to capture a dump, to then discover that we were using the same term but with different meanings... (for the records: now I'm always very clear with the customers regarding what really is a crash and what is not!).

    So, to clarify: we have a real crash when looking at your ASP.NET worker process (aspnet_wp.exe under Windows 200/XP, w3wp.exe under Windows 2003) when the problem occurs you see that it gets a new PID (ProcessID); this means that the process has somehow been recycled (either by IIS because of a configuration setting, or because of a critical error which terminated the process). Looking at your System event log you'll also see a message similar to "a process serving application pool xxxx terminated unexpectedly".

    Other types of problems/exceptions require a different approach (sometimes we could use the -crash switch to get information about first chance exceptions, but that's a different story). If the application is somehow blocked and stops responding to new incoming http requests you get a hang/deadlock (most likely because all of your threads a busy doing other things, rather serving new requests...), and in this case even if you get an exception or an ugly error message ("Server too busy" is a quite bad one) you don't have a crash and we'll probably need to capture a hang dump to start looking at the inside of the process...

    Hope this makes sense, and if you'll ever need to raise a support call be prepared to give as much details as possible to let us understand your problem, so that we can suggest the most appropriate action plan to address the issue.

     

    Cheers
    Carlo

Page 1 of 1 (4 items)