簡體   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