【问题标题】:Programmatically getting the current Visual Studio IDE solution directory from addins以编程方式从插件获取当前的 Visual Studio IDE 解决方案目录
【发布时间】:2010-01-13 03:29:11
【问题描述】:

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

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

但是,我希望通过自定义顶级菜单(为此我创建了 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 外部工具并启动它们(使用已定义的宏参数),那也可以。

解决办法是什么?

【问题讨论】:

  • 2+ 显然我在这里跟踪你这个疯狂的 DTE 哈哈

标签: c# visual-studio solution envdte vs-extensibility


【解决方案1】:

EnvDTE.DTE dte = (EnvDTE.DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE"); 字符串解决方案目录 = 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);

【讨论】:

  • 谢谢彼得,这正是问题和解决方案!现在,我将寻找一种方法来通过自定义菜单执行工具来获取输出,以转到输出窗口而不是单独的窗口,所有这些都将完美运行。再次感谢。
【解决方案2】:

在 Peter 的推动下,我设置了上下文菜单插件以启动带有解决方案目录的外部工具,并将结果输出到输出窗格。插件中的一些示例简介:

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

【讨论】:

  • 不,没有找到轮询外部进程的方法,我最终在 VS 包中做了我需要的内部进程。
  • 我有一个结果轮询的解决方案(或者更确切地说,将输出流式传输到输出窗格中),至少在使用 VSPackage 时是这样。而不是这个问题的范围(不适合这里..),所以也许你可以打开一个新问题,我会在那里回答。
  • 好的,我为此提出了一个单独的问题,如果您的答案看起来不错,我会接受! stackoverflow.com/questions/8345636/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-15
  • 1970-01-01
  • 1970-01-01
  • 2017-08-15
  • 2011-06-24
  • 1970-01-01
相关资源
最近更新 更多