繁体   English   中英

调试:附加到在 cmd.exe 中运行的控制台应用程序的进程

[英]Debugging: Attach to Process for Console App running inside cmd.exe

对于从 CMD 窗口运行而不是由 F5 启动的控制台应用程序,您如何“附加到进程...”? 我问的原因是因为应用程序需要命令行参数,我想获得真正的体验。

我什至附加到CMD.exe ,但没有运气,或者使用Console.ReadKey()设置断点也没有运气。 我有点不知所措。

这可能吗?

你有一些选择:

  • 在 Visual Studio 中使用“调试 -> 命令行参数”选项;
  • 使用“调试 -> 附加到进程”并找到您的进程; 它不是 cmd.exe,而是具有可执行名称的进程,例如“MyProject.exe”。 您可以使用 进程资源管理器或其他具有“树视图”支持的任务管理器来轻松查找进程 ID - 只需查找由 cmd.exe 启动的进程。
  • 将 Debugger.Break() 放入您的代码中 - 执行此方法时,系统将启动一个对话框,要求您选择用于调试的 Visual Studio 实例(您可以选择已打开项目的实例)。

要从命令行调试而不是使用 VS GUI 迷宫:

  • 启动 Visual Studio 命令提示符

  • 输入vsjitdebugger/? 它为您提供了如下命令示例:

c:> vsjitdebugger [AppName] [Args] : 启动指定的可执行文件并附加到调试器

  • 键入tlisttasklist将为您提供用于附加到现有进程的 PID。 例子:

c:> 任务列表 | 找到 /i "web"

有可能,当然。 尝试以下两种方法之一:

  1. 启动进程,然后转到调试->附加并找到进程。 您可能需要刷新才能看到它。
  2. 如果可能,在代码中添加“Debugger.Break()”语句; 这将自动中断(但一定要删除它或用预处理器指令包围它,这样它就不会进入生产代码)。

2020 年更新: @VladV 回答

Debugger.Break()不再起作用。

尝试使用Debugger.Launch()代替,也在这一行之后放置断点,否则 VS 将开始抱怨。

正如其他人所说,您可以从项目中指定 stratup 命令行参数,然后在 Visual Studio 中开始调试。

如果您仍想附加到正在运行的应用程序,您需要将调试器附加到 MyApp.exe(无论您的应用程序被称为什么 - 被编译到 bin\\debug 目录的 exe)而不是 cmd.exe。 附加到 cmd.exe 它附加到命令进程,而不是应用程序的进程。

只需在“HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\currentversion\\image file execution options”中为您的exe名称添加一个注册表项,在其下添加一个值为“vsjitdebugger.exe”的“调试器”键,您可以看到一个对话框弹出up 要求您选择 VS 版本以在 exe 启动时进行调试。

有关详细信息,请参阅 MSDN“ 如何:自动启动调试器”。

在项目设置“调试”部分,有一个“命令行参数:”文本框。 当 VS 调试器启动 C# 程序时,它会将这些参数传递给进程,就像程序是从命令行使用这些参数启动的一样。

另一种方法是使用命令行调试器。 这里有几个选项,但老实说,除非您进入一些非常棘手的调试场景,否则它们可能不是您想要使用的而不是 VS。 如果您有兴趣查看它们,那么这个 SO 答案中有一个很好的总结:

您还可以尝试在初始化早期调用System.Diagnostics.Debugger.Break()的技术 - 如果程序在调试器下运行,它会中断,它不在调试器下运行,您应该被问到如果你想附上一张。 您可以根据配置文件或环境变量设置有条件地进行调用,因此只有在您真正对它感兴趣时才能中断(有点侵入性,但还不错)。

我以为我会在这里找到一些更好的解决方案,但似乎我已经拥有的解决方案是最好的。 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 实例,不需要您选择调试器。

设置

  1. 创建名为VSAttacher或任何您喜欢的新类库项目。
  2. 在要调试的项目中添加对VSAttacher项目的引用。
  3. VSAttacher项目中,添加对envdte库的引用
  4. 将以下代码粘贴到VSAttacher项目中:

代码:

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 的express 版本,您应该更改VSProcessNameVSObjectName (这仅在express 和社区版本中进行了测试)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM