// // // // // $Revision: 2285 $ // using System; using System.Collections.Generic; using Microsoft.Win32.SafeHandles; using Debugger.Wrappers.CorDebug; using System.Diagnostics; using System.Runtime.InteropServices; using System.ComponentModel; using System.ComponentModel.Design; using System.IO; using System.Text; namespace Debugger { [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto, Pack=8), ComVisible(false)] public class STARTUPINFO { public int cb; public string lpReserved; public string lpDesktop; public string lpTitle; public int dwX; public int dwY; public int dwXSize; public int dwYSize; public int dwXCountChars; public int dwYCountChars; public int dwFillAttribute; public int dwFlags; public short wShowWindow; public short cbReserved2; public IntPtr lpReserved2; public SafeFileHandle hStdInput; public SafeFileHandle hStdOutput; public SafeFileHandle hStdError; } public delegate void DataReceivedEventHandler(object sender, DataReceivedEventArgs e); public partial class Process: DebuggerObject, IExpirable { NDebugger debugger; ICorDebugProcess corProcess; ManagedCallback callbackInterface; Thread selectedThread; PauseSession pauseSession; bool hasExpired = false; public event EventHandler Expired; public event DataReceivedEventHandler OutputDataReceived; public event DataReceivedEventHandler ErrorDataReceived; public bool HasExpired { get { return hasExpired; } } private StreamWriter standardInput; public StreamWriter StandardInput { get { return standardInput; } set { standardInput = value; } } private StreamReader standardOutput; public StreamReader StandardOutput { get { return standardOutput; } set { standardOutput = value; } } private StreamReader standardError; public StreamReader StandardError { get { return standardError; } set { standardError = value; } } internal void NotifyHasExpired() { if(!hasExpired) { hasExpired = true; if (Expired != null) { Expired(this, new ProcessEventArgs(this)); } // if (PausedReason == PausedReason.Exception) { // ExceptionEventArgs args = new ExceptionEventArgs(this, SelectedThread.CurrentException); // OnExceptionThrown(args); //// if (args.Continue) { //// this.Continue(); //// } // } debugger.RemoveProcess(this); } } /// /// Indentification of the current debugger session. This value changes whenever debugger is continued /// public PauseSession PauseSession { get { return pauseSession; } } public void NotifyPaused(PauseSession pauseSession) { this.pauseSession = pauseSession; } public NDebugger Debugger { get { return debugger; } } internal ManagedCallback CallbackInterface { get { return callbackInterface; } } internal Process(NDebugger debugger, ICorDebugProcess corProcess) { this.debugger = debugger; this.corProcess = corProcess; this.callbackInterface = new ManagedCallback(this); } internal ICorDebugProcess CorProcess { get { return corProcess; } } public uint Id { get { return corProcess.ID; } } public Thread SelectedThread { get { return selectedThread; } set { selectedThread = value; } } static public Process CreateProcess(NDebugger debugger, string filename, string workingDirectory, string arguments) { return debugger.MTA2STA.Call(delegate{ return StartInternal(debugger, filename, workingDirectory, arguments); }); } private static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, int nSize) { if ((!CreatePipe(out hReadPipe, out hWritePipe, lpPipeAttributes, nSize) || hReadPipe.IsInvalid) || hWritePipe.IsInvalid) { throw new Win32Exception(); } } [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SECURITY_ATTRIBUTES lpPipeAttributes, int nSize); [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern int GetConsoleCP(); [DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern int GetConsoleOutputCP(); [DllImport("kernel32.dll", CharSet=CharSet.Ansi, SetLastError=true)] private static extern IntPtr GetCurrentProcess(); [DllImport("kernel32.dll", CharSet=CharSet.Ansi, SetLastError=true)] private static extern bool DuplicateHandle(HandleRef hSourceProcessHandle, SafeHandle hSourceHandle, HandleRef hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess, bool bInheritHandle, int dwOptions); [StructLayout(LayoutKind.Sequential)] internal class SECURITY_ATTRIBUTES { public int nLength; public int lpSecurityDescriptor; public bool bInheritHandle; } private static void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs) { SECURITY_ATTRIBUTES lpPipeAttributes = new SECURITY_ATTRIBUTES(); lpPipeAttributes.bInheritHandle = true; SafeFileHandle hWritePipe = null; parentHandle = null; try { if (parentInputs) { CreatePipeWithSecurityAttributes(out childHandle, out hWritePipe, lpPipeAttributes, 0); parentHandle = hWritePipe; } else { CreatePipeWithSecurityAttributes(out hWritePipe, out childHandle, lpPipeAttributes, 0); parentHandle = hWritePipe; } // if (!DuplicateHandle(new HandleRef(this, GetCurrentProcess()), hWritePipe, new HandleRef(this, GetCurrentProcess()), out parentHandle, 0, false, 2)) // { // throw new Win32Exception(); // } } finally { // if ((hWritePipe != null) && !hWritePipe.IsInvalid) // { // hWritePipe.Close(); // } } } static unsafe Process StartInternal(NDebugger debugger, string filename, string workingDirectory, string arguments) { debugger.TraceMessage("Executing " + filename); uint[] processStartupInfo = new uint[17]; processStartupInfo[0] = sizeof(uint) * 17; uint[] processInfo = new uint[4]; SafeFileHandle prnt_inp; SafeFileHandle prnt_out; SafeFileHandle prnt_err; SafeFileHandle input_handle; SafeFileHandle output_handle; SafeFileHandle err_handle; // CreatePipe(out prnt_out,out output_handle,false); // CreatePipe(out prnt_inp,out input_handle,true); STARTUPINFO startupInfo = new STARTUPINFO(); startupInfo.cb = Marshal.SizeOf(startupInfo); SECURITY_ATTRIBUTES lpPipeAttributes = new SECURITY_ATTRIBUTES(); lpPipeAttributes.bInheritHandle = true; //CreatePipe(out output_handle, out input_handle,lpPipeAttributes,0); CreatePipe(out prnt_inp, out input_handle, true); CreatePipe(out prnt_out, out output_handle, false); CreatePipe(out prnt_err, out err_handle, false); startupInfo.hStdInput = input_handle; startupInfo.hStdOutput = output_handle; startupInfo.hStdError = err_handle; ICorDebugProcess outProcess; // processStartupInfo[14] = (uint)GCHandle.Alloc(input_handle,GCHandleType.Pinned).AddrOfPinnedObject().ToInt32(); // processStartupInfo[15] = (uint)GCHandle.Alloc(output_handle,GCHandleType.Pinned).AddrOfPinnedObject().ToInt32(); StreamWriter standardInput = new StreamWriter(new FileStream(prnt_inp, FileAccess.Write, 0x1000, false), Encoding.GetEncoding(GetConsoleCP()), 0x1000); standardInput.AutoFlush = true; Encoding encoding = Encoding.GetEncoding(GetConsoleOutputCP()); StreamReader standardOutput = new StreamReader(new FileStream(prnt_out, FileAccess.Read, 0x1000, false), encoding, true, 0x1000); StreamReader errorOutput = new StreamReader(new FileStream(prnt_err, FileAccess.Read, 0x1000, false), encoding, true, 0x1000); if (workingDirectory == null || workingDirectory == "") { workingDirectory = System.IO.Path.GetDirectoryName(filename); } fixed (uint* pprocessStartupInfo = processStartupInfo) fixed (uint* pprocessInfo = processInfo) outProcess = debugger.CorDebug.CreateProcess( filename, // lpApplicationName // If we do not prepend " ", the first argument migh just get lost " " + arguments, // lpCommandLine ref _SECURITY_ATTRIBUTES.Default, // lpProcessAttributes ref _SECURITY_ATTRIBUTES.Default, // lpThreadAttributes 1,//TRUE // bInheritHandles //0x00000010 /*CREATE_NEW_CONSOLE*/, // dwCreationFlags 0x08000000, IntPtr.Zero, // lpEnvironment workingDirectory, // lpCurrentDirectory startupInfo, //(uint)pprocessStartupInfo, // lpStartupInfo (uint)pprocessInfo, // lpProcessInformation, CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS // debuggingFlags ); Process p = new Process(debugger, outProcess); p.StandardInput = standardInput; p.StandardOutput = standardOutput; p.StandardError = errorOutput; return p; } public void Break() { AssertRunning(); corProcess.Stop(100); // TODO: Hardcoded value pauseSession = new PauseSession(PausedReason.ForcedBreak); // TODO: Code duplication from enter callback // Remove expired threads and functions foreach(Thread thread in this.Threads) { thread.CheckExpiration(); } Pause(true); } public void Continue() { try { AssertPaused(); pauseSession.NotifyHasExpired(); pauseSession = null; OnDebuggingResumed(); corProcess.Continue(0); } catch(System.Exception e) { } } public void Terminate() { // Resume stoped tread if (this.IsPaused) { // We might get more callbacks so we should maintain consistent sate this.Continue(); // TODO: Remove this... } // Expose race condition - drain callback queue System.Threading.Thread.Sleep(0); // Stop&terminate - both must be called corProcess.Stop(100); // TODO: ...and this corProcess.Terminate(0); } public bool IsRunning { get { return pauseSession == null; } } public bool IsPaused { get { return !IsRunning; } } public void AssertPaused() { if (IsRunning) { throw new DebuggerException("Process is not paused."); } } public void AssertRunning() { if (IsPaused) { throw new DebuggerException("Process is not running."); } } public string DebuggeeVersion { get { return debugger.DebuggeeVersion; } } /// /// Fired when System.Diagnostics.Trace.WriteLine() is called in debuged process /// public event EventHandler LogMessage; protected internal virtual void OnLogMessage(MessageEventArgs arg) { TraceMessage ("Debugger event: OnLogMessage"); if (LogMessage != null) { LogMessage(this, arg); } } public void TraceMessage(string message) { System.Diagnostics.Debug.WriteLine("Debugger:" + message); debugger.OnDebuggerTraceMessage(new MessageEventArgs(this, message)); } public SourcecodeSegment NextStatement { get { if (SelectedFunction == null || IsRunning) { return null; } else { return SelectedFunction.NextStatement; } } } public NamedValueCollection LocalVariables { get { if (SelectedFunction == null || IsRunning) { return NamedValueCollection.Empty; } else { return SelectedFunction.Variables; } } } /// Gets value of given name which is accessible from selected function /// Null if not found public NamedValue GetValue(string name) { if (SelectedFunction == null || IsRunning) { return null; } else { return SelectedFunction.GetValue(name); } } } }