对于从CMD窗口运行但未由F5启动的控制台应用程序,如何“附加到进程...”?我问的原因是因为应用程序需要命令行参数,我希望有一个真实的经验。
我甚至附加到CMD.exe
,但没有运气,或使用Console.ReadKey()
设置断点也没有运气。我在这里有点不知所措。
这可能吗?
答案 0 :(得分:30)
你有一些选择:
答案 1 :(得分:5)
要从命令行调试而不是使用VS GUI迷宫:
启动Visual Studio命令提示符
输入 vsjitdebugger /?,它会为您提供如下命令示例:
C:GT; vsjitdebugger [AppName] [Args]:启动指定的可执行文件并附加到调试器
C:GT;任务列表|找/我“网”
答案 2 :(得分:2)
有可能,当然。尝试以下两种方法之一:
答案 3 :(得分:1)
正如其他人所说,您可以在项目中指定stratup命令行参数,然后在Visual Studio中开始调试。
如果您仍想附加到正在运行的应用程序,则需要将调试器附加到MyApp.exe(无论您的应用程序被调用 - 编译到bin \ debug目录的exe)而不是cmd.exe。附加到cmd.exe它附加到命令进程,而不是应用程序的进程。
答案 4 :(得分:1)
2020更新:到@VladV答案
Debugger.Break()
不再起作用。
尝试改用Debugger.Launch()
,也请在此行之后放置断点,否则VS将开始抱怨。
答案 5 :(得分:0)
在项目设置“Debug”部分中,有一个“命令行参数:”的文本框。当VS调试器启动C#程序时,它会将这些参数传递给进程,就好像程序已经从带有这些参数的命令行启动一样。
另一种方法是使用命令行调试器。这里有一些选项,但是老实说,除非你进入一些非常多毛的调试场景,否则它们可能不是你想用的而不是VS。如果你有兴趣查看它们,那么在这个SO答案中有一个很好的总结:
你也可以尝试在初始化的早期调用System.Diagnostics.Debugger.Break()
的技术 - 如果程序在调试器下运行,它会中断,它不在调试器下运行你应该被问到是否你想附上一个。您可以根据配置文件或环境变量设置有条件地进行调用,这样您只有在真正感兴趣时才能获得休息(有点干扰,但不是太糟糕)。
答案 6 :(得分:0)
只需在" HKEY_LOCAL_MACHINE \ Software \ Microsoft \ Windows NT \ currentversion \ image文件执行选项"中添加exe文件名的注册表项,添加"调试器"密钥值为" vsjitdebugger.exe"在它下面,你可以看到弹出一个对话框,要求你在exe启动时选择要调试的VS版本。
请参阅MSDN" How to: Launch the Debugger Automatically"了解更多信息。
答案 7 :(得分:0)
我以为我会在这里找到一些更好的解决方案,但看来我已经拥有的解决方案是最好的。 Debugger.Break()
根本不为我工作。但是前段时间,我在GitHub上找到了VisualStudioAttacher类。现在找不到代表,但我要发布经过稍微修改的版本。
您将像这样使用它。
class Program {
static void Main(string[] args) {
VSAttacher.attachDebugger("SolutionFileContainingThisCode.sln");
Console.WriteLine("Hello World"); //set a brakepoint here
//...
}
}
这只会附加到Visual Studio当前打开的实例上,不需要您选择调试器。
代码:
using System.IO;
using EnvDTE;
using DTEProcess = EnvDTE.Process;
using System;
using System.Collections.Generic;
using Process = System.Diagnostics.Process;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
namespace AppController {
#region Classes
/// <summary>Visual Studio attacher.</summary>
public static class VSAttacher {
public static Action<object> log = (o) => Console.WriteLine(o);
//Change following variables depending on your version of visual studio
//public static string VSProcessName = "WDExpress";
//public static string VSObjectName = "!WDExpress";
public static string VSProcessName = "devenv";
public static string VSObjectName = "!VisualStudio";
/// <summary>
/// Tries to attach the program to Visual Studio debugger.
/// Returns true is the attaching was successful, false is debugger attaching failed.
/// </summary>
/// <param name="sln">Solution file containing code to be debugged.</param>
public static bool attachDebugger(string sln) {
if (System.Diagnostics.Debugger.IsAttached) return true;
log("Attaching to Visual Studio debugger...");
var proc = VSAttacher.GetVisualStudioForSolutions(
new List<string>() { Path.GetFileName(sln) });
if (proc != null) VSAttacher.AttachVSToProcess(
proc, Process.GetCurrentProcess());
else {
try { System.Diagnostics.Debugger.Launch(); }
catch (Exception e) { }
} // try and attach the old fashioned way
if (System.Diagnostics.Debugger.IsAttached) {
log(@"The builder was attached successfully. Further messages will displayed in ""Debug"" output of ""Output"" window.");
return true;
}
log("Could not attach to visual studio instance.");
return false;
}
#region Public Methods
#region Imports
[DllImport("User32")]
private static extern int ShowWindow(int hwnd, int nCmdShow);
/// <summary>Returns a pointer to an implementation of <see cref="IBindCtx"/> (a bind context object). This object stores information about a particular moniker-binding operation.</summary>
/// <param name="reserved">This parameter is reserved and must be 0.</param>
/// <param name="ppbc">Address of an <see cref="IBindCtx"/>* pointer variable that receives the interface pointer to the new bind context object. When the function is successful, the caller is responsible for calling Release on the bind context. A NULL value for the bind context indicates that an error occurred.</param>
/// <returns></returns>
[DllImport("ole32.dll")]
public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
/// <summary>Returns a pointer to the <see cref="IRunningObjectTable"/> interface on the local running object table (ROT).</summary>
/// <param name="reserved">This parameter is reserved and must be 0.</param>
/// <param name="prot">The address of an IRunningObjectTable* pointer variable that receives the interface pointer to the local ROT. When the function is successful, the caller is responsible for calling Release on the interface pointer. If an error occurs, *pprot is undefined.</param>
/// <returns>his function can return the standard return values E_UNEXPECTED and S_OK.</returns>
[DllImport("ole32.dll")]
public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr SetFocus(IntPtr hWnd);
#endregion
public static string GetSolutionForVisualStudio(Process visualStudioProcess) {
var vsi = getVSInstance(visualStudioProcess.Id);
try { return vsi?.Solution.FullName;}
catch (Exception) {} return null;
}
public static Process GetAttachedVisualStudio(Process ap) {
var vsps = getVSProcess();
foreach (Process vsp in vsps) {
var vsi = getVSInstance(vsp.Id);
if (vsi == null) continue;
try {
foreach (Process dp in vsi.Debugger.DebuggedProcesses)
if (dp.Id == ap.Id) return dp;
} catch (Exception) {}
}
return null;
}
public static void AttachVSToProcess(Process vsp, Process applicationProcess) {
var vsi = getVSInstance(vsp.Id);
if (vsi == null) return;
//Find the process you want the VS instance to attach to...
DTEProcess tp = vsi.Debugger.LocalProcesses.Cast<DTEProcess>().FirstOrDefault(process => process.ProcessID == applicationProcess.Id);
//Attach to the process.
if (tp != null) {
tp.Attach();
ShowWindow((int)vsp.MainWindowHandle, 3);
SetForegroundWindow(vsp.MainWindowHandle);
} else {
throw new InvalidOperationException("Visual Studio process cannot find specified application '" + applicationProcess.Id + "'");
}
}
public static Process GetVisualStudioForSolutions(List<string> sns) {
foreach (string sn in sns) {
var vsp = GetVSProc(sn);
if (vsp != null) return vsp;
}
return null;
}
public static Process GetVSProc(string name) {
var vsps = getVSProcess(); var e = false;
foreach (Process vsp in vsps) {
_DTE vsi = getVSInstance(vsp.Id);
if (vsi == null) { e = true; continue; }
try {
string sn = Path.GetFileName(vsi.Solution.FullName);
if (string.Compare(sn, name, StringComparison.InvariantCultureIgnoreCase)
== 0) return vsp;
} catch (Exception) { e = true; }
}
if (!e) log($@"No running Visual Studio process named ""{VSProcessName}"" were found.");
return null;
}
#endregion
#region Private Methods
private static IEnumerable<Process> getVSProcess() {
Process[] ps = Process.GetProcesses();
//var vsp = ps.Where(p => p.Id == 11576);
return ps.Where(o => o.ProcessName.Contains(VSProcessName));
}
private static _DTE getVSInstance(int processId) {
IntPtr numFetched = IntPtr.Zero;
IMoniker[] m = new IMoniker[1];
GetRunningObjectTable(0, out var rot);
rot.EnumRunning(out var ms); ms.Reset();
var rons = new List<string>();
while (ms.Next(1, m, numFetched) == 0) {
IBindCtx ctx;
CreateBindCtx(0, out ctx);
m[0].GetDisplayName(ctx, null, out var ron);
rons.Add(ron);
rot.GetObject(m[0], out var rov);
if (rov is _DTE && ron.StartsWith(VSObjectName)) {
int currentProcessId = int.Parse(ron.Split(':')[1]);
if (currentProcessId == processId) {
return (_DTE)rov;
}
}
}
log($@"No Visual Studio _DTE object was found with the name ""{VSObjectName}"" that resides in given process (PID:{processId}).");
log("The processes exposes following objects:");
foreach (var ron in rons) log(ron);
return null;
}
#endregion
}
#endregion
}
如果使用Visual Studio的快速版本,则应更改VSProcessName
和VSObjectName
(仅在快速版本和社区版本中对此进行了测试)。