This is the trick that kept me up until 4AM and I still didn't figure out what to do until just before work the next morning.

I can't just do a callback into this AppDomain and have it unload the child AppDomain, because the child AppDomain would still be in the call stack executing code. Instead, I do the callback, and in the callback spin up another thread whose only job is to watch the RobotContainer and wait for it to finish shutting down. Once the new thread detects that the robot is done, it waits another second (probably not necessary, but I prefer to err on the side of caution) and returns.

Because DoHardReset is waiting on this new thread to return, we've managed to sever the connection between the parent and child AppDomains, and the child AppDomain is no longer executing code. We can now unload it.