public static DBG.DebugProcessOptions Convert(DebugOptions options, IDebuggerSettings settings, DBG.CLRTypeDebugInfo info) { if (options == null) throw new ArgumentNullException(); var o = new DBG.DebugProcessOptions(info); o.Filename = options.Filename; o.CommandLine = options.CommandLine; o.CurrentDirectory = options.CurrentDirectory; o.DebugMessageDispatcher = WpfDebugMessageDispatcher.Instance; o.BreakProcessKind = Convert(options.BreakProcessKind); o.DebugOptions.IgnoreBreakInstructions = settings.IgnoreBreakInstructions; return o; }
public static DBG.DebugProcessOptions Convert(DebugOptions options, IDebuggerSettings settings, DBG.CLRTypeDebugInfo info) { if (options == null) { throw new ArgumentNullException(); } var o = new DBG.DebugProcessOptions(info); o.Filename = options.Filename; o.CommandLine = options.CommandLine; o.CurrentDirectory = options.CurrentDirectory; o.DebugMessageDispatcher = WpfDebugMessageDispatcher.Instance; o.BreakProcessKind = Convert(options.BreakProcessKind); o.DebugOptions.IgnoreBreakInstructions = settings.IgnoreBreakInstructions; return(o); }
void CreateProcess(DebugProcessOptions options) { ICorDebugProcess comProcess; try { var dwCreationFlags = options.ProcessCreationFlags ?? DebugProcessOptions.DefaultProcessCreationFlags; var si = new STARTUPINFO(); si.cb = (uint)(4 * 1 + IntPtr.Size * 3 + 4 * 8 + 2 * 2 + IntPtr.Size * 4); var pi = new PROCESS_INFORMATION(); // We must add the space here if the beginning of the command line appears to be // the path to a file, eg. "/someOption" or "c:blah" or it won't be passed to the // debugged program. var cmdline = " " + (options.CommandLine ?? string.Empty); corDebug.CreateProcess(options.Filename ?? string.Empty, cmdline, IntPtr.Zero, IntPtr.Zero, options.InheritHandles ? 1 : 0, dwCreationFlags, IntPtr.Zero, options.CurrentDirectory, ref si, ref pi, CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS, out comProcess); // We don't need these NativeMethods.CloseHandle(pi.hProcess); NativeMethods.CloseHandle(pi.hThread); } catch { ProcessesTerminated(); throw; } var process = TryAdd(comProcess); if (process != null) process.Initialize(false, options.Filename, options.CurrentDirectory, options.CommandLine); }
public unsafe static DnDebugger CreateDnDebugger(DebugProcessOptions options, CoreCLRTypeDebugInfo info, IntPtr outputHandle, IntPtr errorHandle, Func <bool> keepWaiting, Func <ICorDebug, string, uint, string?, DnDebugger> createDnDebugger) { var dbgShimState = GetOrCreateDbgShimState(info.HostFilename, info.DbgShimFilename); if (dbgShimState is null) { throw new Exception($"Could not load {dbgshimFilename}: '{info.DbgShimFilename}' . Make sure you use the {IntPtr.Size * 8}-bit version"); } var startupEvent = IntPtr.Zero; var hThread = IntPtr.Zero; IntPtr pHandleArray = IntPtr.Zero, pStringArray = IntPtr.Zero; uint dwArrayLength = 0; bool useHost = !(info.HostFilename is null); var pi = new PROCESS_INFORMATION(); bool error = true, calledSetEvent = false; try { bool inheritHandles = options.InheritHandles; var dwCreationFlags = options.ProcessCreationFlags ?? DebugProcessOptions.DefaultProcessCreationFlags; dwCreationFlags |= ProcessCreationFlags.CREATE_SUSPENDED; var si = new STARTUPINFO(); si.hStdOutput = outputHandle; si.hStdError = errorHandle; if (si.hStdOutput != IntPtr.Zero || si.hStdError != IntPtr.Zero) { si.dwFlags |= STARTUPINFO.STARTF_USESTDHANDLES; inheritHandles = true; } si.cb = (uint)(4 * 1 + IntPtr.Size * 3 + 4 * 8 + 2 * 2 + IntPtr.Size * 4); string cmdline; if (useHost) { cmdline = "\"" + info.HostFilename + "\" " + info.HostCommandLine + " \"" + options.Filename + "\"" + (string.IsNullOrEmpty(options.CommandLine) ? string.Empty : " " + options.CommandLine); } else { cmdline = "\"" + options.Filename + "\"" + (string.IsNullOrEmpty(options.CommandLine) ? string.Empty : " " + options.CommandLine); } var env = Win32EnvironmentStringBuilder.CreateEnvironmentUnicodeString(options.Environment !); dwCreationFlags |= ProcessCreationFlags.CREATE_UNICODE_ENVIRONMENT; var appName = useHost ? info.HostFilename : options.Filename; bool b = NativeMethods.CreateProcess(appName ?? string.Empty, cmdline, IntPtr.Zero, IntPtr.Zero, inheritHandles, dwCreationFlags, env, options.CurrentDirectory, ref si, out pi); hThread = pi.hThread; if (!b) { throw new Exception($"Could not execute '{options.Filename}'"); } int hr = dbgShimState.GetStartupNotificationEvent !(pi.dwProcessId, out startupEvent); if (hr < 0) { throw new Exception($"GetStartupNotificationEvent failed: 0x{hr:X8}"); } NativeMethods.ResumeThread(hThread); const uint WAIT_MS = 5000; for (;;) { uint res = NativeMethods.WaitForSingleObject(startupEvent, WAIT_MS); if (res == 0) { break; } if (res == NativeMethods.WAIT_FAILED) { throw new Exception($"Error waiting for startup event: 0x{Marshal.GetLastWin32Error():X8}"); } if (res == NativeMethods.WAIT_TIMEOUT) { if (keepWaiting()) { continue; } throw new TimeoutException("Waiting for CoreCLR timed out. Debug 32-bit .NET Core apps with 32-bit dnSpy (dnSpy-x86.exe), and 64-bit .NET Core apps with 64-bit dnSpy (dnSpy.exe)."); } Debug.Fail($"Unknown result from WaitForMultipleObjects: 0x{res:X8}"); throw new Exception("Error waiting for startup event"); } hr = dbgShimState.EnumerateCLRs !(pi.dwProcessId, out pHandleArray, out pStringArray, out dwArrayLength); if (hr < 0 || dwArrayLength == 0) { // CoreCLR doesn't give us a good error code if we try to debug a .NET Core app // with an incompatible bitness: // x86 tries to debug x64: hr == 0x8007012B (ERROR_PARTIAL_COPY) // x64 tries to debug x86: hr == 0x00000000 && dwArrayLength == 0x00000000 if (IntPtr.Size == 4 && (uint)hr == 0x8007012B) { throw new StartDebuggerException(StartDebuggerError.UnsupportedBitness); } if (IntPtr.Size == 8 && hr == 0 && dwArrayLength == 0) { throw new StartDebuggerException(StartDebuggerError.UnsupportedBitness); } throw new Exception("Process started but no CoreCLR found"); } var psa = (IntPtr *)pStringArray; var pha = (IntPtr *)pHandleArray; const int index = 0; var version = GetVersionStringFromModule(dbgShimState, pi.dwProcessId, psa[index], out string coreclrFilename); hr = dbgShimState.CreateDebuggingInterfaceFromVersionEx !(CorDebugInterfaceVersion.CorDebugVersion_4_0, version, out object obj); var corDebug = obj as ICorDebug; if (corDebug is null) { throw new Exception($"Could not create a ICorDebug: hr=0x{hr:X8}"); } var dbg = createDnDebugger(corDebug, coreclrFilename, pi.dwProcessId, version); for (uint i = 0; i < dwArrayLength; i++) { NativeMethods.SetEvent(pha[i]); } calledSetEvent = true; error = false; return(dbg); } finally { if (!calledSetEvent && pHandleArray != IntPtr.Zero && dwArrayLength != 0) { var pha = (IntPtr *)pHandleArray; for (uint i = 0; i < dwArrayLength; i++) { NativeMethods.SetEvent(pha[i]); } } if (startupEvent != IntPtr.Zero) { NativeMethods.CloseHandle(startupEvent); } if (hThread != IntPtr.Zero) { NativeMethods.CloseHandle(hThread); } if (pHandleArray != IntPtr.Zero) { dbgShimState.CloseCLREnumeration !(pHandleArray, pStringArray, dwArrayLength); } if (error) { NativeMethods.TerminateProcess(pi.hProcess, uint.MaxValue); } if (pi.hProcess != IntPtr.Zero) { NativeMethods.CloseHandle(pi.hProcess); } } }
void DnDebugger_OnProcessStateChanged(object sender, DebuggerEventArgs e) { if (debugger == null || sender != debugger) return; CallOnProcessStateChanged(sender, e); if (debugger.ProcessState == DebuggerProcessState.Terminated) { lastDebugProcessOptions = null; RemoveDebugger(); } // This is sometimes needed. Press Ctrl+Shift+F5 a couple of times and the toolbar // debugger icons aren't updated until you release Ctrl+Shift. if (ProcessState == DebuggerProcessState.Stopped || !IsDebugging) CommandManager.InvalidateRequerySuggested(); if (ProcessState == DebuggerProcessState.Stopped) ShowExceptionMessage(); }
bool DebugProcess(DebugProcessOptions options) { if (IsDebugging) return false; if (options == null) return false; RemoveDebugger(); DnDebugger newDebugger; try { newDebugger = DnDebugger.DebugProcess(options); } catch (Exception ex) { var cex = ex as COMException; const int ERROR_NOT_SUPPORTED = unchecked((int)0x80070032); if (cex != null && cex.ErrorCode == ERROR_NOT_SUPPORTED) MainWindow.Instance.ShowMessageBox(string.Format("Could not start the debugger. {0}", GetIncompatiblePlatformErrorMessage())); else if (cex != null && cex.ErrorCode == CordbgErrors.CORDBG_E_UNCOMPATIBLE_PLATFORMS) MainWindow.Instance.ShowMessageBox(string.Format("Could not start the debugger. {0}", GetIncompatiblePlatformErrorMessage())); else if (cex != null && cex.ErrorCode == unchecked((int)0x800702E4)) MainWindow.Instance.ShowMessageBox("Could not start the debugger. The debugged program requires admin privileges. Restart dnSpy with admin rights and try again."); else MainWindow.Instance.ShowMessageBox(string.Format("Could not start the debugger. Make sure you have access to the file '{0}'\n\nError: {1}", options.Filename, ex.Message)); return false; } Initialize(newDebugger); return true; }
void DebugAssembly(DebugProcessOptions options) { if (options == null) return; var optionsCopy = options.Clone(); if (!DebugProcess(options)) return; lastDebugProcessOptions = optionsCopy; }
public static DnDebugger DebugProcess(DebugProcessOptions options) { if (options.DebugMessageDispatcher == null) throw new ArgumentException("DebugMessageDispatcher is null"); var debuggeeVersion = options.DebuggeeVersion ?? DebuggeeVersionDetector.GetVersion(options.Filename); var dbg = new DnDebugger(CreateCorDebug(debuggeeVersion), options.DebugOptions, options.DebugMessageDispatcher, debuggeeVersion); if (options.BreakProcessType != BreakProcessType.None) new BreakProcessHelper(dbg, options.BreakProcessType, options.Filename); dbg.CreateProcess(options); return dbg; }
static DnDebugger CreateDnDebuggerCoreCLR(DebugProcessOptions options) { var clrType = (CoreCLRTypeDebugInfo)options.CLRTypeDebugInfo; var dbg2 = CoreCLRHelper.CreateDnDebugger(options, clrType, () => false, (cd, pid) => { var dbg = new DnDebugger(cd, options.DebugOptions, options.DebugMessageDispatcher, null); if (options.BreakProcessType != BreakProcessType.None) new BreakProcessHelper(dbg, options.BreakProcessType, options.Filename); ICorDebugProcess comProcess; cd.DebugActiveProcess((int)pid, 0, out comProcess); var dnProcess = dbg.TryAdd(comProcess); if (dnProcess != null) dnProcess.Initialize(false, options.Filename, options.CurrentDirectory, options.CommandLine); return dbg; }); if (dbg2 == null) throw new Exception("Could not create a debugger instance"); return dbg2; }
public unsafe static DnDebugger CreateDnDebugger(DebugProcessOptions options, CoreCLRTypeDebugInfo info, Func <bool> keepWaiting, Func <ICorDebug, uint, DnDebugger> createDnDebugger) { var dbgShimState = GetOrCreateDbgShimState(info.HostFilename, info.DbgShimFilename); if (dbgShimState == null) { throw new Exception(string.Format("Could not load dbgshim.dll: '{0}' . Make sure you use the {1}-bit version", info.DbgShimFilename, IntPtr.Size * 8)); } IntPtr startupEvent = IntPtr.Zero; IntPtr hThread = IntPtr.Zero; IntPtr pHandleArray = IntPtr.Zero, pStringArray = IntPtr.Zero; uint dwArrayLength = 0; string version = null; var pi = new PROCESS_INFORMATION(); bool error = true; try { var dwCreationFlags = options.ProcessCreationFlags ?? DebugProcessOptions.DefaultProcessCreationFlags; dwCreationFlags |= ProcessCreationFlags.CREATE_SUSPENDED; var si = new STARTUPINFO(); si.cb = (uint)(4 * 1 + IntPtr.Size * 3 + 4 * 8 + 2 * 2 + IntPtr.Size * 4); var cmdline = " " + (info.HostCommandLine ?? string.Empty) + " \"" + options.Filename + "\" " + (options.CommandLine ?? string.Empty); bool b = NativeMethods.CreateProcess(info.HostFilename ?? string.Empty, cmdline, IntPtr.Zero, IntPtr.Zero, options.InheritHandles, dwCreationFlags, IntPtr.Zero, options.CurrentDirectory, ref si, out pi); hThread = pi.hThread; if (!b) { throw new Exception(string.Format("Could not execute '{0}'", options.Filename)); } int hr = dbgShimState.GetStartupNotificationEvent(pi.dwProcessId, out startupEvent); if (hr < 0) { throw new Exception(string.Format("GetStartupNotificationEvent failed: 0x{0:X8}", hr)); } NativeMethods.ResumeThread(hThread); const uint WAIT_MS = 1000; for (;;) { uint res = NativeMethods.WaitForSingleObject(startupEvent, WAIT_MS); if (res == 0) { break; } if (res == NativeMethods.WAIT_FAILED) { throw new Exception(string.Format("Error waiting for startup event: 0x{0:X8}", Marshal.GetLastWin32Error())); } if (res == NativeMethods.WAIT_TIMEOUT) { if (keepWaiting()) { continue; } throw new TimeoutException("Waiting for CoreCLR timed out"); } Debug.Fail(string.Format("Unknown result from WaitForMultipleObjects: 0x{0:X8}", res)); throw new Exception("Error waiting for startup event"); } hr = dbgShimState.EnumerateCLRs(pi.dwProcessId, out pHandleArray, out pStringArray, out dwArrayLength); if (hr < 0 || dwArrayLength == 0) { throw new Exception("Process started but no CoreCLR found"); } var psa = (IntPtr *)pStringArray; var pha = (IntPtr *)pHandleArray; string moduleFilename; const int index = 0; version = GetVersionStringFromModule(dbgShimState, pi.dwProcessId, psa[index], out moduleFilename); object obj; hr = dbgShimState.CreateDebuggingInterfaceFromVersionEx(CorDebugInterfaceVersion.CorDebugVersion_4_0, version, out obj); var corDebug = obj as ICorDebug; if (corDebug == null) { throw new Exception(string.Format("Could not create a ICorDebug: hr=0x{0:X8}", hr)); } var dbg = createDnDebugger(corDebug, pi.dwProcessId); for (uint i = 0; i < dwArrayLength; i++) { NativeMethods.SetEvent(pha[i]); } error = false; return(dbg); } finally { if (startupEvent != IntPtr.Zero) { NativeMethods.CloseHandle(startupEvent); } if (hThread != IntPtr.Zero) { NativeMethods.CloseHandle(hThread); } if (pHandleArray != IntPtr.Zero) { dbgShimState.CloseCLREnumeration(pHandleArray, pStringArray, dwArrayLength); } if (error) { NativeMethods.TerminateProcess(pi.hProcess, uint.MaxValue); } if (pi.hProcess != IntPtr.Zero) { NativeMethods.CloseHandle(pi.hProcess); } } }
void CreateProcess(DebugProcessOptions options) { ICorDebugProcess comProcess; try { var dwCreationFlags = options.ProcessCreationFlags ?? DebugProcessOptions.DefaultProcessCreationFlags; var si = new STARTUPINFO(); si.cb = (uint)(4 * 1 + IntPtr.Size * 3 + 4 * 8 + 2 * 2 + IntPtr.Size * 4); var pi = new PROCESS_INFORMATION(); var cmdline = "\"" + options.Filename + "\""; if (!string.IsNullOrEmpty(options.CommandLine)) cmdline = cmdline + " " + options.CommandLine; corDebug.CreateProcess(options.Filename ?? string.Empty, cmdline, IntPtr.Zero, IntPtr.Zero, options.InheritHandles ? 1 : 0, dwCreationFlags, IntPtr.Zero, options.CurrentDirectory, ref si, ref pi, CorDebugCreateProcessFlags.DEBUG_NO_SPECIAL_OPTIONS, out comProcess); // We don't need these NativeMethods.CloseHandle(pi.hProcess); NativeMethods.CloseHandle(pi.hThread); } catch { ProcessesTerminated(); throw; } var process = TryAdd(comProcess); if (process != null) process.Initialize(false, options.Filename, options.CurrentDirectory, options.CommandLine); }
bool DebugAssembly2(DebugProcessOptions options, bool isInteractive = true) { if (options == null) return false; var optionsCopy = options.Clone(); if (!DebugProcess(options, isInteractive)) return false; lastDebugProcessOptions = optionsCopy; return true; }
public bool DebugAssembly(DebugProcessOptions options) => DebugAssembly2(options, false);
DebugProcessOptions GetDebugAssemblyOptions(DebugCoreCLRVM vm, bool askUser = true) { if (askUser) { var win = new DebugCoreCLRDlg(); win.DataContext = vm; win.Owner = appWindow.MainWindow; if (win.ShowDialog() != true) return null; } var opts = new DebugProcessOptions(new CoreCLRTypeDebugInfo(vm.DbgShimFilename, vm.HostFilename, vm.HostCommandLine)); opts.DebugMessageDispatcher = WpfDebugMessageDispatcher.Instance; opts.CurrentDirectory = vm.CurrentDirectory; opts.Filename = vm.Filename; opts.CommandLine = vm.CommandLine; opts.BreakProcessKind = vm.BreakProcessKind; lastDebugCoreCLRVM = vm; return opts; }
bool DebugProcess(DebugProcessOptions options, bool isInteractive) { if (IsDebugging) return false; if (options == null) return false; TheDebugger.RemoveDebugger(); DnDebugger newDebugger; try { newDebugger = DnDebugger.DebugProcess(options); } catch (Exception ex) { var cex = ex as COMException; const int ERROR_NOT_SUPPORTED = unchecked((int)0x80070032); string errMsg; if (cex != null && cex.ErrorCode == ERROR_NOT_SUPPORTED) errMsg = string.Format(dnSpy_Debugger_Resources.Error_CouldNotStartDebugger, GetIncompatiblePlatformErrorMessage()); else if (cex != null && cex.ErrorCode == CordbgErrors.CORDBG_E_UNCOMPATIBLE_PLATFORMS) errMsg = string.Format(dnSpy_Debugger_Resources.Error_CouldNotStartDebugger, GetIncompatiblePlatformErrorMessage()); else if (cex != null && cex.ErrorCode == unchecked((int)0x800702E4)) errMsg = dnSpy_Debugger_Resources.Error_CouldNotStartDebuggerRequireAdminPrivLvl; else errMsg = string.Format(dnSpy_Debugger_Resources.Error_CouldNotStartDebuggerCheckAccessToFile, options.Filename, ex.Message); if (isInteractive) messageBoxService.Show(errMsg); return false; } TheDebugger.Initialize(newDebugger); return true; }
public static DnDebugger DebugProcess(DebugProcessOptions options) { if (options.DebugMessageDispatcher == null) throw new ArgumentException("DebugMessageDispatcher is null"); var dbg = CreateDnDebugger(options); if (dbg == null) throw new Exception("Couldn't create a debugger instance"); return dbg; }
static DnDebugger CreateDnDebugger(DebugProcessOptions options) { switch (options.CLRTypeDebugInfo.CLRType) { case CLRType.Desktop: return CreateDnDebuggerDesktop(options); case CLRType.CoreCLR: return CreateDnDebuggerCoreCLR(options); default: Debug.Fail("Invalid CLRType"); throw new InvalidOperationException(); } }
DebugProcessOptions GetDebugAssemblyOptions(DebugProcessVM vm, bool askUser = true) { if (askUser) { var win = new DebugProcessDlg(); win.DataContext = vm; win.Owner = MainWindow.Instance; if (win.ShowDialog() != true) return null; } var opts = new DebugProcessOptions(new DesktopCLRTypeDebugInfo()); opts.DebugMessageDispatcher = WpfDebugMessageDispatcher.Instance; opts.CurrentDirectory = vm.CurrentDirectory; opts.Filename = vm.Filename; opts.CommandLine = vm.CommandLine; opts.BreakProcessType = vm.BreakProcessType; lastDebugProcessVM = vm; return opts; }
static DnDebugger CreateDnDebuggerDesktop(DebugProcessOptions options) { var clrType = (DesktopCLRTypeDebugInfo)options.CLRTypeDebugInfo; var debuggeeVersion = clrType.DebuggeeVersion ?? DebuggeeVersionDetector.GetVersion(options.Filename); var corDebug = CreateCorDebug(debuggeeVersion); if (corDebug == null) throw new Exception("Could not create an ICorDebug instance"); var dbg = new DnDebugger(corDebug, options.DebugOptions, options.DebugMessageDispatcher, debuggeeVersion); if (options.BreakProcessType != BreakProcessType.None) new BreakProcessHelper(dbg, options.BreakProcessType, options.Filename); dbg.CreateProcess(options); return dbg; }
public bool DebugAssembly(DebugProcessOptions options) { return DebugAssembly2(options, false); }