Debugging Unmanaged Code Calling Back Into Managed


One of the guys I work with spent a good part of his day tracking down a tricky problem with some code he had written.  The mistake he made is relatively common, so I thought I’d share it here.


The application he had written was managed.  He made a delegate to a static managed method, and passed that delegate to unmanaged code, which used it as a callback.  When this was written as a single threaded application, everything worked as expected.  The problem came when he passed the callback to unmanaged code on a different thread.  Once the app was modified to be multithreaded, he would get occasional and random AVs.  From looking at them, it appeared that the address of the callback in the unmanaged world was garbage.


What was happening was that the garbage collector was running in the background and collecting his delegate.  Initially he didn’t debug in this area because the method was static, and the AppDomain the class the method was on was not yet unloaded.  However, the garbage collecter was not collecting the object the method was on (since it was static), it was actually collecting the delegate itself.  The solution to the problem was to simply keep a reference to the delegate elsewhere in his code so that the garbage collecter wouldn’t touch it.


The reason the application worked when the app was single threaded was that the GC thread never got scheduled to run before the callback occurred.  However, when he the callback occured in a seperate thread, the GC thread would somtimes get run before hand.  And since the garbage collector likes to collect objects that were allocated recently, the delegate was usually one of the first things to go.

Comments (1)

  1. You know it’s really strange. I had the EXACT same problem today, but I don’t think you’re talking about me… [or are you?]

    I was working on a sample, and I would p/invoke out from my managed code and hand the delegate out. Then later the managed code would cause the call to be invoked.

    And it ended up that I had not one delegate, but two. I did the right thing to keep the 1st delegate alive. I would go into the debugger, and sure enough, there it was! Still alive! How could I possible be crashing?

    But after drilling down later it was the 2nd delegate that I had forgotten about that was get collected.