public unsafe static DnDebugger CreateDnDebugger(DebugProcessOptions options, CoreCLRTypeDebugInfo info, Func <bool> keepWaiting, Func <ICorDebug, string, uint, string, 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)); } var startupEvent = IntPtr.Zero; var hThread = IntPtr.Zero; IntPtr pHandleArray = IntPtr.Zero, pStringArray = IntPtr.Zero; uint dwArrayLength = 0; var pi = new PROCESS_INFORMATION(); bool error = true, calledSetEvent = false; 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.HostFilename + "\" " + info.HostCommandLine + " \"" + options.Filename + "\"" + (string.IsNullOrEmpty(options.CommandLine) ? string.Empty : " " + options.CommandLine); var env = Win32EnvironmentStringBuilder.CreateEnvironmentUnicodeString(options.Environment); dwCreationFlags |= ProcessCreationFlags.CREATE_UNICODE_ENVIRONMENT; bool b = NativeMethods.CreateProcess(info.HostFilename ?? string.Empty, cmdline, IntPtr.Zero, IntPtr.Zero, options.InheritHandles, dwCreationFlags, env, 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 = 5000; 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 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(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; 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 == null) { throw new Exception(string.Format("Could not create a ICorDebug: hr=0x{0:X8}", hr)); } 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); } } }
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 == 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 != 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 == 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); } } }