Archives

Archives / 2005 / January
  • Mdi Bug - When Focus isn't enough

     
    We discovered a problem with Mdi-style Windows.Forms applications with the .NET Framework 1.1 with and without SP 1. The problem causes incorrect handling of keyboard input by MdiClient windows.
     
    To reproduce the problem we build a very simple forms application with a toolbar to open client windows. The client window has a text box and some buttons. We wired up a KeyDown handler to the textbox that checks for pressing the enter key while the cursor is in the text box. In the repro example simply shows a MessageBox when you pressed enter. The event handler works fine when you first open a client window.
     
     
     
     
    However, when you click on any of the buttons, then open another client window (or change focus to another window in the Mdi Frame) and then put the cursor back into the text box and hit enter, the form will invoke the event handler for the button you clicked before you switched between child windows. It will no longer invoke the KeyPress handler that's hooked up to the textbox, like it did before we switched focus between the two children.
     
     
     
     
     
    Trying to determine what's going on, we looked at a number of things. First we looked if it was a matter of the current focus on the form, but the Focused property is set to the TextBox when the handler for the button is executed.
     
    Then we checked if the registered event handlers for the textbox were somehow getting messed up, but examining the event handlers with the code below showed that the events were still properly wired up. We also found that the sender object passed to the event handler is indeed the button, therefore we started looking for the problem in other areas.
     
    The realized that the form acted as if it’s executing the event handler for the AcceptButton, but both AcceptButton properties, for the MdiChild and the main frame, were set to null.
     
    We then concluded that it must be a bug in the Mdi Framework and sent it off to Microsoft. In the meantime, the only workaround we came up with was to add code to every button handler on the form to check the Focused property.
     
    private void button2_Click(object sender, System.EventArgs e)
    {
    if( textBox1.Focused )
    {
    MessageBox.Show( "You should really call the text box event handler" );
     
    }
    else
    {
    MessageBox.Show( "Button 2" );
    }
    }
     
    That workaround gets pretty tedious if you have lots of input controls on the form though. I'll post an update when I get a response from Microsoft.

  • Jeff Richter For $100 - Help the Tsunami Victims

    Julie Lerman pointed me to this great idea to collect donations for the tsunami relief:
     
    Many .NET celebrities are auctioning off an hour of their precious consulting time and donate their fee to charity. It's great to see our community getting involved in such a great cause as well. It doesn't always have to be actors and rock stars, we can do it, too.
     
    So if you always wanted to get some consulting time from people like Jeff Richter, Jeff Prosise, The Angry Coder - Johnathan Goodyear, Ted Neward, John Lam or the many, many others that valounteered, now's the time to do it! Go to the auction and get yourself an hour of their time. Click on the link above to see the details on the auction and the full list of people available.

  • Follow up: Workaround for XmlSerializer assembly leaks

     
    A while ago, Paul Wilson and Kirk Allen Evans reported that the XmlSerializer is leaking assemblies when the serializer object was instantiated with any of the constructors but the most basic one. The simple XmlSerializer constructor has logic to re-use the temporary assemblies if it already built them for a given type. The more complex constructors are missing that caching logic and allow the temporary assemblies to leak if you don't keep the serializer instance around in your program.
     
    the other day, I found out that the XmlSerializer assembly leak is not going to be fixed in Whidbey. Within a few days of me reading that, John Bristowe was asking why there isn’t anything in the Mvp.Xml project to work around that issue. And because I didn't have a real good answer to that I sat down and wrote one, because there should be.
     
    The result is the XmlSerializerCache class. It's very easy to use. You simply obtain XmlSerializer instances from the various overloads of XmlSerializerCache.GetSerializer() instead of the instantiating a serializer instance with the XmlSerializer constructor. The signatures of the GetSerializer() method match those of the XmlSerializer constructor.
     
    XmlSerializer ser = XmlSerializerCache.GetSerializer( typeof( MyClass ), "http://www.mvpxml.org/mytool" );
     
    The XmlSerializerCache canonicalizes the contents of the parameter list to reduce the amount of serializer objects in the application. For example, canonicalization recognizes that an XmlSerializer instance created for this request:
     
    XmlSerializerCache cache = new XmlSerializerCache();
    XmlSerializer ser1 = cache.GetSerializer( typeof(MyType), new Type[] { typeof(TypeOne), typeof(TypeTwo) } );
     
    is compatible with this request:
     
    XmlSerializerCache cache = new XmlSerializerCache();
    XmlSerializer ser2 = cache.GetSerializer( typeof(MyType), new Type[] { typeof(TypeTwo), typeof(TypeOne) } );
     
    The GetSerializer method checks cache for compatible instances that are compatible with the method parameters before constructing a new instance to minimize the number of created serializers. The compatibility check computes a fingerprint from the method parameters. This computation looks at every property of the passed in parameters. Yet this operation is still less expensive than reflecting over an object graph to create code and compile temporary assemblies and it prevents "leaking" memory due to orphaned assemblies that you cannot unload.
     
    Parameter canonicalization is not the only feature of the XmlSerializerCache. It also allows you to monitor what it's doing, through raising events and via two performance counters. Both counters are in the category Mvp.Xml.XmlSerializerCache. Make sure you install the Mvp.Xml with the .msi to create and delete the performance counters.
     
    The performance first counter  "Cache Hits" exposes the number of currently cached XmlSerializer instances. The second counter "Cached Instances" reflects the number of cache hits, i.e. how many times the instances were successfully retrieved from the cache instead of creating a new one.
     
    In addition to the performance counters, the XmlSerializerCache also features a pair of events to track its operations from within the program that is using it. The parameters of these events contain the parameter of the request to the XmlSerializerCache's in case you want to track or log what serializers are created for example.

  • C++ support in WSE 2.0 SP2

    WSE 2.0 SP2 adds some support for adding WSE to C++ projects, which had been missing since the initial release last May.
     
    At last, the WSE Configuration Editor, the one that comes up when you right-click in the Solution Explorer and then select WSE 2.0 Settings allows you to enable and disable WSE for C++ project and lets you create policies.
     
     
    Before SP2, nothing would happen when you clicked on the item in the context menu. Unfortunately, other IDE integration, such as automatic generation of WSE proxies in a WSE enabled project is still not happening and probably will never happen in WSE 2 because wsewsdl2.exe does not support generation of C++ code. If you take a look with Reflector, you'll find that for some reason, the tool does not emit the proxy code via CodeDOM, hence it would be really difficult for the WSE team to generate C++ proxy classes.
     
    A general shortcoming of the IDE integration of the configuration tool, that's not specific the C++ projects are the relative references generated for the policy files. When you enable policy for a C++ an application project (not a web service), the tool will add
     
    <policy>
    <cache name="../policyCache.config" />
    </policy>
     
     
    to the application's configuration file. Unfortunately, the relative reference would only valid during development -- if C++ projects behaved like C# projects in the first place. When you deploy the application you most likely will not have the policyCache.config file one directory higher than the application and its config file. To complicate matters a little further, C++ application projects behave differentely than C# of VB.NET projects in Visual Studio. C++ projects don't even copy the app.config file to the output directory. The best solution is to delete the relative path information from the path to the policyCache.config file.
     
    <policy>
    <cache name="policyCache.config" />
    </policy>
     
    and then add a post build event to rename app.config and copy it, together with the policyCache file to the output directory:
     
    copy $(ProjectDir)app.config $(OutDir)\$(TargetFileName).config /Y
    copy "$(ProjectDir)policyCache.config" $(OutDir)\ /Y
     
     
    Changing the path and letting the post build event do the work for you also helps when you build an installer. Simply mark the policyCache.config file as "Content", add "Content File" to the installer project and you're ready to go.
     
    Finally, when you build and ASP.NET Web service in C++ you have to make sure that the policyCache.config file is deployed to the web server.
     

  • Talks in 2005

    The new year is starting out really busy and I've already got a few user group talks scheduled:
     
    2/10/2005, Houston .NET User Group: The XML Data Type and XQuery in SQL Server 2005
    3/16/2005, Austin SQL Server Group: XML and Web Services in SQL Server 2005
    4/13/2005, Austin XML: Indigo - Where are my anglebrackets and other WS-Goodness
     
    and tentatively
    12/12/2005, Austin .NET User Group: Indigo - Where are my anglebrackets and other WS-Goodness
     
    I'll post the abstracts soon.
     
    Hope to see you there.