void EnterCallback(PausedReason pausedReason, string name, ICorDebugProcess pProcess)
		{
			process.TraceMessage("Callback: " + name);
			System.Diagnostics.Debug.Assert(process.CorProcess == pProcess);
			// Check state
			if (process.IsRunning ||
				// After break is pressed we may receive some messages that were already queued
				process.PauseSession.PausedReason == PausedReason.ForcedBreak ||
				// ExitProcess may be called at any time when debuggee is killed
				name == "ExitProcess") {
				
				if (process.IsPaused && process.PauseSession.PausedReason == PausedReason.ForcedBreak && name != "ExitProcess") {
					process.TraceMessage("Processing post-break callback");
					// Continue the break, process is still breaked because of the callback
					process.Continue();
					pauseProcessInsteadOfContinue = true;
				} else {
					pauseProcessInsteadOfContinue = false;
				}
				
				// Remove expired threads and functions
				foreach(Thread thread in process.Threads) {
					thread.CheckExpiration();
				}
				
				process.NotifyPaused(new PauseSession(pausedReason));
			} else {
				throw new DebuggerException("Invalid state at the start of callback");
			}
		}
		void EnterCallback(PausedReason pausedReason, string name, ICorDebugProcess pProcess)
		{
			isInCallback = true;
			
			process.TraceMessage("Callback: " + name);
			System.Diagnostics.Debug.Assert(process.CorProcess == pProcess);
			
			// After break is pressed we may receive some messages that were already queued
			if (process.IsPaused && process.PauseSession.PausedReason == PausedReason.ForcedBreak) {
				process.TraceMessage("Processing post-break callback");
				// This compensates for the break call and we are in normal callback handling mode
				process.AsyncContinue(DebuggeeStateAction.Keep);
				// Start of call back - create new pause session (as usual)
				process.NotifyPaused(pausedReason);
				// Make sure we stay pause after the callback is handled
				pauseOnNextExit = true;
				return;
			}
			
			if (process.IsRunning) {
				process.NotifyPaused(pausedReason);
				return;
			}
			
			throw new DebuggerException("Invalid state at the start of callback");
		}
		public ManagedCallback GetProcessCallbackInterface(string name, ICorDebugProcess pProcess)
		{
			Process process;
			// We have to wait until the created process is added into the collection
			lock(debugger.ProcessIsBeingCreatedLock) {
				process = debugger.GetProcess(pProcess);
			}
			// Make *really* sure the process is not dead
			if (process == null) {
				debugger.TraceMessage("Ignoring callback \"" + name + "\": Process not found");
				return null;
			}
			if (process.HasExited) {
				debugger.TraceMessage("Ignoring callback \"" + name + "\": Process has exited");
				return null;
			}
			if (process.TerminateCommandIssued && !(name == "ExitProcess")) {
				debugger.TraceMessage("Ignoring callback \"" + name + "\": Terminate command was issued for the process");
				return null;
			}
			// Check that the process is not exited
			try {
				int isRunning = process.CorProcess.IsRunning;
			} catch (COMException e) {
				process.TraceMessage("Ignoring callback \"" + name + "\": " + e.Message);
				return null;
			}
			return process.CallbackInterface;
		}
		internal Process(NDebugger debugger, ICorDebugProcess corProcess)
		{
			this.debugger = debugger;
			this.corProcess = corProcess;
			
			this.callbackInterface = new ManagedCallback(this);
		}
		internal Process GetProcess(ICorDebugProcess corProcess)
		{
			foreach (Process process in Processes) {
				if (process.CorProcess == corProcess) {
					return process;
				}
			}
			return null;
		}
		internal Process GetProcess(ICorDebugProcess corProcess)
		{
			foreach (Process process in Processes) {
				if (process.CorProcess == corProcess) {
					return process;
				}
			}
			return null;
			//throw new DebuggerException("Process is not in collection");
		}
		public ManagedCallback GetProcessCallbackInterface(ICorDebugProcess pProcess)
		{
			Process process = debugger.GetProcess(pProcess);
			if (process == null)
				return null;
			try {
				int isRunning = process.CorProcess.IsRunning;
			} catch (COMException e) {
				// 0x80131301: Process was terminated
				if ((uint)e.ErrorCode == 0x80131301) {
					process.TraceMessage("Ingoring callback of exited process");
					return null;
				}
			}
			return process.CallbackInterface;
		}
		public void DebuggerError(ICorDebugProcess pProcess, int errorHR, uint errorCode)
		{
			ManagedCallback managedCallback = GetProcessCallbackInterface("DebuggerError", pProcess);
			if (managedCallback != null) {
				managedCallback.DebuggerError(pProcess, errorHR, errorCode);
			}
		}
		public void ControlCTrap(ICorDebugProcess pProcess)
		{
			ManagedCallback managedCallback = GetProcessCallbackInterface("ControlCTrap", pProcess);
			if (managedCallback != null) {
				managedCallback.ControlCTrap(pProcess);
			}
		}
		public void DestroyConnection(ICorDebugProcess pProcess, uint dwConnectionId)
		{
			EnterCallback(PausedReason.Other, "DestroyConnection", pProcess);
			
			ExitCallback_Continue();
		}
		public void CreateConnection(ICorDebugProcess pProcess, uint dwConnectionId, IntPtr pConnName)
		{
			EnterCallback(PausedReason.Other, "CreateConnection", pProcess);
			
			ExitCallback_Continue();
		}
		public void ExitProcess(ICorDebugProcess pProcess)
		{
			// ExitProcess may be called at any time when debuggee is killed
			process.TraceMessage("Callback: ExitProcess");
			
			process.NotifyHasExited();
		}
		public void ChangeConnection(ICorDebugProcess pProcess, uint dwConnectionId)
		{
			ManagedCallback managedCallback = GetProcessCallbackInterface(pProcess);
			if (managedCallback != null) {
				managedCallback.ChangeConnection(pProcess, dwConnectionId);
			}
		}
		public void CreateConnection(ICorDebugProcess pProcess, uint dwConnectionId, IntPtr pConnName)
		{
			ManagedCallback managedCallback = GetProcessCallbackInterface("CreateConnection", pProcess);
			if (managedCallback != null) {
				managedCallback.CreateConnection(pProcess, dwConnectionId, pConnName);
			}
		}
		public void ExitProcess(ICorDebugProcess pProcess)
		{
			ManagedCallback managedCallback = GetProcessCallbackInterface(pProcess);
			if (managedCallback != null) {
				managedCallback.ExitProcess(pProcess);
			}
		}
		public void CreateProcess(ICorDebugProcess pProcess)
		{
			EnterCallback(PausedReason.Other, "CreateProcess", pProcess);

			// Process is added in NDebugger.Start

			ExitCallback_Continue();
		}
		public void DebuggerError(ICorDebugProcess pProcess, int errorHR, uint errorCode)
		{
			EnterCallback(PausedReason.DebuggerError, "DebuggerError", pProcess);

			string errorText = String.Format("Debugger error: \nHR = 0x{0:X} \nCode = 0x{1:X}", errorHR, errorCode);
			
			if ((uint)errorHR == 0x80131C30) {
				errorText += "\n\nIf you are running a 64-bit system this setting might help:\nProject -> Project Options -> Compiling -> Target CPU = 32-bit Intel";
			}
			
			System.Windows.Forms.MessageBox.Show(errorText);

			ExitCallback_Paused();
		}
		public void ControlCTrap(ICorDebugProcess pProcess)
		{
			EnterCallback(PausedReason.ControlCTrap, "ControlCTrap", pProcess);

			ExitCallback_Paused();
		}
		public void ControlCTrap(ICorDebugProcess pProcess)
		{
			EnterCallback(PausedReason.ControlCTrap, "ControlCTrap", pProcess);

			pauseOnNextExit = true;
			ExitCallback();
		}
		public void ChangeConnection(ICorDebugProcess pProcess, uint dwConnectionId)
		{
			EnterCallback(PausedReason.Other, "ChangeConnection", pProcess);
			
			ExitCallback();
		}
		public void CreateProcess(ICorDebugProcess pProcess)
		{
			ManagedCallback managedCallback = GetProcessCallbackInterface("CreateProcess", pProcess);
			if (managedCallback != null) {
				managedCallback.CreateProcess(pProcess);
			}
		}
		public void ExitAppDomain(ICorDebugProcess pProcess, ICorDebugAppDomain pAppDomain)
		{
			ManagedCallback managedCallback = GetProcessCallbackInterface("ExitAppDomain", pProcess);
			if (managedCallback != null) {
				managedCallback.ExitAppDomain(pProcess, pAppDomain);
			}
		}
		public void ExitAppDomain(ICorDebugProcess pProcess, ICorDebugAppDomain pAppDomain)
		{
			EnterCallback(PausedReason.Other, "ExitAppDomain", pAppDomain);
			
			ExitCallback_Continue();
		}
		public void DestroyConnection(ICorDebugProcess pProcess, uint dwConnectionId)
		{
			ManagedCallback managedCallback = GetProcessCallbackInterface("DestroyConnection", pProcess);
			if (managedCallback != null) {
				managedCallback.DestroyConnection(pProcess, dwConnectionId);
			}
		}
		public void ExitProcess(ICorDebugProcess pProcess)
		{
			EnterCallback(PausedReason.Other, "ExitProcess", pProcess);
			process.NotifyHasExpired();
		}
		public void CreateAppDomain(ICorDebugProcess pProcess, ICorDebugAppDomain pAppDomain)
		{
			EnterCallback(PausedReason.Other, "CreateAppDomain", pAppDomain);

			pAppDomain.Attach();

			ExitCallback_Continue();
		}
		public void DebuggerError(ICorDebugProcess pProcess, int errorHR, uint errorCode)
		{
			EnterCallback(PausedReason.DebuggerError, "DebuggerError", pProcess);

			string errorText = String.Format("Debugger error: \nHR = 0x{0:X} \nCode = 0x{1:X}", errorHR, errorCode);
			
			if ((uint)errorHR == 0x80131C30) {
				errorText += "\n\nDebugging 64-bit processes is currently not supported.\n" +
					"If you are running a 64-bit system, this setting might help:\n" +
					"Project -> Project Options -> Compiling -> Target CPU = 32-bit Intel";
			}
			
			if (Environment.UserInteractive)
				System.Windows.Forms.MessageBox.Show(errorText);
			else
				throw new DebuggerException(errorText);

			try {
				pauseOnNextExit = true;
				ExitCallback();
			} catch (COMException) {
			} catch (InvalidComObjectException) {
				// ignore errors during shutdown after debugger error
			}
		}