I’ve spent the last two days working on the Debug Engine and I’ve made some progress:

  • Tracepoint Support
  • Breakpoint Support
  • Support for the Watch window (I might have enlisted this already once but now support is more complete)

I’m not going to post any screenshots this time, instead I’ll try to create an alpha package for you to test. This will probably take some time, too, because I want to make sure that you can install and uninstall it cleanly. I’ve been using the experimental hive so far, so I have to make sure it doesn’t corrupt anyone’s installation accidentially… :-)

A few implementation notes for interested people as always:

  • If you ever have to marshal an object to an IntPtr, be very careful, because doing the wrong thing will certainly crash your application - I had that happen to me and it took me quite some time to figure out why Visual Studio was suddenly ‘experiencing internal problems’ and had to be closed’ all the time:

    When you have to convert an IntPtr to a wrapped object, use

        MyObject object = (MyType) GetObjectFromIUnknown( intPtr ); // or As MyType;

    As you see, the intPtr is a COM object and every COM object implements IUnknown, so the call is pretty safe. The conversion afterwards is already ‘managed’, so safe, too. If you want to convert an IntPtr from an object, you have to be more careful though. The Marshal namespace offers at least two methods for it:

    public static IntPtr GetComInterfaceForObject(
        Object o,
        Type T
    )

    and

    public static IntPtr GetIUnknownForObject(
        Object o
    )

    Don’t use the latter for anything except if you really need an IUnknown interface IntPtr pointer! The method really only returns a pointer to the IUnknown interface of the object, which usually isn’t what the unmanaged code expects - which probably will result in misinterpreting some uninitialized memory as a vtable that is not there. You have to use GetComInterfaceForObject and don’t forget this. Because you’re only working with IntPtrs in the end, there is nothing to warn you about your misstep.

  • If you want to use an object with System.Collection.Generic.Dictionary<K,V>, it’s not sufficient to implement IEquatable<K> as stated in the documentation - you also have to override GetHashCode (and override the normal Equals, too, then while you’re at it), because Dictionary uses GetHashCode for the bucket assignments and you can implement IEquatable as hard as you want, GetHashCode might simply assign two “equal” objects to different buffers and you have a problem..

  • One of the benefits of COM is that no one cares about your implementation.. take advantage of it! I had to remember this the hard way, when I implemented my breakpoint code. At first I wrote a PendingBreakpoint class, which implemented IDebugPendingBreakpoint2, and then a BoundBreakpoint one, which implemented IDebugBoundBreakpoint2. It was unnecessary messy and today I sighed and rewrote the breakpoint code to use just one Breakpoint class that implements both interfaces and it’s a lot more elegant now.

  • Funny fact: An enum that is even mentioned in MSDN and the SDK docs is missing from the Interops library:

    public enum enum_BP_UNBOUND_REASON
    {
        BPUR_UNKNOWN = 0x0000,
        BPUR_CODE_UNLOADED = 0x0002,
        BPUR_BREAKPOINT_REBIND = 0x0003,
        BPUR_BREAKPOINT_ERROR = 0x0004
    };
  • The initialization process for the DE is as follows:

    1. DebugEngine and Program Creation Events
    2. Load Complete Event
    3. Call CreatePendingBreakpoint for all known breakpoints
    4. First Continue Call
    5. Entry Point Event
    6. Either the second Continue call or the SDM enters break mode

    I had to write a few wrapper classes, too, which I might post about next time around. It’s enough for today though.

Cheers,
 Andreas