« March 2008 | Main | May 2008 »

April 9, 2008

Exception 0xc0020001 in C++/CLI assembly

After reorganising some code in a C++/CLI assembly, I started getting exception "0xc0020001: The string binding is invalid" when shutting down the C# application that loaded that assembly.

When the program was run under the debugger, it would throw the exception from a function in crtdll.c that was processing the DLL_PROCESS_DETACH notification sent to DllMain. The error occurred when attempting to call the function pointer function_to_call (on line 444).

  437 /* cache the function to call. */

  438 function_to_call = (_PVFV)_decode_pointer(*onexitend);

  439 

  440 /* mark the function pointer as visited. */

  441 *onexitend = (_PVFV)_encoded_null();

  442 

  443 /* call the function, which can eventually change __onexitbegin and __onexitend */

  444 (*function_to_call)();

  445 

  446 onexitbegin_new = (_PVFV *)_decode_pointer(__onexitbegin);

  447 onexitend_new = (_PVFV *)_decode_pointer(__onexitend);

Here's where a feature of the Visual Studio debugger that I hadn't seen before came in very handy. If I set a breakpoint on line 444 and simply hovered my mouse over function_to_call, the debugger tooltip showed the full decorated name of the function, in this case, "_t2m@???__FstaticNativeObject@?1??NativeMethod@@YAHH@Z@YAXXZ@?A0x754dd9c9@@YAXXZ". 

Chris Brumme explains error C0020001 and identifies one of the causes as "trying to call into managed code … after the runtime has started shutting down". According to a forum post (about this same error), "t2m" stands for "transition to managed". The information in the decorated function name ("staticNativeObject" and "NativeMethod") was enough to piece together the rest of the puzzle. I had written code much like the following:

#pragma unmanaged

 

class NativeClass

{

public:

    NativeClass() { }

    ~NativeClass() { }

};

 

bool NativeMethod()

{

    static NativeClass staticNativeObject;

    return true;

}

Even though NativeMethod is emitted as native code, the disassembly showed that it registers a managed entry point for the NativeClass destructor (for staticNativeObject) with the atexit function. But by the time atexit ran this destructor (from DllMain when the C++/CLI assembly was unloaded), the CLR had already started shutting down, and the function call failed.

This problem can be solved by removing the static variable. Either make it non-static, or move it to class or file (or global!) scope. (Slightly more complex workarounds may, of course, be necessary depending on the expense or difficulty of initialising the object.)

It seems like the compiler is emitting incorrect code here—it should register a native entry point for the destructor (or call the managed version from AppDomain.DomainUnloaded) instead—so I filed a bug report with Microsoft Connect on this problem.

Posted by Bradley Grainger at 12:08 PM | Comments (0) | TrackBack

April 8, 2008

Finalizers called from partially constructed objects

Did you know that finalizers are called from partially constructed objects? I certainly didn't. If an exception is thrown from a class constructor, that object is considered “partially constructed” – and its finalizer is still run when the object is garbage collected. Chris Brumme mentioned this four years ago when he helped us understand that it’s hard to implement Finalize properly: “Your Finalize method must tolerate partially constructed instances.”

A coworker discovered this fact when he was unit testing a class that called Debug.Fail in its finalizer to make sure that its instances were being disposed properly. He passed an invalid argument to the constructor to verify that an exception would be thrown – but then found that the call to Debug.Fail in the finalizer was causing tests to fail.

We couldn't figure out a good way to determine whether an object is partially constructed, so we just had to hack around the problem. Any better ideas for detecting undisposed objects?

Posted by Ed Ball at 3:49 PM | Comments (1) | TrackBack

April 5, 2008

“Memory leak” with BitmapImage and MemoryStream

The code snippet below has a small “memory leak”:

BitmapImage bitmap = new BitmapImage();

 

byte[] buffer = GetHugeByteArray(); // from some external source

using (MemoryStream stream = new MemoryStream(buffer, false))

{

    bitmap.BeginInit();

    bitmap.CacheOption = BitmapCacheOption.OnLoad;

    bitmap.StreamSource = stream;

    bitmap.EndInit();

    bitmap.Freeze();

}

 

// use bitmap...

The BitmapImage keeps a reference to the source stream (presumably so that you can read the StreamSource property at any time), so it keeps the MemoryStream object alive. Unfortunately, even though MemoryStream.Dispose has been invoked, it doesn't release the byte array that the memory stream wraps. So, in this case, bitmap is referencing stream, which is referencing buffer, which may be taking up a lot of space on the large object heap. Note that there isn't a true memory leak; when there are no more references to bitmap, all these objects will (eventually) be garbage collected. But since bitmap has already made its own private copy of the image (for rendering), it seems rather wasteful to have the now-unnecessary original copy of the bitmap still in memory.

The solution here is fairly straightforward: create an implementation of Stream that wraps another stream (in this example, the MemoryStream). The Dispose method of this wrapper class needs to release the wrapped stream, so that it can be garbage collected. Once the BitmapImage is initialised with this wrapper stream, the wrapper stream can be disposed, releasing the underlying stream, and allowing the large byte array itself to be freed.

Posted by Bradley Grainger at 12:40 PM | Comments (5) | TrackBack

April 3, 2008

Managed Debugging Assistant Configuration Files

MDAs (Managed Debugging Assistants) are a feature of the CLR that help track down difficult-to-diagnose bugs in complex areas such as interop and threading. Basic information about MDAs can be found in the MSDN Library topic, Stephen Toub's MSDN magazine article, and Mike Stall's blog post. For fine-grained control over the MDAs that are enabled for an application (or to use MDAs without running the application under the Visual Studio debugger), you need to use an application-specific configuration file (as well as setting the MDA registry key or COMPLUS_MDA environment variable, as detailed in those articles). Unfortunately, advanced MDA usage (with an application-specific config file) can itself be a complex and difficult-to-diagnose process.

For starters, the config file must be named properly. Like a regular application config file, the name of this file needs to include the full name of the program (including the “.exe” suffix), followed by “.mda.config”, e.g., MdaTest.exe.mda.config.

I don't know of any way to check that the MDA configuration is being read, apart from deliberately introducing an error (see below) and seeing if the runtime raises the “InvalidConfigFile” MDA when the application is run.

If there is an error in the MDA config file, the following message will be displayed when the application is run:

An unhandled exception ('InvalidConfigFile Managed Debugging Assistant') occurred

If you're running the application under WinDbg, you'll see the following message instead:

<mda:msg xmlns:mda="http://schemas.microsoft.com/CLR/2004/10/mda">
  <!--
       The 'mdaConfig' configuration file is invalid.
   -->
  <mda:invalidConfigFileMsg break="true" configFile="mdaConfig"/>
</mda:msg>

There are two main causes I've found for this error.

1. Incorrect XML element names. Double-check every XML element and attribute name to ensure it's spelt correctly. The basic structure of the config file is:

<mdaConfig>
    <assistants>
        <!-- MDA elements go here -->
    </assistants>
</mdaConfig>

If any of the element names are not recognised, the InvalidConfigFile error will occur. Note that the online MSDN documentation for .NET 2.0 and .NET 3.0 has incorrect examples; be sure you're using the .NET 3.5 version of the documentation. (For example, this 2.0 page says to use “<fatalExecutionError />”, but the equivalent 3.5 page correctly gives “<fatalExecutionEngineError />” as the configuration element name.)

The MDA parser doesn't seem to log any information about why the config file is invalid; to determine the element causing the problem, remove all the elements from the config file and add them back in one-by-one (running the application after each addition) until you find the one that causes the problem.

2. Incorrectly ordered XML elements. The MDA configuration elements must be in alphabetical order, or the file will not load properly. So, this will not work:

<assistants>
    <bindingFailure/>
    <asynchronousThreadAbort/>
</assistants>

but this will:

<assistants>
    <asynchronousThreadAbort/>
    <bindingFailure/>
</assistants>

One final thing to note is that Visual Studio uses the settings in the Debug > Exceptions dialog and ignores the MDA config file when the application is started under the VS debugger. To debug with the MDA config file settings, use WinDbg or attach the Visual Studio debugger to the process after it has started.

Posted by Bradley Grainger at 1:23 PM | Comments (0) | TrackBack

April 2, 2008

Author Introduction: Bradley Grainger

My name is Bradley Grainger. I completed my B.Sc. in Computer Science at the University of Auckland in 1999 and have been employed as a software developer by Logos Bible Software since 2000.

I started programming with Microsoft BASIC (and Z80 assembly language) on my dad's TRS-80 Model 4 before graduating to GW-BASIC and 80286 assembly on a Tandy 1000 TL/2 PC. Since then I've mostly used QuickBASIC, C++, JavaScript, SQL, and C#; other languages I have dabbled in include Perl, Python, Ruby, Haskell and F#. While C# is my primary language these days, every now and then I fall back to my x86 assembly roots when the law of leaky abstractions rears its ugly head and we need to figure out what's actually happening when JITted MSIL is executed; I'll be chronicling some of the more interesting discoveries in this area in the “Debugging” category.

One of the things I greatly enjoy about working at Logos is being part of a small, focused team of great coders (including my wife, who works in our web development department). If an environment that encourages great code is appealing to you, check out http://www.logos.com/jobs for our current openings.

Posted by Bradley Grainger at 9:00 AM | Comments (0) | TrackBack

April 1, 2008

Handling the PropertyChanged event

When handling the PropertyChanged event of an object that implements INotifyPropertyChanged, one is tempted to write a handler like this:

void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)

{

    if (e.PropertyName == "Text")

        UpdateText();

    else if (e.PropertyName == "Icon")

        UpdateIcon();

}

Strictly speaking, this isn't entirely correct. According to the documentation for the PropertyChanged event, “the PropertyChanged event can indicate all properties on the object have changed by using either null or string.Empty as the property name in the PropertyChangedEventArgs.” So, you're forced to write something like this:

void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)

{

    bool bAllChanged = string.IsNullOrEmpty(e.PropertyName);

    if (bAllChanged || e.PropertyName == "Text")

        UpdateText();

    if (bAllChanged || e.PropertyName == "Icon")

        UpdateIcon();

}

To make checking for this situation a little bit easier, and to increase the likelihood that we remember to do it, we put together a nice little extension method on PropertyChangedEventArgs called HasChanged:

public static bool HasChanged(this PropertyChangedEventArgs e,

    string strPropertyName)

{

    string strEventPropertyName = e.PropertyName;

    return string.IsNullOrEmpty(strEventPropertyName) ||

        strPropertyName == strEventPropertyName;

}

Now, proper handling of the PropertyChanged event is as easy as this:

void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)

{

    if (e.HasChanged("Text"))

        UpdateText();

    if (e.HasChanged("Icon"))

        UpdateIcon();

}

Just remember not to use "else"...

Posted by Ed Ball at 4:27 PM | Comments (2) | TrackBack