У меня есть утилита (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. Идея в том, что если мне нужен специализированный код для открытия файла, то это и есть этот код. Извините, я не ответил вам раньше, я только сейчас заметил ваш комментарий.
Интересно, есть ли у кого-нибудь более чистый способ сделать это с VS 2010?





Я не могу понять, как это сделать с помощью прямых параметров командной строки. Похоже, вам придется написать для него макрос. Предположительно, вы можете так их вызывать.
devenv /command "Macros.MyMacros.Module1.OpenFavoriteFiles"
Итак, вы, вероятно, можете создать макрос, который принимает имя файла и номер строки, затем открывает файл и переходит в нужное место. Но я не знаю, можно ли где-то указать флаг того же экземпляра или нет.
На самом деле это не решает проблему, потому что команда devenv / запускает новый экземпляр
Совсем не решение. Смотрите правильный ответ ниже, с сообщением от reder !!
С VS2008 SP1 вы можете использовать следующую командную строку, чтобы открыть файл в определенной строке в существующем экземпляре:
devenv /edit FILE_PATH /command "edit.goto FILE_LINE"
Я попробовал это без запущенного экземпляра, и это сработало - VS2008 запустился, открыл файл и перешел в указанную строку. Затем я попробовал его в другом файле (с еще работающим VS2008), и он открыл файл, но остался в строке 1. Как вы думаете, в чем разница между нашими настройками?
У меня это настроено в Vim, и оно срабатывает один раз из десяти. <ворчать ворчать> VS2010
Это открывает для меня файлы в VS2010, но не переходит к строке, если не открывает новый экземпляр Visual Studio.
Все еще не решение. Правильное решение - один пост ниже.
Это сработало для VS2013 для меня. Даже если VS2013 еще не открыт. Однако мне нужно было использовать командную строку Visual Studio или указать полный путь к devenv. Я ожидаю, что можно также добавить путь к переменной среды PATH.
Я не могу заставить это работать - я использую VS2010 Pro, если у меня есть флаг / edit, кажется, что флаг / command полностью игнорируется. Если у меня нет флага / edit, он открывает новый экземпляр VS.
Обновление: все еще работает для VS2017! Вы получите сообщение в строке состояния в левом нижнем углу с надписью Command "edit.goto" is not available., если предоставленный вами файл не может быть открыт.
Для справки - 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)
Может быть, вы адаптируете свой код для открытия файла без имени пути, который является частью открытого проекта (путь должен вычитаться из данных проекта)? И с помощью следующего ввода: FILENAME: LINE_NUM. Этот формат широко используется в мире Unix. В идеале должна быть интерактивная команда, например Ctrl + G, которая переходит к LINE_NUM, но она также должна переходить к другому файлу, если указана часть «FILENAME:»
Отличное решение - я предлагаю добавить ниже, чтобы использовать формат, который я получаю в исключениях, т.е. заканчивается на <: line n> int iPos = filename.IndexOf (": line"); если (iPos> 0) {строка sLineNumber = filename.Substring (iPos + 6); int.TryParse (sLineNumber, исходная строка файла); filename = filename.Remove (iPos); }
На основании ответа красный я опубликовал репозиторий с исходным кодом, вот двоичный (.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 ..
Я собирался задать этот вопрос, потому что, когда вы получаете «желтый экран смерти» при отладке веб-приложения, вы хотите быстро перейти к файлу и строке, которые он дает вам в трассировке стека, например:
[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». Кажется, что цитаты проглочены. Ты знаешь почему?
@LoveRight Я не понимаю, о чем вы говорите. В скрипте по ссылке нет «ExecuteCommand».
У меня работает эта команда, если 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());
}
Потрясающие. Итак, вызываете ли вы это с помощью «devenv / command Macros.MyMacros.visual_studio_open_file myFile someLineNumber»?