Программное получение текущего каталога решений Visual Studio IDE из надстроек

У меня есть инструменты, которые обновляют решения .NET, но им нужно знать каталог, в котором находится решение.

Я добавил эти инструменты как внешние инструменты, где они появляются в меню «Инструменты IDE», и указал $(SolutionDir) в качестве аргумента. Это прекрасно работает.

Однако я хочу, чтобы к этим инструментам было проще получить доступ в среде IDE для пользователя через настраиваемое меню верхнего уровня (для которого я создал проект пакета интеграции Visual Studio) и через контекстное меню на узлах решения (для которого я создал Visual Проект надстройки Studio). Я ищу способ получить текущий каталог решения через эти контексты.

Я попытался получить информацию о решении из объекта VisualStudio.DTE:

EnvDTE.DTE dte = (EnvDTE.DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE");
string solutionDir = System.IO.Path.GetDirectoryName(dte.Solution.FullName);

Но это возвращает каталог решения для надстроек, а не текущее решение.

Я попытался повторить $(SolutionDir) и перечитать его:

System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo("cmd", "echo $(SolutionDir)");

// The following commands are needed to redirect the standard output.
// This means that it will be redirected to the Process.StandardOutput StreamReader.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
// Do not create the black window.
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result = proc.StandardOutput.ReadToEnd();

Но это вернуло каталог для IDE, а не текущее решение.

Я не нашел соответствующей информации в узле решения CommandBar.

В качестве альтернативы, если бы существовал способ программного доступа к определенным внешним инструментам Visual Studio и их запуска (с использованием уже определенных аргументов макроса), это сработало бы.

Каково решение?


person Dave Clemmer    schedule 13.01.2010    source источник
comment
2+ снова, по-видимому, я преследую вас здесь С этим безумием DTE, лол   -  person Terrance    schedule 03.12.2010


Ответы (2)


EnvDTE.DTE dte = (EnvDTE.DTE) System.Runtime.InteropServices.Marshal.GetActiveObject ("VisualStudio.DTE"); строка solutionDir = System.IO.Path.GetDirectoryName (dte.Solution.FullName);

Но это возвращает каталог решения для надстроек, а не текущее решение.

Ваш подход к получению каталога хорош. Неправильно то, как вы получили объект VisualStudio.DTE. Где называется этот код? Я предполагаю, что это в вашей надстройке. Вы выполняете (отлаживаете) свою надстройку в Visual Studio, которая открывает другой экземпляр Visual Studio, в котором вы открываете свое решение? Итак, у вас есть два экземпляра Visual Studio.

GetActiveObject("VisualStudio.DTE") получает случайный экземпляр Visual Studio. В вашем случае это, по-видимому, Visual Studio с проектом надстройки, поскольку вы получаете путь к своей надстройке. Это для объяснения, в чем причина вашей проблемы.

Правильный способ получить DTE очень прост. Фактически, ваша надстройка уже имеет ссылку на DTE, в котором она работает (то есть, в котором открыто решение). Он хранится в глобальной переменной _applicationObject в классе подключения надстройки. Он устанавливается при запуске надстройки в обработчике событий OnConnection. Так что все, что вам нужно, это позвонить:

string solutionDir = System.IO.Path.GetDirectoryName(_applicationObject.Solution.FullName);
person Peter Macej    schedule 13.01.2010
comment
Спасибо, Питер, это была именно проблема и решение! Теперь я буду искать способ получить результат выполнения инструментов через настраиваемые меню, чтобы перейти в окно вывода, а не в отдельное окно, и все будет работать отлично. Еще раз спасибо. - person Dave Clemmer; 13.01.2010

Продвигая Питер в правильном направлении, я настроил надстройку контекстного меню для запуска внешнего инструмента с каталогом решения и вывода результатов на панель вывода. Пример рекламного сообщения из надстройки:

    ///--------------------------------------------------------------------------------
    /// <summary>This method implements the OnConnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being loaded.</summary>
    ///
    /// <param term='application'>Root object of the host application.</param>
    /// <param term='connectMode'>Describes how the Add-in is being loaded.</param>
    /// <param term='addInInst'>Object representing this Add-in.</param>
    /// <seealso class='IDTExtensibility2' />
    ///--------------------------------------------------------------------------------
    public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
    {
        _applicationObject = (DTE2)application;
        _addInInstance = (AddIn)addInInst;

        // Get the solution command bar
        CommandBar solutionCommandBar = ((CommandBars)_applicationObject.CommandBars)["Solution"];

        // Set up the main InCode
        CommandBarPopup solutionPopup = (CommandBarPopup)solutionCommandBar.Controls.Add(MsoControlType.msoControlPopup, System.Reflection.Missing.Value, System.Reflection.Missing.Value, 1, true);
        solutionPopup.Caption = "InCode";

        // Add solution updater submenu
        CommandBarControl solutionUpdaterControl = solutionPopup.Controls.Add(MsoControlType.msoControlButton, System.Reflection.Missing.Value, System.Reflection.Missing.Value, 1, true);
        solutionUpdaterControl.Caption = "Update Solution";
        updateSolutionMenuItemHandler = (CommandBarEvents)_applicationObject.Events.get_CommandBarEvents(solutionUpdaterControl);
        updateSolutionMenuItemHandler.Click += new _dispCommandBarControlEvents_ClickEventHandler(updateSolution_Click);
    }

    // The event handlers for the solution submenu items
    CommandBarEvents updateSolutionMenuItemHandler;

    ///--------------------------------------------------------------------------------
    /// <summary>This property gets the solution updater output pane.</summary>
    ///--------------------------------------------------------------------------------
    protected OutputWindowPane _solutionUpdaterPane = null;
    protected OutputWindowPane SolutionUpdaterPane
    {
        get
        {
            if (_solutionUpdaterPane == null)
            {
                OutputWindow outputWindow = _applicationObject.ToolWindows.OutputWindow;
                foreach (OutputWindowPane loopPane in outputWindow.OutputWindowPanes)
                {
                    if (loopPane.Name == "Solution Updater")
                    {
                        _solutionUpdaterPane = loopPane;
                        return _solutionUpdaterPane;
                    }
                }
                _solutionUpdaterPane = outputWindow.OutputWindowPanes.Add("Solution Updater");
            }
            return _solutionUpdaterPane;
        }
    }

    ///--------------------------------------------------------------------------------
    /// <summary>This method handles clicking on the Update Solution submenu.</summary>
    ///
    /// <param term='inputCommandBarControl'>The control that is source of the click.</param>
    /// <param term='handled'>Handled flag.</param>
    /// <param term='cancelDefault'>Cancel default flag.</param>
    ///--------------------------------------------------------------------------------
    protected void updateSolution_Click(object inputCommandBarControl, ref bool handled, ref bool cancelDefault)
    {
        try
        {
            // set up and execute solution updater thread
            UpdateSolutionDelegate updateSolutionDelegate = UpdateSolution;
            updateSolutionDelegate.BeginInvoke(UpdateSolutionCompleted, updateSolutionDelegate);
        }
        catch (System.Exception ex)
        {
            // put exception message in output pane
            SolutionUpdaterPane.OutputString(ex.Message);
        }
    }

    protected delegate void UpdateSolutionDelegate();

    ///--------------------------------------------------------------------------------
    /// <summary>This method launches the solution updater to update the solution.</summary>
    ///--------------------------------------------------------------------------------
    protected void UpdateSolution()
    {
        try
        {
            // set up solution updater process
            string solutionDir = System.IO.Path.GetDirectoryName(_applicationObject.Solution.FullName);
            System.Diagnostics.ProcessStartInfo procStartInfo = new System.Diagnostics.ProcessStartInfo(@"SolutionUpdater.exe", solutionDir);
            procStartInfo.RedirectStandardOutput = true;
            procStartInfo.UseShellExecute = false;
            procStartInfo.CreateNoWindow = true;
            System.Diagnostics.Process proc = new System.Diagnostics.Process();
            proc.StartInfo = procStartInfo;

            // execute the solution updater
            proc.Start();

            // put solution updater output to output pane
            SolutionUpdaterPane.OutputString(proc.StandardOutput.ReadToEnd());
            SolutionUpdaterPane.OutputString("Solution update complete.");
        }
        catch (System.Exception ex)
        {
            // put exception message in output pane
            SolutionUpdaterPane.OutputString(ex.Message);
        }
    }

    ///--------------------------------------------------------------------------------
    /// <summary>This method completing the update solution thread.</summary>
    ///
    /// <param name="ar">IAsyncResult.</param>
    ///--------------------------------------------------------------------------------
    protected void UpdateSolutionCompleted(IAsyncResult ar)
    {
        try
        {
            if (ar == null) throw new ArgumentNullException("ar");

            UpdateSolutionDelegate updateSolutionDelegate = ar.AsyncState as UpdateSolutionDelegate;
            Trace.Assert(updateSolutionDelegate != null, "Invalid object type");

            updateSolutionDelegate.EndInvoke(ar);
        }
        catch (System.Exception ex)
        {
            // put exception message in output pane
            SolutionUpdaterPane.OutputString(ex.Message);
        }
    }
person Dave Clemmer    schedule 13.01.2010
comment
Нет, не нашел способа опросить внешний процесс, я закончил тем, что сделал то, что мне нужно, как внутренний процесс в пакете VS. - person Dave Clemmer; 28.10.2011
comment
У меня есть решение для опроса результатов (или, скорее, потокового вывода в панель вывода), по крайней мере, при использовании VSPackage. Скорее выходит за рамки этого вопроса (и здесь не подходит ..), так что, возможно, вы можете открыть новый вопрос, и я отвечу там. - person Tatu Lahtela; 01.12.2011
comment
Хорошо, я собрал для этого отдельный вопрос, если ваш ответ будет удовлетворительным, я приму! stackoverflow.com/questions/8345636/ - person Dave Clemmer; 01.12.2011