<ChristophDotNet

desc="My angle on brackets" />

  • Running IBF with the .NET Framework 2.0 installed

    ".NET ends DLL Hell" … that was the message back in the days when the .NET Framework was still in beta.
     
    With .NET, each assembly is easily versioned, global assembly registrations are versions specific and app developers are strongly encouraged to keep their assemblies local. And that can work to solve DLL Hell problems.
     
    However, .NET doesn't completely solve the problem quite yet. There is still the case of unmanaged excutables loading the .NET Framework where DLL Hell is as bad as ever. I've blogged a few times about my problems with BizTalk 2004, NUnit's problem's with different framework versions are pretty well known as well and generally speaking having more than one version of the .NET framework installed is an issue for every unmanaged application, e.g. Internet Explorer, Word, etc. , and managed apps that explicitly load the runtime. The latest victim on my list of apps that break when installing a new version of the .NET Framework is the IBF client.
     
    I found that out, when I was preparing a demo for my last SQL Server XML features talk. I thought it would be a cool idea to consume a native SQL Server 2005 web service in IBF. Unfortunately, I got an error from the smart tag loader from Excel, Outlook, Word and since IBF 1.5 added Internet Explorer support, I even got errors when launching IE. The error was:
     
    There are problems with the Microsoft Information Bridge Framework 1.5 client. Run the client Setup program to repair the installation. The error details are stored in the event log.
     
    But the details in the event log didn't really provide that much more information:
     
    Information Bridge cannot start because Microsoft .NET Framework 1.1 failed. Return Code: 80131604
     
    Since I had the smart tag running on a different VPC image that didn't have SQL Server 2005 or a CTP of .NET installed I suspected once again issues with loading the wrong version of the .NET framework.
     
    When troubleshooting my BizTalk issues I found how you can configure applications to load specific runtime versions. With IBF, I just had to find where I needed to add these configurations. With some help from Ted Howard and Michael Kiselman from the IBF team I found that I need to add .NET app.config files for the unmanaged(!) applications that host the IBF pane, i.e. Word and Excel. That's pretty obvious actually, if you know that Fusion, the .NET assembly loader, does look for app.config files for unmanaged and managed executables alike. Adding WinWord.exe.config, Excel.exe.config, Outlook.exe.config  files in \Program Files\Microsoft Office\OFFICE11 for Word, Excel and Outlook and Iexplore.exe.config in \Program Files\Internet Explorer for Internet Explorer with the following entries:
     
    <?xml version="1.0"?>
    <configuration>
     
      <startup>
        <supportedRuntime version="v1.1.4322" />
      </startup>
     
    </configuration>
     
    fixes the smart tag problems just fine.
     
    Now I still have some issues trying to run VSTO solutions on that VPC image, but I haven't had a chance to get to the bottom of those yet. 

  • The Future of Microsoft SOA Client Architecture, my take

    So to answer the headline in Scott's post, yes I do believe IBF is a herald of the service clients to come. An autonomous client that plugs into another applications and communicates with it's host via XML messages. That's what we saw in the Don-and-Chris Show during the PDC 2003 keynote on Indigo. Even this implementation is almost completely decoupled from Office. It runs autonomous during debugging already, but still requires a COM interface (via. NET interop) as the backchannel to the host app. In the future hopefully more and more apps will be able to share their current context, serialized to XML messages, with frameworks like IBF. I have two SO projects going on right now that that would tremendously benefit from such capabilities.

  • What do IBF 1.5 and BizTalk have in common?

    Version 1.5 of the Information Bridge Framework (IBF) was released last weekend and is available for download from MSDN.
     
    I gave it a whirl to get ready for my seminar in Austin on Wednesday and found that the description of one of the new features is a slight understatement. On the download page it says:
    A few of the improvements you can look forward to in this release are an integrated debugger, integrated XSL transformation capability
     
    Well, upon further examination, it turns out that the XSL transform capability is in fact the BizTalk 2004 Mapper. It's installed when you install the new Metadata designer -- free …
     
    Anyway … IBF 1.5 is a huge, huge improvement over 1.0. Big kudos to the IBF team.
     
    If you happen to be in Austin on 3/2 and you're curious about IBF 1.5, you can attend the seminar -- also free. To register, please go to the Microsoft Training Web Site (http://www.microsofttraining.com/content/invitationcode.asp) and enter Course Code Number 303157.

  • 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.