Откройте файл в Visual Studio с определенным номером строки

У меня есть утилита (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»?

EndangeredMassa 12.12.2008 17:55

Нет, это просто код для открытия файла с существующим экземпляром devenv. Идея в том, что если мне нужен специализированный код для открытия файла, то это и есть этот код. Извините, я не ответил вам раньше, я только сейчас заметил ваш комментарий.

Harold Bamford 17.12.2008 20:00

Интересно, есть ли у кого-нибудь более чистый способ сделать это с VS 2010?

Dan Fitch 25.08.2010 00:52
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
57
3
22 167
14
Перейти к ответу Данный вопрос помечен как решенный

Ответы 14

Ответ принят как подходящий

Я не могу понять, как это сделать с помощью прямых параметров командной строки. Похоже, вам придется написать для него макрос. Предположительно, вы можете так их вызывать.

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

Итак, вы, вероятно, можете создать макрос, который принимает имя файла и номер строки, затем открывает файл и переходит в нужное место. Но я не знаю, можно ли где-то указать флаг того же экземпляра или нет.

На самом деле это не решает проблему, потому что команда devenv / запускает новый экземпляр

Dan Fitch 25.08.2010 00:52

Совсем не решение. Смотрите правильный ответ ниже, с сообщением от reder !!

Alexis Pautrot 18.09.2012 17:54

С VS2008 SP1 вы можете использовать следующую командную строку, чтобы открыть файл в определенной строке в существующем экземпляре:

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

Источник

Я попробовал это без запущенного экземпляра, и это сработало - VS2008 запустился, открыл файл и перешел в указанную строку. Затем я попробовал его в другом файле (с еще работающим VS2008), и он открыл файл, но остался в строке 1. Как вы думаете, в чем разница между нашими настройками?

Harold Bamford 10.09.2010 00:44

У меня это настроено в Vim, и оно срабатывает один раз из десяти. <ворчать ворчать> VS2010

dash-tom-bang 01.10.2010 04:10

Это открывает для меня файлы в VS2010, но не переходит к строке, если не открывает новый экземпляр Visual Studio.

idbrii 08.10.2011 01:27

Все еще не решение. Правильное решение - один пост ниже.

Alexis Pautrot 18.09.2012 17:54

Это сработало для VS2013 для меня. Даже если VS2013 еще не открыт. Однако мне нужно было использовать командную строку Visual Studio или указать полный путь к devenv. Я ожидаю, что можно также добавить путь к переменной среды PATH.

thomthom 24.01.2014 01:09

Я не могу заставить это работать - я использую VS2010 Pro, если у меня есть флаг / edit, кажется, что флаг / command полностью игнорируется. Если у меня нет флага / edit, он открывает новый экземпляр VS.

Lynden Shields 24.04.2014 07:55

Обновление: все еще работает для VS2017! Вы получите сообщение в строке состояния в левом нижнем углу с надписью Command "edit.goto" is not available., если предоставленный вами файл не может быть открыт.

Chad 15.09.2019 06:50

Для справки - ENVDE, написанный на C# (с использованием Платформа O2 внутри VisualStudio для получения ссылки на живой объект 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 мс).

Разрабатывая вопросы и ответы Гарольда, я адаптировал решение 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, чтобы он заработал (в противном случае он выдает исключение COMException с сообщением MK_E_UNAVAILABLE)

Alexis Pautrot 18.09.2012 17:53

Может быть, вы адаптируете свой код для открытия файла без имени пути, который является частью открытого проекта (путь должен вычитаться из данных проекта)? И с помощью следующего ввода: FILENAME: LINE_NUM. Этот формат широко используется в мире Unix. В идеале должна быть интерактивная команда, например Ctrl + G, которая переходит к LINE_NUM, но она также должна переходить к другому файлу, если указана часть «FILENAME:»

midenok 28.08.2016 10:29

Отличное решение - я предлагаю добавить ниже, чтобы использовать формат, который я получаю в исключениях, т.е. заканчивается на <: line n> int iPos = filename.IndexOf (": line"); если (iPos> 0) {строка sLineNumber = filename.Substring (iPos + 6); int.TryParse (sLineNumber, исходная строка файла); filename = filename.Remove (iPos); }

Paul McCarthy 07.08.2019 16:51

На основании ответа красный я опубликовал репозиторий с исходным кодом, вот двоичный (.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.

Stéphane Gourichon 17.09.2013 17:49

Большое спасибо. В моих приложениях Qt у меня есть регистратор, который записывает исходный файл / номер строки каждой записи журнала. С помощью этого кода я теперь могу добавить кнопку в виджет регистратора для перехода к строке в Visual Studio ..

drescherjm 24.09.2014 20:11

Я собирался задать этот вопрос, потому что, когда вы получаете «желтый экран смерти» при отладке веб-приложения, вы хотите быстро перейти к файлу и строке, которые он дает вам в трассировке стека, например:

[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

Скажем, я хочу перейти к ServiceUserController.cs в строке 41. Обычно я открывал 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
}

Довольно старый тред, но он заставил меня начать, так что вот еще один пример. Эта функция 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.

Вот вариант решения Гарольда для 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)

Показывает, как перейти к указанной строке + столбцу.

Правильная командная строка wingrepsyntax для принудительного выполнения new instance и перехода к номеру строки:

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

Замените studio version number версией, подходящей для вашей установки.

Вот вариант решения Гарольда для VBS: ссылка на скрипт .vbs.

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

Windows изначально поддерживает VBScript - нет необходимости в компиляции или каких-либо дополнительных интерпретаторах.

Я пытаюсь открыть файл и найти ключевое слово. dte.ExecuteCommand "Edit.Find", "ID = ""label1""" не работает, поскольку VS говорит: «Следующий указанный текст не найден: ID = label1». Кажется, что цитаты проглочены. Ты знаешь почему?

Gqqnbig 05.09.2017 20:06

@LoveRight Я не понимаю, о чем вы говорите. В скрипте по ссылке нет «ExecuteCommand».

Evgeny Panasyuk 10.09.2017 16:56

У меня работает эта команда, если Visual Studio еще НЕ открыта. «C: \ Program Files (x86) \ Microsoft Visual Studio 14.0 \ Common7 \ IDE \ devenv.exe» / редактировать «ABSOLUTEFILEPATH_FILENAME.CPP» / команда «Edit.GoTo 164»

Если он уже открыт, то иногда он срабатывает и уходит в нужную строку, но потом он просто перестает работать, и я так и не понял, почему. Похоже, что Microsoft знает об этой проблеме, но заявила, что «не исправит» ее, если только больше людей не пожалуется. Так что, если это все еще проблема, я предлагаю прокомментировать здесь: https://connect.microsoft.com/VisualStudio/Feedback/Details/1128717

Эти зависимости 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 });
}

Простые а?

Это мое рабочее решение C# для Visual Studio 2017 (15.9.7)

Для других версий 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());
}

Версия, опубликованная @ 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());
}

Другие вопросы по теме