简体   繁体   English

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

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

How do you "Attach to Process..." for a console application thats running from a CMD window and not launched by F5?对于从 CMD 窗口运行而不是由 F5 启动的控制台应用程序,您如何“附加到进程...”? The reason I ask is because the application takes command line arguments and I want to have a genuine experience.我问的原因是因为应用程序需要命令行参数,我想获得真正的体验。

I've even attaching to CMD.exe , but no luck, or setting a break-point using Console.ReadKey() with also no luck.我什至附加到CMD.exe ,但没有运气,或者使用Console.ReadKey()设置断点也没有运气。 I'm kind of at a loss here.我有点不知所措。

Is this possible?这可能吗?

You have some options:你有一些选择:

  • Use "Debug -> Command line arguments" option in Visual Studio;在 Visual Studio 中使用“调试 -> 命令行参数”选项;
  • Use "Debug -> Attach to process" and find your process;使用“调试 -> 附加到进程”并找到您的进程; it is not cmd.exe, but a process with executable name like "MyProject.exe".它不是 cmd.exe,而是具有可执行名称的进程,例如“MyProject.exe”。 You can use Process Explorer or another task manager with "tree view" support to easily find the Process ID - just look for the processes started by your cmd.exe.您可以使用 进程资源管理器或其他具有“树视图”支持的任务管理器来轻松查找进程 ID - 只需查找由 cmd.exe 启动的进程。
  • Put Debugger.Break() into your code - when this method is executed, the system will launch a dialog asking you to choose what instance of Visual Studio to use for debugging (you can choose the one with your project already open).将 Debugger.Break() 放入您的代码中 - 执行此方法时,系统将启动一个对话框,要求您选择用于调试的 Visual Studio 实例(您可以选择已打开项目的实例)。

To debug from the command line rather than using the VS GUI maze:要从命令行调试而不是使用 VS GUI 迷宫:

  • Launch the Visual Studio Command Prompt启动 Visual Studio 命令提示符

  • type vsjitdebugger/?输入vsjitdebugger/? which gives you the command example like :它为您提供了如下命令示例:

c:> vsjitdebugger [AppName] [Args] : Launch the specified executable and attach to debugger c:> vsjitdebugger [AppName] [Args] : 启动指定的可执行文件并附加到调试器

  • typing tlist or tasklist will give you PIDs for attaching to existing processes.键入tlisttasklist将为您提供用于附加到现有进程的 PID。 example:例子:

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

It's possible, sure.有可能,当然。 Try one of these two:尝试以下两种方法之一:

  1. Start the process, then go to Debug->Attach and find the process.启动进程,然后转到调试->附加并找到进程。 You may have to refresh to see it.您可能需要刷新才能看到它。
  2. Add a "Debugger.Break()" statement in the code, if possible;如果可能,在代码中添加“Debugger.Break()”语句; that will break automatically (but be sure to remove it or surround it with preprocessor directives so it doesn't get into production code).这将自动中断(但一定要删除它或用预处理器指令包围它,这样它就不会进入生产代码)。

2020 UPDATE: to @VladV answer 2020 年更新: @VladV 回答

Debugger.Break() doesn't work anymore. Debugger.Break()不再起作用。

Try using Debugger.Launch() instead, also put breakpoints after this line, or VS will start complaining.尝试使用Debugger.Launch()代替,也在这一行之后放置断点,否则 VS 将开始抱怨。

As others have said, you can specify the stratup command line arguments from within the project and just start debugging within Visual Studio.正如其他人所说,您可以从项目中指定 stratup 命令行参数,然后在 Visual Studio 中开始调试。

If you still want to attach to the running application, you need to attach the debugger to MyApp.exe (whatever your application is called - the exe that gets compiled to the bin\\debug directory) and not cmd.exe.如果您仍想附加到正在运行的应用程序,您需要将调试器附加到 MyApp.exe(无论您的应用程序被称为什么 - 被编译到 bin\\debug 目录的 exe)而不是 cmd.exe。 Attaching to cmd.exe it attaching to the command process, not the process of your application.附加到 cmd.exe 它附加到命令进程,而不是应用程序的进程。

Just add an registry entry for your exe's name in "HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\currentversion\\image file execution options", adding a "debugger" key valued with "vsjitdebugger.exe" under it, you can see a dialog pops up asking you to choose a VS version to debug when the exe starts up.只需在“HKEY_LOCAL_MACHINE\\Software\\Microsoft\\Windows NT\\currentversion\\image file execution options”中为您的exe名称添加一个注册表项,在其下添加一个值为“vsjitdebugger.exe”的“调试器”键,您可以看到一个对话框弹出up 要求您选择 VS 版本以在 exe 启动时进行调试。

see MSDN " How to: Launch the Debugger Automatically " for more information.有关详细信息,请参阅 MSDN“ 如何:自动启动调试器”。

In the projects settings "Debug" section there's a textbox for "Command line arguments:".在项目设置“调试”部分,有一个“命令行参数:”文本框。 When the VS debugger starts the C# program, it'll pass those arguments to the process just as if the program had been started form the command line with those arguments.当 VS 调试器启动 C# 程序时,它会将这些参数传递给进程,就像程序是从命令行使用这些参数启动的一样。

The alternative is to use a command line debugger.另一种方法是使用命令行调试器。 There are a few options here, but in all honesty they're probably not what you want to use instead of VS unless you're getting into some really hairy debugging scenarios.这里有几个选项,但老实说,除非您进入一些非常棘手的调试场景,否则它们可能不是您想要使用的而不是 VS。 If you're interested in checking them out, there's a good summary in this SO answer:如果您有兴趣查看它们,那么这个 SO 答案中有一个很好的总结:

You can also try the techique of putting a call to System.Diagnostics.Debugger.Break() early in your initialization - if the program is running under a debugger, it'll break, it it's not running under a debugger you should be asked if you want to attach one.您还可以尝试在初始化早期调用System.Diagnostics.Debugger.Break()的技术 - 如果程序在调试器下运行,它会中断,它不在调试器下运行,您应该被问到如果你想附上一张。 You can make the call conditionally depending on a configuration file or environment variable setting so you only get the break if you're really interested in it (somewhat intrusive, but not too bad).您可以根据配置文件或环境变量设置有条件地进行调用,因此只有在您真正对它感兴趣时才能中断(有点侵入性,但还不错)。

I thought I would find some better solutions here but it seem the one I already have is the best.我以为我会在这里找到一些更好的解决方案,但似乎我已经拥有的解决方案是最好的。 Debugger.Break() just simply don't work for me at all. Debugger.Break()根本不适合我。 But some time ago I found VisualStudioAttacher class on GitHub.但是前段时间我在 GitHub 上发现了 VisualStudioAttacher 类。 Can't find the rep right now, but I'm posting my slightly modified version.现在找不到代表,但我正在发布我稍微修改过的版本。

You will use it like this.你会像这样使用它。

class Program {
    static void Main(string[] args) {
        VSAttacher.attachDebugger("SolutionFileContainingThisCode.sln");

        Console.WriteLine("Hello World"); //set a brakepoint here
        //...               
    }

}

This will just attach to currently opened instance of visual studio and it doesn't require you to choose the debugger.这只会附加到当前打开的 Visual Studio 实例,不需要您选择调试器。

Setup设置

  1. Create new class library project named VSAttacher , or whatever you like.创建名为VSAttacher或任何您喜欢的新类库项目。
  2. Add reference to VSAttacher project in the project you want to debug.在要调试的项目中添加对VSAttacher项目的引用。
  3. In VSAttacher project, add reference to envdte libraryVSAttacher项目中,添加对envdte库的引用
  4. Paste following code to VSAttacher project :将以下代码粘贴到VSAttacher项目中:

code:代码:

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
}

If you use express version of visual studio, you should change VSProcessName and VSObjectName (this was tested only with express and community versions).如果您使用visual studio 的express 版本,您应该更改VSProcessNameVSObjectName (这仅在express 和社区版本中进行了测试)。

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

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