所以你在这里有几个选择。
第一个选项(如果适用)是仅添加以下行:
#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif
这通常会弹出“Visual Studio JIT 调试”窗口并允许您选择正在运行的实例或启动新的 Visual Studio。
或者,如果您想要一些...“更棘手”的东西,您可以遍历正在运行的进程,查找任何 Visual Studio 实例,并使用EnvDTE
API 绑定到它们。这要复杂得多,所以如果适用,我推荐上面更简单的选项,但这里是:
首先,我们需要一种“查找”Visual Studio 实例的方法:
private static IEnumerable<Process> GetVisualStudioProcesses()
{
var processes = Process.GetProcesses();
return processes.Where(o => o.ProcessName.Contains("devenv"));
}
然后我们可以使用它来尝试附加到那些正在运行的实例支持的正确的基于 COM 的接口:
private static bool TryGetVsInstance(int processId, out EnvDTE._DTE instance)
{
var numFetched = IntPtr.Zero;
var monikers = new IMoniker[1];
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
GetRunningObjectTable(0, out runningObjectTable);
runningObjectTable.EnumRunning(out monikerEnumerator);
monikerEnumerator.Reset();
while (monikerEnumerator.Next(1, monikers, numFetched) == 0)
{
IBindCtx ctx;
CreateBindCtx(0, out ctx);
string runningObjectName;
monikers[0].GetDisplayName(ctx, null, out runningObjectName);
object runningObjectVal;
runningObjectTable.GetObject(monikers[0], out runningObjectVal);
if (runningObjectVal is EnvDTE._DTE && runningObjectName.StartsWith("!VisualStudio"))
{
var currentProcessId = int.Parse(runningObjectName.Split(':')[1]);
if (currentProcessId == processId)
{
instance = (EnvDTE._DTE)runningObjectVal;
return true;
}
}
}
instance = null;
return false;
}
COM 定义在哪里:
[ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}
public class MessageFilter : IOleMessageFilter
{
private const int Handled = 0, RetryAllowed = 2, Retry = 99, Cancel = -1, WaitAndDispatch = 2;
int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
{
return Handled;
}
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
return dwRejectType == RetryAllowed ? Retry : Cancel;
}
int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
{
return WaitAndDispatch;
}
public static void Register()
{
CoRegisterMessageFilter(new MessageFilter());
}
public static void Revoke()
{
CoRegisterMessageFilter(null);
}
private static void CoRegisterMessageFilter(IOleMessageFilter newFilter)
{
IOleMessageFilter oldFilter;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
[DllImport("Ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}
我们可以像这样在“尝试附加到”调用中结束它:
public static bool AttachToProcess(int processId)
{
#if !DEBUG
// Not allowed if not in DEBUG mode...
return false;
#else
MessageFilter.Register();
Exception lastException = null;
bool attachSuccess = false;
while (!attachSuccess && !(lastException is COMException))
{
Log("Attempting DynamicAttach to process id:" + processId);
try
{
Log("Querying for active VS instances...");
var studioProcesses = GetVisualStudioProcesses();
var studioDtes = studioProcesses
.Select(proc =>
{
EnvDTE._DTE dte;
if (TryGetVsInstance(proc.Id, out dte))
{
return new DteWrapper(dte);
}
return null;
})
.Where(dte => dte != null);
foreach (var studioDte in studioDtes)
{
attachSuccess = TryAttachTo(processId, studioDte, out lastException);
if(attachSuccess) break;
}
}
catch (Exception vsex)
{
lastException = vsex;
}
}
MessageFilter.Revoke();
return attachSuccess;
#endif
}
private static bool TryAttachTo(int processId, DteWrapper studioDte, out Exception lastException)
{
bool attachSuccess = false;
lastException = null;
Log("Attempting to attach to process " + processId);
try
{
var localProcesses =
from localProcess in studioDte.Debugger.LocalProcesses.Cast<EnvDTE.Process>()
let processWrapper = new ProcessWrapper(localProcess)
where processWrapper.Process2 == null || !processWrapper.Process2.IsBeingDebugged
select processWrapper;
if (!localProcesses.Any())
{
lastException = new ApplicationException("Can not find process to attach to!");
return false;
}
foreach (var process in localProcesses)
{
try
{
if (process.Process.ProcessID == processId)
{
Log("Found dte process to attach to, attempting attach...", studioDte);
try
{
process.Process.Attach();
Log("Attached to process!");
attachSuccess = true;
AtLeastOneDebuggerWasAttached = true;
}
catch (Exception detachException)
{
Log("Could not attach to process:" + detachException, studioDte);
attachSuccess = false;
lastException = detachException;
}
}
}
catch (Exception attachException)
{
lastException = attachException;
}
}
}
catch (Exception queryProcessesException)
{
lastException = queryProcessesException;
}
return attachSuccess;
}
一些辅助类定义为:
internal class DteWrapper
{
public DteWrapper(EnvDTE._DTE dte)
{
Dte = dte as EnvDTE.DTE;
Dte2 = dte as EnvDTE80.DTE2;
Debugger = dte.Debugger;
Debugger2 = (Dte2 != null) ? Dte2.Debugger as EnvDTE80.Debugger2 : null;
Events = dte.Events;
DebuggerEvents = dte.Events.DebuggerEvents;
}
public EnvDTE.DTE Dte { get; private set; }
public EnvDTE80.DTE2 Dte2 { get; private set; }
public EnvDTE.Debugger Debugger { get; private set; }
public EnvDTE80.Debugger2 Debugger2 { get; private set; }
public EnvDTE.Events Events { get; private set; }
public EnvDTE.DebuggerEvents DebuggerEvents { get; private set; }
}
internal class ProcessWrapper
{
public ProcessWrapper(EnvDTE.Process dteProcess)
{
this.Process = dteProcess;
this.Process2 = dteProcess as EnvDTE80.Process2;
this.Process3 = dteProcess as EnvDTE90.Process3;
this.ManagedProcess = System.Diagnostics.Process.GetProcessById(dteProcess.ProcessID);
}
public Process ManagedProcess { get; private set; }
public EnvDTE.Process Process { get; private set; }
public EnvDTE80.Process2 Process2 { get; private set; }
public EnvDTE90.Process3 Process3 { get; private set; }
}