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
Marshalnamespace 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
IUnknowninterfaceIntPtrpointer! The method really only returns a pointer to theIUnknowninterface 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 useGetComInterfaceForObjectand don’t forget this. Because you’re only working withIntPtrsin 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 implementIEquatable<K>as stated in the documentation - you also have to overrideGetHashCode(and override the normalEquals, too, then while you’re at it), becauseDictionaryusesGetHashCodefor the bucket assignments and you can implementIEquatableas hard as you want,GetHashCodemight 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
PendingBreakpointclass, which implementedIDebugPendingBreakpoint2, and then aBoundBreakpointone, which implementedIDebugBoundBreakpoint2. It was unnecessary messy and today I sighed and rewrote the breakpoint code to use just oneBreakpointclass 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
Interopslibrary: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:
- DebugEngine and Program Creation Events
- Load Complete Event
- Call CreatePendingBreakpoint for all known breakpoints
- First Continue Call
- Entry Point Event
- 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