C# 以编程方式从插件获取当前的 Visual Studio IDE 解决方案目录

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2054182/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 23:03:45  来源:igfitidea点击:

Programmatically getting the current Visual Studio IDE solution directory from addins

c#visual-studiosolutionenvdtevs-extensibility

提问by Dave Clemmer

I have some tools that perform updates on .NET solutions, but they need to know the directory where the solution is located.

我有一些工具可以在 .NET 解决方案上执行更新,但他们需要知道解决方案所在的目录。

I added these tools as External Tools, where they appear in the IDE Tools menu, and supplying $(SolutionDir)as an argument. This works fine.

我将这些工具添加为外部工具,它们出现在 IDE 工具菜单中,并$(SolutionDir)作为参数提供。这工作正常。

However, I want these tools to be easier to access in the IDE for the user through a custom top level menu (for which I created a Visual Studio integration package project) and through a context menu on solution nodes (for which I created a Visual Studio add-in project). I'm looking for a way to get the current solution directory through these contexts.

但是,我希望用户通过自定义顶级菜单(为此我创建了一个 Visual Studio 集成包项目)和解决方案节点上的上下文菜单(为此我创建了一个 Visual Studio 插件项目)。我正在寻找一种通过这些上下文获取当前解决方案目录的方法。

I tried getting the solution information from the VisualStudio.DTEobject:

我尝试从VisualStudio.DTE对象中获取解决方案信息:

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

But, this returns the solution directory for the add ins, not the current solution.

但是,这将返回加载项的解决方案目录,而不是当前的解决方案。

I tried echoing $(SolutionDir)and reading it back:

我尝试回声$(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();

But, this returned the directory for the IDE, not the current solution.

但是,这返回了 IDE 的目录,而不是当前的解决方案。

I didn't see any relevant information in the solution node CommandBar.

我在解决方案节点中没有看到任何相关信息CommandBar

Alternatively, if there was a way to programmatically access the defined Visual Studio external tools and launch them (using the already defined macro arguments), that would work.

或者,如果有一种方法可以以编程方式访问已定义的 Visual Studio 外部工具并启动它们(使用已定义的宏参数),那就行了。

What is the solution?

解决办法是什么?

采纳答案by Peter Macej

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

But, this returns the solution directory for the add ins, not the current solution.

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

但是,这将返回加载项的解决方案目录,而不是当前的解决方案。

Your approach to get the directory is good. What's wrong is the way you get the VisualStudio.DTEobject. Where is this code called? I assume it is in your add-in. Do you execute (debug) your add-in in Visual Studio which opens another instance of Visual Studio where you open your solution? So you have two instances of Visual Studio.

您获取目录的方法很好。问题在于您获取VisualStudio.DTE对象的方式。这段代码在哪里调用?我假设它在您的加载项中。您是否在 Visual Studio 中执行(调试)您的加载项,这会打开您打开解决方案的另一个 Visual Studio 实例?所以你有两个 Visual Studio 实例。

The GetActiveObject("VisualStudio.DTE")gets a random Visual Studio instance. In your case, it is apparently Visual Studio with an add-in project since you get path to your add-in. That's for explanation what would be the reason of your problem.

GetActiveObject("VisualStudio.DTE")得到一个随机的Visual Studio实例。在您的情况下,它显然是带有加载项项目的 Visual Studio,因为您获得了加载项的路径。这是为了解释您的问题的原因。

The correct way to get DTEis very simple. In fact, your add-in already has reference to DTE in which it runs (that is, in which the solution is opened). It is stored in a global variable _applicationObject in your add-in connect class. It is set when your add-in starts in the OnConnectionevent handler. So all you need is to call:

正确的获取DTE方式很简单。事实上,您的加载项已经引用了它在其中运行的 DTE(即,在其中打开了解决方案)。它存储在加载项连接类中的全局变量 _applicationObject 中。它在您的加载项在OnConnection事件处理程序中启动时设置。所以你只需要调用:

string solutionDir = System.IO.Path.GetDirectoryName(_applicationObject.Solution.FullName);

回答by Dave Clemmer

With Peter's push in the right direction, I set up the context menu addin to launch an external tool with the solution directory, and output the results to the output pane. Some example blurb from the add in:

随着彼得朝着正确的方向推进,我设置了上下文菜单加载项以使用解决方案目录启动外部工具,并将结果输出到输出窗格。来自插件的一些示例简介:

    ///--------------------------------------------------------------------------------
    /// <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);
        }
    }