Sunday, August 22, 2004

FlexWikiPad 0.93.1 Released - EnableVisualStyles Suspect

Ah, I have so much sympathy for Microsoft right now. I had just released version 0.93 of FlexWikiPad when I got email from several users complaining of some rather serious problems. Sure enough, I was able to verify them. D'oh!


Well, like any responsible software vendor, I committed to getting a patch out ASAP. And I did, just today: FlexWikiPad version 0.93.1 is available here. I think I got all the bad bugs that I know about. I certainly did a better job of testing this time.


There were about three different bugs that I had to address, but the worst one by far was a bit subtle. You see, users were experiencing actual crashes of the application when creating a new namespace. This was surprising to me because I had used this technique to catch unhandled exceptions. In my handler, I display a dialog box giving the user a chance to email me an exception report, and once they have, I let them go back to the application. In all the months I've been developing FlexWikiPad, this has always worked well enough to let me save my work whenever I hit some sort of unexpected problem. In other words, this was the first real “Oh *&%$ I lost work“ crash I'd seen.


The symptoms looked something like this: an unhandled exception would be thrown, after which the dialog would be displayed. The dialog would work fine (i.e. I could email myself an exception report), and then boom! The application would go down hard, with Application.Run() throwing an SEHException. This happens when someone in unmanaged code receives the dreaded COM HRESULT 0x80004005 (E_FAIL). This is a number I know well, but had hoped never to see again in my life.


I took a look at the code I've added to the application recently. On a whim, I tried removing from my Main the innocuous-looking line:


Application.EnableVisualStyles();


And sure enough, the problems went away. Which is to say, exceptions caused by bugs would still be thrown, but now they could be handled by my handler without an SEHException ensuing.


Needless to say, that line now looks like this:


// Application.EnableVisualStyles();


I can't say I understand why this change would fix the problem. EnableVisualStyles is the call you issue to ensure that you get support for Windows XP-style visual styles. Personally, I think they should have called it EnableUselessVisualFluff, but that's just me. A user requested it, so I wanted to try to support it. Of course, choosing between this and application stability was a pretty easy call.


What's really weird is that I just now compiled the source for version 0.93 to try to play around with this a bit, to see if I could maybe move the call around in Main to see what it would do. And now I can't get it to fail again. Sigh. Don't you just love software?


Either way, FlexWikiPad appears to be stable again. Download and enjoy.

10 comments:

  1. Craig,

    Invoke the Application.DoEvents() just after you invoke the Application.EnableVisualStyles()



    Application.EnableVisualStyles()

    Application.DoEvents()

    Application.Run(Form1())

    ReplyDelete
  2. Kudos, Craig.



    Out of curiosity, what is your thinking about deferring implementation of "hyperlinks of the Web kind".



    T

    ReplyDelete
  3. Re: hyperlinks. I just haven't done it yet. Right now, all the formatting rules are hardcoded. In the next version, I plan to implement pluggable, configurable parsers, which should make it easier to add URL detection, as well as user-defined formatting rules.

    ReplyDelete
  4. Why does the DoEvents make a difference? I assume it just pumps the message queue. But doesn't Run() also pump the message queue?

    ReplyDelete
  5. I can always count on you guys. :)



    Thanks! I generally try to do my research, but this time around I was in too much of a hurry to get the patched version out the door.

    ReplyDelete
  6. Craig, this would explain why you were seeing the problem in the simple tests we were doing of Process.Start but I wasn't. And it also explains one of the errors I think you mentioned (on IM) that you saw - I think you mentioned one that was related to SxS application contexts.



    What Application.EnableVisualStyles does is it creates a new application context. Application contexts are a SxS feature. This is relevant to visual styles because the only way to enable visual styles in Windows XP is to be running in an SxS application context that is configured to load the latest version of the common controls DLL.



    Of course the problem for the Application.EnableVisualStyles API is that your application is already up and running by the time it is called, so it's too late to configure the SxS context. So what it does is it creates a new one, and moves your thread into that.



    There are two complications. First, it defers the actual creation of the relevant SxS context until the creation of the message pump. (Lots of Windows Forms init is done at that point in time.) This is why calling Application.DoEvents is the standard hack. Second, you can sometimes end up with some components having been loaded *before* this init happens, meaning you end up with some DLLs bound to the old context and some to the new, resulting in a horrible mixture of old and new styles...



    This tends to happen as a result of the class level init done at JIT compile time. So this kind of code:



    static void Main()

    {

    Application.EnableVisualStyles();

    Application.DoEvents();

    Form f = new Form();

    Application.Run(f);

    }



    will usually run the static init for the Form class (and the transitive closure of any types referred to in any fields it may have) *before* Main starts to run.



    So this is usually better:



    static void Main()

    {

    Application.EnableVisualStyles();

    Application.DoEvents();

    Go();

    }

    static void Go()

    {

    Form f = new Form();

    Application.Run(f);

    }



    This will typically let the DoEvents run before any static init for Form. Of course that's not guaranteed anywhere, but it seems to work in practice. Today.

    ReplyDelete
  7. We ended up using a manifest because we were having the exact same problem. Here's all it takes:



    Add a new file in the same name as your .exe called [exename].exe.manifest.



    Put the following into it:



    <?xml version="1.0" encoding="UTF-8" standalone="yes" ?>

    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

    <description>Your App Name Here</description>

    <dependency>

    <dependentAssembly>

    <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" publicKeyToken="6595b64144ccf1df"

    language="*" processorArchitecture="x86" />

    </dependentAssembly>

    </dependency>

    </assembly>

    ReplyDelete
  8. Holy cow. Looks like I wasn't alone on hitting this bug. And that I have plenty of places to go to find out how to work around it. The user that requested XP-style display will be happy. :)



    Thanks all!

    ReplyDelete
  9. Hey, that was me that requested it ;-).



    Yes, for me it's standard practice to call:



    Application.DoEvents();



    I don't know why it works, but I see there is an explanation above.



    Don't fortget you also need to set the FlatStyle of the controls that have them to "System".

    ReplyDelete
  10. I'll try to work it into the next release, as long as it doesn't bust other stuff this time! :)

    ReplyDelete