Attaching to Process after Hang and do a live Debug.
Just use the Debugger (either the Visual Studio debugger, or WinDBG) to "Attach to the process" after it's got into the hanged state....then you can "break" into your process.
Then look at the stacks of all the threads to see what they are doing...you might spot something a particular thread is doing that is causing deadlock.
If you use WinDBG with the SOSEX plugin, you might even be able to use the !dlk command ... which can sometimes automatically find any deadlocks.
There are lots of other debugger commands you could to check whats going on e.g. look at the Garbage Collector, Finalization Queue (e.g. might have blocked Finalizer Thread), etc.
Change Process Priority and/or Processor Affinity
Now I know you said you that the whole system hangs, so this might be tricky/impossible.....one thing you could try is lowering the process Priority on your process and/or set the Processor Affinity before it gets to the hanged state...this should give you a better chance to load your debugger on the same machine because in theory your stopping your process from monopolising the CPU so much.
Go to task manager right click on the process and do Set Priority Low or Set Affinity.
Now it might be the case that changing the priority actually has a side-effect in that your application no longer hangs or behaves differently....but if this happens at the very least this has given you a clue as to the problem.
If it still hangs (but not as much) and you are able to just about run your Debugger...then hey presto you can now attach to your process, and break it.
Kernel Debugging with WinDBG
If the above doesn't work then its time to install WinDBG on the machine crashing, set up Windows to boot into Kernel Debugging mode, and then from another machine (connected via USB, Firewire, etc) you use the WinDBG client to talk to your hanged machines back-end debug engine, and this will let you break into the system and analyse the machines state.
Creating a Crash Dump (.dmp) and Post-Mortem Analysing
Another technique is to capture the process state via a Crash Dump (which you can create by right clicking on the process name in Task Manager and saying Create Dump, or by using other tools to do that for you.). You can then load the .dmp file into Visual Studio (by using File Open), or in WinDBG (by using Open Crash Dump). You can then look at certain states of that process.
This can be automated with various tools e.g. ADPlus, so that the Crash Dump is automatically done for you when your application becomes unresponsive....if you're lucky this may work in an automated way even when your system is in the hanged system....or it may not.
Note it's preferable to do a live debug rather than a crash dump if possible as you have access to more information/state.
Managed Debug Assistants
It's possible to run a .NET application with the CLR runtime performing extra checks to verify you are doing things in the corect way. These checks are called Managed Debug Assistants. When an MDA finds a problem it raises an exception in your program.
They can be turned on by using some configuration switches in a "myapplication.mda.config", or through the Exceptions dialog in the Debug menu (there's a branch in the tree dedicated to them). One caveat, don't just turn all of them on, because some of them are very aggressive and might lead to red herrings...you need to really read up on each MDA and understand what check is is doing.