Один проект, над которым я работаю, запускает локальные процессы во время тестирования. Помимо прочего, он запускает экземпляр IISExpress, на котором запущен веб-сайт .NET.
Я могу отладить код веб-сайта, вручную подключив отладчик к процессу IISExpress. Однако я бы хотел автоматизировать этот ручной шаг.
Ниже приведен код, который у меня есть. Кажется, он находит процесс для подключения (т.е. вызывается Attach2
). Однако точки останова в коде веб-сайта по-прежнему не используются даже после вызова Attach2
(они отображаются в виде красного кружка с белой заливкой).
Что я делаю неправильно?
public class DebuggerHelper
{
[DllImport("ole32.dll")]
private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
public static bool TryAttachProcessesToVisualStudioDebuggingCurrentProcess(params int[] processIds)
{
var notAttached = processIds.Length;
var currentProcessId = System.Diagnostics.Process.GetCurrentProcess().Id;
IBindCtx bindCtx = null;
IRunningObjectTable runningObjectTable = null;
IEnumMoniker enumMonikers = null;
try
{
Marshal.ThrowExceptionForHR(CreateBindCtx(0, out bindCtx));
bindCtx.GetRunningObjectTable(out runningObjectTable);
runningObjectTable.EnumRunning(out enumMonikers);
enumMonikers.Reset();
var numFetched = IntPtr.Zero;
var monikers = new IMoniker[1];
while (enumMonikers.Next(1, monikers, numFetched) == 0)
{
monikers[0].GetDisplayName(bindCtx, null, out var runningObjectName);
runningObjectTable.GetObject(monikers[0], out var runningObjectVal);
if (runningObjectVal is EnvDTE80.DTE2 dte
&& runningObjectName.StartsWith("!VisualStudio.DTE.15.0"))
{
foreach (EnvDTE80.Process2 debuggedProcess in dte.Debugger.DebuggedProcesses)
{
if (debuggedProcess.ProcessID == currentProcessId)
{
foreach (EnvDTE80.Process2 localProcess in dte.Debugger.LocalProcesses)
{
if (processIds.Contains(localProcess.ProcessID))
{
localProcess.Attach();
notAttached--;
}
}
}
}
}
}
return notAttached == 0;
}
finally
{
if (enumMonikers != null)
{
Marshal.ReleaseComObject(enumMonikers);
}
if (runningObjectTable != null)
{
Marshal.ReleaseComObject(runningObjectTable);
}
if (bindCtx != null)
{
Marshal.ReleaseComObject(bindCtx);
}
}
}
}
Обновлено: похоже, что он несколько привязан, но что-то не так. Получаю следующее:
Оказалось, что отладчик подключается к IISExpress как собственный процесс, а не как управляемый. После изменения кода, чтобы вместо этого использовать Attach2
с указанным управляемым, он работает, как ожидалось.
Обновленный рабочий код (некоторые другие изменения, вероятно, не нужны и являются только результатом процесса устранения неполадок):
public class DebugHelper
{
[DllImport("ole32.dll")]
static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
[DllImport("ole32.dll")]
static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
public static bool AttachTo(params int[] processIds)
{
var notAttached = processIds.Length;
var maybeDebuggedProcessId = System.Diagnostics.Process.GetCurrentProcess().Id;
IRunningObjectTable runningObjectTable = null;
IEnumMoniker enumMoniker = null;
IBindCtx bindCtx = null;
try
{
CreateBindCtx(0, out bindCtx);
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out enumMoniker);
var monikers = new IMoniker[1];
var numFetched = IntPtr.Zero;
while (enumMoniker.Next(1, monikers, numFetched) == 0)
{
monikers[0].GetDisplayName(bindCtx, null, out var runningObjectName);
runningObjectTable.GetObject(monikers[0], out var runningObjectVal);
if (runningObjectVal is DTE2 dte
&& dte.Debugger is Debugger2 debugger
&& runningObjectName.StartsWith("!VisualStudio.DTE.15.0"))
{
foreach (Process2 debuggedProcess in dte.Debugger.DebuggedProcesses)
{
if (debuggedProcess.ProcessID == maybeDebuggedProcessId)
{
var def = debugger.Transports.Item("Default");
var engines = new List<Engine>();
foreach (Engine engine in def.Engines)
{
engines.Add(engine);
}
var debugEngine = new[]
{
engines.Single(x => x.Name == "Managed (v4.6, v4.5, v4.0)")
};
foreach (Process2 localProcess in dte.Debugger.LocalProcesses)
{
if (processIds.Contains(localProcess.ProcessID))
{
localProcess.Attach2(debugEngine);
notAttached--;
}
}
break;
}
}
}
}
return notAttached == 0;
}
finally
{
if (bindCtx != null) Marshal.ReleaseComObject(bindCtx);
if (runningObjectTable != null) Marshal.ReleaseComObject(runningObjectTable);
if (enumMoniker != null) Marshal.ReleaseComObject(enumMoniker);
}
}
}