Using MC++ to wrap ICorDebug for .NET 1.1

MDbg and the managed wrappers around ICorDebug only work on .NET 2.0 (VS2005). I previously discussed why they won't work on 1.1. One workaround to access ICorDebug from managed code in .NET 1.1 is to use MC++. In general, MC++ can be extremely useful for gluing managed and unmanaged code together.

Using MC++ :
MC++ will let you easily create a bunch of real managed objects that wrap the com-classic ICorDebug. This lets you control exactly how you're using ICorDebug, including how you're using IUnknown, and thus work around the the issues with COM-interop on ICorDebug in 1.1.

This beats out alternative ideas such as:
- creating your own COM-object to wrap ICorDebug and then using COM-interop to import that.
- using a lot of P/ invokes and native wrappers to access ICorDebug.
Both of these involve larger wrapper layers. Using MC++ here lets you avoid an extra component, and COM-interop altogether. We use MC++ for the native disassembly view in the SDK MDbg (mdbgdis.dll).

Fitting into application model:
I think the MC++ model here fits well into many Application's models:
1. Debuggers commonly wrap ICorDebug objects with their own classes to add additional functionality. (MDbg, VS, Cordbg all do this). Thus you can just make those wrappers be in MC++.
2. In a GUI app, you're going to have some STA UI thread that needs to make a cross thread call to access ICorDebug (which is MTA) and then get the data back to the UI thread to to do stuff like fill out some listbox for a callstack with frames. This sets up a natural boundary. The code that fills out the listbox can just be written in MC++. Or you could have some small MC++ glue to just read the data from ICorDebug and then give it back to some C# that actually fills out the listbox. The rest of the GUI can be some managed code, such as winforms app.

One testimony that it works:
Claudiu Codreanu (from Starlims) tried out this technique and found it to work very well.

Code examples:

Here's an example of part of a MC++ class wrapper for ICorDebugThread. It's using the new VS2005 MC++ syntax, but you can do the same things with the old VS2003 MC++ syntax.

 
// Managed MC++ class to wrap it.
public ref class ThreadWrapper
{
public:
    // Method 
    int GetId()
    {
        DWORD id;
        HRESULT hr = m_thread->GetID(&id);
        
        System::Runtime::InteropServices::Marshal::ThrowExceptionForHR(hr);
        return id;
    }
    
    // Above as a property
    property int Id {
        int get()
        {
            return GetId();
        }
    }

    // add other methods of interest

    // Raw pointer to COM-classic interface from ICorDebug
    ICorDebugThread * m_thread; 

    // Could have additional data 
    // (or we could put that data in a derived class)
};

C# can then access that class just like any other class. For example:

     void PrintThread(ThreadWrapper t)
    {
        Console.WriteLine("Thread's ID is: " + t.Id);
    }

Comments

  • Anonymous
    December 14, 2005
    Indeed, using this technique we succeeded to use managed C++ as a bridge between our managed application and ICorDebug services.

    We came out with a small, cute prototype debugger, with a decent amount of functionality. We still have a lot to add to it, but it's a promising start.

    Managed C++ saved us a lot of work, and it's a breeze to be able to use cool things like System::String or System::Collections::Hashtable from C++.

    Secondly, the articles we found on the web (Jon Shute, Mike Pellegrino, etc) are excellent, and one needs to read them before trying to approach this complex subject matter. However, we also found cordbg's source code indispensable. We had to consult it for almost everything, and it is rich in explanations and hints that cannot be found anywhere else.

    We think that it should not be discontinued; for people who need to stick with C++, or who don't need the full blown ComInterop assemblies from Mdbg, or who need to understand ICD in depth, something like cordbg is a real blessing.

    Claudiu Codreanu.