【问题标题】:Open a file in Visual Studio at a specific line number在 Visual Studio 中打开特定行号的文件
【发布时间】:2019-12-11 06:42:19
【问题描述】:

我有一个实用程序 (grep),它给我一个文件名列表和一个行号。在我确定 devenv 是打开文件的正确程序后,我想确保它在指定的行号处打开。在 emacs 中,这将是:

emacs +140 filename.c

我没有为 Visual Studio (devenv) 找到类似的东西。我找到的最接近的是:

devenv /Command "Edit.Goto 140" filename.c

但是,这会为每个此类文件创建一个单独的 devenv 实例。我宁愿拥有使用现有实例的东西。

这些变体重复使用现有的 devenv,但不要转到指示的行:

devenv /Command "Edit.Goto 140" /Edit filename.c
devenv /Command  /Edit filename.c "Edit.Goto 140"

我认为使用多个“/Command”参数可能会做到这一点,但我可能没有正确的参数,因为我要么收到错误,要么根本没有响应(除了打开一个空的 devenv)。

我可以为 devenv 编写一个特殊的宏,但我希望其他没有该宏的人可以使用这个实用程序。而且我不清楚如何使用“/Command”选项调用该宏。

有什么想法吗?


好吧,似乎没有办法按照我的意愿执行此操作。因为看起来我需要有专门的代码来启动 Visual Studio,所以我决定使用 EnvDTE,如下所示。希望这对其他人有帮助。

#include "stdafx.h"

//-----------------------------------------------------------------------
// This code is blatently stolen from http://benbuck.com/archives/13
//
// This is from the blog of somebody called "BenBuck" for which there
// seems to be no information.
//-----------------------------------------------------------------------

// import EnvDTE
#pragma warning(disable : 4278)
#pragma warning(disable : 4146)
#import "libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0") lcid("0") raw_interfaces_only named_guids
#pragma warning(default : 4146)
#pragma warning(default : 4278)

bool visual_studio_open_file(char const *filename, unsigned int line)
{
    HRESULT result;
    CLSID clsid;
    result = ::CLSIDFromProgID(L"VisualStudio.DTE", &clsid);
    if (FAILED(result))
        return false;

    CComPtr<IUnknown> punk;
    result = ::GetActiveObject(clsid, NULL, &punk);
    if (FAILED(result))
        return false;

    CComPtr<EnvDTE::_DTE> DTE;
    DTE = punk;

    CComPtr<EnvDTE::ItemOperations> item_ops;
    result = DTE->get_ItemOperations(&item_ops);
    if (FAILED(result))
        return false;

    CComBSTR bstrFileName(filename);
    CComBSTR bstrKind(EnvDTE::vsViewKindTextView);
    CComPtr<EnvDTE::Window> window;
    result = item_ops->OpenFile(bstrFileName, bstrKind, &window);
    if (FAILED(result))
        return false;

    CComPtr<EnvDTE::Document> doc;
    result = DTE->get_ActiveDocument(&doc);
    if (FAILED(result))
        return false;

    CComPtr<IDispatch> selection_dispatch;
    result = doc->get_Selection(&selection_dispatch);
    if (FAILED(result))
        return false;

    CComPtr<EnvDTE::TextSelection> selection;
    result = selection_dispatch->QueryInterface(&selection);
    if (FAILED(result))
        return false;

    result = selection->GotoLine(line, TRUE);
    if (FAILED(result))
        return false;

    return true;
}

【问题讨论】:

  • 太棒了。那么,您是否使用“devenv /command Macros.MyMacros.visual_studio_open_file myFile someLineNumber”来调用它?
  • 不,这只是用现有 devenv 实例打开文件的代码。这个想法是,如果我必须有专门的代码来打开文件,这就是那个代码。对不起,我之前没有回复你,我才注意到你的评论。
  • 我想知道截至 VS 2010 是否有人有更清洁的方法来做到这一点?

标签: visual-studio command-line


【解决方案1】:

使用 VS2008 SP1,您可以使用以下命令行在现有实例的特定行打开文件:

devenv /edit FILE_PATH /command "edit.goto FILE_LINE"

Source

【讨论】:

  • 我在没有运行实例的情况下尝试了这个,它成功了——VS2008 启动,打开文件,然后转到指示的行。然后我在另一个文件上尝试了它(VS2008 仍在运行),它打开了文件但停留在第 1 行。你认为我们的设置有什么不同?
  • 我在 Vim 中设置了这个,它可以工作十分之一。 VS2010
  • 这会在 VS2010 中为我打开文件,但不会跳转到该行,除非它正在打开 Visual Studio 的新实例。
  • 仍然不是解决方案。正确的解决方案是下面的一篇文章。
  • 这对我来说适用于 VS2013。即使 VS2013 尚未打开。但是,我需要使用 Visual Studio 命令提示符或指定 devenv 的完整路径。我希望人们也可以将路径添加到 PATH 环境变量。
【解决方案2】:

详细说明 Harold 问答,我将 C++ 解决方案(我首先采用的)改编为 C#。它要简单得多(那是我的第一个 C# 程序!)。只需创建一个项目,添加对“envDTE”和“envDTE80”的引用并删除以下代码:

using System;
using System.Collections.Generic;
using System.Text;

namespace openStudioFileLine
{
    class Program    
    {
        [STAThread]
        static void Main(string[] args)     
        {
            try          
            {
                String filename = args[0];
                int fileline;
                int.TryParse(args[1], out fileline);
                EnvDTE80.DTE2 dte2;
                dte2 = (EnvDTE80.DTE2)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE");
                dte2.MainWindow.Activate();
                EnvDTE.Window w = dte2.ItemOperations.OpenFile(filename, EnvDTE.Constants.vsViewKindTextView);
                ((EnvDTE.TextSelection)dte2.ActiveDocument.Selection).GotoLine(fileline, true);
            }
            catch (Exception e)          
            {          
                Console.Write(e.Message);      
            }
        }
    }
}

然后只需拨打openStudioFileLine path_to_file numberOfLine

希望能有所帮助!

【讨论】:

  • 太棒了!我必须设置“VisualStudio.DTE.9.0”才能让它工作(否则它会向我抛出一个带有消息 MK_E_UNAVAILABLE 的 COMException)
  • 也许你会调整你的代码来打开一个没有路径名的文件,它是一个打开的项目的一部分(路径应该从项目数据中扣除)?并通过以下输入:FILENAME:LINE_NUM。这种格式在 Unix 世界中被广泛使用。理想情况下,应该有一个交互式命令,如 Ctrl+G 跳转到 LINE_NUM,但如果提供“文件名:”部分,它也应该跳转到不同的文件
  • 很好的解决方案 - 我建议在下面添加以使用我在异常中得到的格式,即以 <:line n> int iPos = filename.IndexOf(":line "); if (iPos > 0) { string sLineNumber = filename.Substring(iPos + 6); int.TryParse(sLineNumber, out fileline);文件名 = 文件名。删除(iPos); }
【解决方案3】:

基于reder的回答我已经发布了repository with sourcehere is binary(.net2.0)

我还添加了对多个 VS 版本的支持

usage: <version> <file path> <line number> 

Visual Studio version                 value 
VisualStudio 2002                     2 
VisualStudio 2003                     3 
VisualStudio 2005                     5 
VisualStudio 2008                     8 
VisualStudio 2010                    10 
VisualStudio 2012                    12 
VisualStudio 2013                    13 

使用 GrepWin 的示例:

VisualStudioFileOpenTool.exe 12 %path% %line%

【讨论】:

  • 很好的一步使它适用于许多版本并托管在 github 上。奇怪的是,它有点作用,但在有趣的情况下却不起作用。它在从调试器运行时起作用,而不是从命令行或 FxCop 运行。 VS2010,Win7。
  • 非常感谢。在我的 Qt 应用程序中,我有一个记录器,用于记录每个日志条目的源文件/行号。使用此代码,我现在可以在记录器小部件上添加一个按钮以跳转到 Visual Studio 中的行。
【解决方案4】:

相当老的线程,但它让我开始了,所以这是另一个例子。这个AutoHotkey 函数打开一个文件,并将光标放在特定的行和列上。

; http://msdn.microsoft.com/en-us/library/envdte.textselection.aspx
; http://msdn.microsoft.com/en-us/library/envdte.textselection.movetodisplaycolumn.aspx
VST_Goto(Filename, Row:=1, Col:=1) {
    DTE := ComObjActive("VisualStudio.DTE.12.0")
    DTE.ExecuteCommand("File.OpenFile", Filename)
    DTE.ActiveDocument.Selection.MoveToDisplayColumn(Row, Col)
}

致电:

VST_Goto("C:\Palabra\.NET\Addin\EscDoc\EscDoc.cs", 328, 40)

您几乎可以将它逐行翻译成 VBScript 或 JScript。

【讨论】:

    【解决方案5】:

    这里是 Harold 解决方案的 Python 变体:

    import sys
    import win32com.client
    
    filename = sys.argv[1]
    line = int(sys.argv[2])
    column = int(sys.argv[3])
    
    dte = win32com.client.GetActiveObject("VisualStudio.DTE")
    
    dte.MainWindow.Activate
    dte.ItemOperations.OpenFile(filename)
    dte.ActiveDocument.Selection.MoveToLineAndOffset(line, column+1)
    

    它显示了如何转到指定的行+列。

    【讨论】:

      【解决方案6】:

      这里是 Harold 解决方案的 VBS 变体:link to .vbs script

      open-in-msvs.vbs full-path-to-file line column
      

      Windows 原生支持 VBScript - 无需编译或任何额外的解释器。

      【讨论】:

      • 我尝试打开一个文件并找到一个关键字。 dte.ExecuteCommand "Edit.Find", "ID=""label1""" 不起作用,因为 VS 说“未找到以下指定文本:ID=label1”。报价似乎被吞没了。你知道为什么吗?
      • @LoveRight 我不确定你指的是什么。链接上的脚本没有任何“ExecuteCommand”。
      【解决方案7】:

      这是我适用于 Visual Studio 2017 (15.9.7) 的 C# 解决方案

      对于其他版本的 VS,只需更改版本号(即“VisualStudio.DTE.14.0”)

      待办事项: 添加参考->搜索 'envdte'->检查 envdte 的复选框->单击确定

      using EnvDTE;        
      
      private static void OpenFileAtLine(string file, int line)
      {
          DTE dte = (DTE)  Marshal.GetActiveObject("VisualStudio.DTE.15.0");
          dte.MainWindow.Visible = true;
          dte.ExecuteCommand("File.OpenFile", file);
          dte.ExecuteCommand("Edit.GoTo", line.ToString());
      }
      

      【讨论】:

      • "DTE dte = (DTE) Marshal.GetActiveObject("VisualStudio.DTE");" (即没有号码)为我工作 - 谢谢
      • 必须使用另一个版本的 Marshall 类,请参阅:stackoverflow.com/questions/58010510/…
      【解决方案8】:

      这里的参考是用 C# 编写的 ENVDE(在 VisualStudio 中使用 O2 Platform 来获取对实时 DTE 对象的引用)

      var visualStudio = new API_VisualStudio_2010();
      
      var vsDTE = visualStudio.VsAddIn.VS_Dte;
      //var document = (Document)vsDTE.ActiveDocument;
      //var window =  (Window)document.Windows.first();           
      var textSelection  = (TextSelection)vsDTE.ActiveDocument.Selection;
      var selectedLine = 1;
      20.loop(100,()=>{
                          textSelection.GotoLine(selectedLine++);
                          textSelection.SelectLine();
                      });
      return textSelection;
      

      这段代码做了一个小动画,选择了 20 行(间隔 100 毫秒)

      【讨论】:

        【解决方案9】:

        正确的wingrep命令行syntax强制new instance跳转到行号是:

        "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe" $F /command "edit.goto $L"
        

        studio version number 替换为适合您的设置的正确版本。

        【讨论】:

          【解决方案10】:

          这些 C# 对项目引用的依赖是完全不必要的。事实上,这里的大部分代码都过于冗长。你只需要这个。

          using System.Reflection;
          using System.Runtime.InteropServices;
          
          private static void OpenFileAtLine(string file, int line) {
              object vs = Marshal.GetActiveObject("VisualStudio.DTE");
              object ops = vs.GetType().InvokeMember("ItemOperations", BindingFlags.GetProperty, null, vs, null);
              object window = ops.GetType().InvokeMember("OpenFile", BindingFlags.InvokeMethod, null, ops, new object[] { file });
              object selection = window.GetType().InvokeMember("Selection", BindingFlags.GetProperty, null, window, null);
              selection.GetType().InvokeMember("GotoLine", BindingFlags.InvokeMethod, null, selection, new object[] { line, true });
          }
          

          简单吧?

          【讨论】:

            【解决方案11】:

            @Mungo64 发布的版本对我有用,但当然版本号总是在变化,所以我制作了一个自动搜索直到找到它的版本。

            添加参考->搜索'envdte'->勾选envdte复选框->点击确定

            //使用EnvDTE; //我没有使用 using 指令,因为它会在我正在使用的另一个模块中引起歧义。

            private static void OpenFileAtLine(string file, int line)
            {
                //The number needs to be rolled to the next version each time a new version of visual studio is used... 
                EnvDTE.DTE dte = null;
            
            
                for (int i = 25; i > 8; i--) {
                    try
                    {
                        dte = (EnvDTE.DTE)Marshal.GetActiveObject("VisualStudio.DTE." + i.ToString() + ".0");
                    }
                    catch (Exception ex)
                    {
                        //don't care... just keep bashing head against wall until success
                    }
                }
            
                //the following line works fine for visual studio 2019:
                //EnvDTE.DTE dte = (EnvDTE.DTE)Marshal.GetActiveObject("VisualStudio.DTE.16.0");
                dte.MainWindow.Visible = true;
                dte.ExecuteCommand("File.OpenFile", file);
                dte.ExecuteCommand("Edit.GoTo", line.ToString());
            }
            

            【讨论】:

              【解决方案12】:

              我想不出一种使用直接命令行选项的方法。看起来您将不得不为它编写一个宏。据说,您可以像这样调用它们。

              devenv /command "Macros.MyMacros.Module1.OpenFavoriteFiles"
              

              因此,您可能可以创建一个带有文件名和行号的宏,然后打开文件并跳转到正确的位置。但是,我不知道您是否可以在某处指定相同实例标志。

              【讨论】:

              • 这实际上并不能解决问题,因为 devenv /command 启动了一个新实例
              • 根本不是解决方案。请参阅下面的正确答案,以及 reder 的帖子!
              【解决方案13】:

              我正要问这个问题,因为当您在调试 Web 应用程序时遇到“黄屏死机”时,您想快速转到它在堆栈跟踪中提供给您的文件和行,例如:

              [ContractException: Precondition failed: session != null]
                 System.Diagnostics.Contracts.__ContractsRuntime.TriggerFailure(ContractFailureKind kind, String msg, String userMessage, String conditionTxt, Exception inner) in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Controllers\CustomErrorsPageController.cs:0
                 System.Diagnostics.Contracts.__ContractsRuntime.ReportFailure(ContractFailureKind kind, String msg, String conditionTxt, Exception inner) in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Controllers\CustomErrorsPageController.cs:0
                 System.Diagnostics.Contracts.__ContractsRuntime.Requires(Boolean condition, String msg, String conditionTxt) in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Controllers\CustomErrorsPageController.cs:0
                 IAS_UI.Web.IAS_Session..ctor(HttpSessionStateBase session) in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Web\IAS_Session.cs:15
                 IAS_UI.Controllers.ServiceUserController..ctor() in C:\_svn\IntegratedAdaptationsSystem\Source\IntegratedAdaptationsSystem\IAS_UI\Controllers\ServiceUserController.cs:41
              

              假设我想转到第 41 行的 ServiceUserController.cs。通常我会打开 Visual Studio 并手动执行此操作,但后来我编写了一个 Autohotkey 小脚本来执行此操作。

              要打开它,您将突出显示文件名和行号,例如ServiceUserController.cs:41 然后按您的快捷方式 Alt + v。这是它的代码:

              $!v::
              if (NOT ProcessExists("devenv.exe"))
              {
                  MsgBox, % "Visual Studio is not loaded"
              }
              else
              {
                  IfWinExist, Microsoft Visual Studio
                  {
                      ToolTip, Opening Visual Studio...
                      c := GetClip()
              
                      if (NOT c) {
                          MsgBox, % "No text selected"
                      }
                      else 
                      {
                          WinActivate ; now activate visual studio
                          Sleep, 50
                          ; for now assume that there is only one instance of visual studio - handling of multiple instances comes in later
              
                          arr := StringSplitF(c, ":")
              
                          if (arr.MaxIndex() <> 2) {
                              MsgBox, % "Text: '" . c . "' is invalid."
                          }
                          else {
                              fileName := arr[1]
                              lineNumber := arr[2]
              
                              ; give focus to the "Find" box
                              SendInput, ^d 
              
                              ; delete the contents of the "Find" box
                              SendInput, {Home}
                              SendInput, +{End}
                              SendInput, {Delete}
              
                              ; input *** >of FILENAME *** into the "Find" box
                              SendInput, >of{Space}
                              SendInput, % fileName
              
                              ; select the first entry in the drop down list
                              SendInput, {Down}
                              SendInput, {Enter}
              
                              ; lineNumber := 12 remove later
              
                              ; open the go to line dialog
                              SendInput, ^g
                              Sleep, 20
              
                              ; send the file number and press enter
                              SendInput, % lineNumber
                              SendInput {Enter}
                          }
                      }    
                      ToolTip
                  }
              }
              return
              

              您需要在其前面粘贴以下“实用程序功能”:

              GetClip()
              {
                  ClipSaved := ClipboardAll
                  Clipboard=
                  Sleep, 30
                  Send ^c
                  ClipWait, 2
                  Sleep, 30
                  Gc := Clipboard
                  Clipboard := ClipSaved
                  ClipSaved=
              
                  return Gc
              }
              
              ProcessExists(procName)
              {
                  Process, Exist, %procName%
              
                  return (ErrorLevel != 0)
              }
              
              StringSplitF(str, delimeters)
              {
                  Arr := Object()
              
                  Loop, parse, str, %delimeters%,
                  {
                      Arr.Insert(A_LoopField)
                  }
              
                  return Arr
              }
              

              【讨论】:

                【解决方案14】:

                只要 Visual Studio 尚未打开,使用此命令即可。 "C:\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\devenv.exe" /edit "ABSOLUTEFILEPATH_FILENAME.CPP" /command "Edit.GoTo 164"

                如果它已经打开,那么有时它会工作并转到正确的行,但随后它就会停止工作,我一直不明白为什么。看起来微软已经意识到了这个问题,但表示他们“不会修复”它,除非有更多的人抱怨。所以如果它仍然是一个问题,我建议在这里评论:https://connect.microsoft.com/VisualStudio/Feedback/Details/1128717

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2016-04-12
                  • 1970-01-01
                  • 2014-10-14
                  • 1970-01-01
                  • 2018-10-08
                  • 2012-02-12
                  • 2019-12-14
                  • 2020-11-09
                  相关资源
                  最近更新 更多