gpt4 book ai didi

c# - Visual Studio拦截F1帮助命令

转载 作者:太空狗 更新时间:2023-10-29 17:46:44 26 4
gpt4 key购买 nike

我希望编写一个Visual Studio插件,该插件可以拦截默认的联机帮助命令,并在类或类型上调用F1帮助时获取MSDN库URL。

例如,说我将光标放在关键字字符串上,然后按F1键,它通常会自动打开浏览器并导航到该字符串引用类型的帮助文档。我想在传递给浏览器的URL到达浏览器之前对其进行抓取。

是否可以编写可以拦截默认F1帮助命令的Visual Studio外接程序/扩展程序?

如果可以完成上述操作,那么有关从何处开始的任何指针?

最佳答案

大约10年前,当我在Microsoft工作时,我在Visual Studio 2005中为原始的“Online F1”功能编写了规范。因此,我的知识虽然有些权威,但可能已经过时了。 ;-)

您无法更改Visual Studio使用的URL(至少我不知道如何更改),但是您可以简单地编写另一个插件来窃取F1键绑定(bind),并使用与默认设置相同的帮助上下文。 F1处理程序会执行此操作,并将用户定向到您自己的URL或应用程序。

首先,有关Online F1工作原理的一些信息:

Visual Studio IDE的

  • 组件将关键字推送到“F1帮助上下文”中,该上下文是有关用户正在执行的操作的信息的属性包:代码编辑器中的当前选择,正在编辑的文件类型,正在编辑的项目类型等。
  • 当用户按下F1时,IDE会将帮助上下文打包到URL中,并打开指向MSDN的浏览器。

  • 选择CSS属性“宽度”时,按在VS2012的HTML编辑器F1的时候,这里是一个示例URL,在这种情况下,
    msdn.microsoft.com/query/dev11.query?
    appId=Dev11IDEF1&
    l=EN-US&
    k=k(width);
    k(vs.csseditor);
    k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.0);
    k(DevLang-CSS)&
    rd=true

    上面的“k”参数包含Visual Studio内部的帮助上下文。帮助上下文同时包含“关键字”(文本字符串)和“属性”(名称/值对),Visual Studio内部的各种窗口都使用它们来告知IDE用户当前正在做什么。

    CSS编辑器推送了两个关键字:我选择的“宽度”和“vs.csseditor”,如果例如在MSDN上找不到我的选择,则MSDN可以将其用作“后备”。

    还有一些上下文过滤属性:
    TargetFrameworkMoniker = NETFramework,Version=v4.0
    DevLang=CSS

    这些确保F1以正确的语言或技术(在本例中为CSS)加载页面。 (存在另一个针对.NET 4.0的过滤器,因为我加载的项目针对的是.NET 4.0)

    注意上下文是有序的。 “宽度”关键字比下方的关键字重要。

    MSDN上的实际帮助内容具有元数据(由编写文档的团队手动设置),该元数据包含与该页面关联的关键字和名称/值上下文属性。例如,当MSDN上的 css width property documentation存储在MSDN服务器上时,它具有与之关联的关键字列表(在本例中为“width”)和上下文属性的列表在本例中为“DevLang = CSS”。页面可以具有多个关键字(例如“System.String”,“String”)和多个上下文属性(例如“DevLang = C#”,“DevLang = VB”等)。

    当关键字列表进入MSDN Online F1服务时,该算法是这样的,但需要注意的是,在最近几年中它可能已更改:
  • 使用第一个关键字
  • 查找与该关键字
  • 匹配的所有页面
  • 排除所有与上下文属性名称匹配的页面(例如“DevLang”),但与值不匹配的所有页面。例如,这将排除Control.Width页面,因为它将被标记为“DevLang = C#”,“DevLang = VB”。但是它不会排除没有DevLang属性的页面。
  • 如果没有结果,但是还有更多的关键字可使用,除非您用尽了所有关键字,否则请从#1重新开始使用下一个关键字(按顺序)。如果没有关键字,请执行“备份”操作,这可能会返回MSDN搜索结果列表,可能会显示“找不到页面”或其他解决方案。
  • 对其余结果进行排名。我不记得确切的排名算法,并且此后可能会发生变化,但是我相信总体思路是先显示匹配更多属性的页面,然后再显示更受欢迎的匹配项。
  • 在浏览器中显示最高结果

  • 这是有关Visual Studio外接程序如何执行的代码示例:
  • 接管F1键绑定(bind)
  • 按下F1时
  • ,获取帮助上下文并将其转换为一组名称=值对
  • 将那组“名称=值”对传递给一些外部代码,以对F1请求进行处理。

  • 我遗漏了所有Visual Studio外接程序样板代码-如果您也需要这样做,那么Google中应该有很多示例。
    using System;
    using Extensibility;
    using EnvDTE;
    using EnvDTE80;
    using Microsoft.VisualStudio.CommandBars;
    using System.Resources;
    using System.Reflection;
    using System.Globalization;
    using System.Collections;
    using System.Collections.Generic;
    using System.Text;

    namespace ReplaceF1
    {
    /// <summary>The object for implementing an Add-in.</summary>
    /// <seealso class='IDTExtensibility2' />
    public class Connect : IDTExtensibility2, IDTCommandTarget
    {
    /// <summary>Implements the constructor for the Add-in object. Place your initialization code within this method.</summary>
    public Connect()
    {
    }

    MsdnExplorer.MainWindow Explorer = new MsdnExplorer.MainWindow();

    /// <summary>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;
    if(connectMode == ext_ConnectMode.ext_cm_UISetup)
    {
    object []contextGUIDS = new object[] { };
    Commands2 commands = (Commands2)_applicationObject.Commands;
    string toolsMenuName;

    try
    {
    // If you would like to move the command to a different menu, change the word "Help" to the
    // English version of the menu. This code will take the culture, append on the name of the menu
    // then add the command to that menu. You can find a list of all the top-level menus in the file
    // CommandBar.resx.
    ResourceManager resourceManager = new ResourceManager("ReplaceF1.CommandBar", Assembly.GetExecutingAssembly());
    CultureInfo cultureInfo = new System.Globalization.CultureInfo(_applicationObject.LocaleID);
    string resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName, "Help");
    toolsMenuName = resourceManager.GetString(resourceName);
    }
    catch
    {
    //We tried to find a localized version of the word Tools, but one was not found.
    // Default to the en-US word, which may work for the current culture.
    toolsMenuName = "Help";
    }

    //Place the command on the tools menu.
    //Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:
    Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];

    //Find the Tools command bar on the MenuBar command bar:
    CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];
    CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;

    //This try/catch block can be duplicated if you wish to add multiple commands to be handled by your Add-in,
    // just make sure you also update the QueryStatus/Exec method to include the new command names.
    try
    {
    //Add a command to the Commands collection:
    Command command = commands.AddNamedCommand2(_addInInstance,
    "ReplaceF1",
    "MSDN Advanced F1",
    "Brings up context-sensitive Help via the MSDN Add-in",
    true,
    59,
    ref contextGUIDS,
    (int)vsCommandStatus.vsCommandStatusSupported+(int)vsCommandStatus.vsCommandStatusEnabled,
    (int)vsCommandStyle.vsCommandStylePictAndText,
    vsCommandControlType.vsCommandControlTypeButton);
    command.Bindings = new object[] { "Global::F1" };
    }
    catch(System.ArgumentException)
    {
    //If we are here, then the exception is probably because a command with that name
    // already exists. If so there is no need to recreate the command and we can
    // safely ignore the exception.
    }
    }
    }

    /// <summary>Implements the OnDisconnection method of the IDTExtensibility2 interface. Receives notification that the Add-in is being unloaded.</summary>
    /// <param term='disconnectMode'>Describes how the Add-in is being unloaded.</param>
    /// <param term='custom'>Array of parameters that are host application specific.</param>
    /// <seealso class='IDTExtensibility2' />
    public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom)
    {
    }

    /// <summary>Implements the OnAddInsUpdate method of the IDTExtensibility2 interface. Receives notification when the collection of Add-ins has changed.</summary>
    /// <param term='custom'>Array of parameters that are host application specific.</param>
    /// <seealso class='IDTExtensibility2' />
    public void OnAddInsUpdate(ref Array custom)
    {
    }

    /// <summary>Implements the OnStartupComplete method of the IDTExtensibility2 interface. Receives notification that the host application has completed loading.</summary>
    /// <param term='custom'>Array of parameters that are host application specific.</param>
    /// <seealso class='IDTExtensibility2' />
    public void OnStartupComplete(ref Array custom)
    {
    }

    /// <summary>Implements the OnBeginShutdown method of the IDTExtensibility2 interface. Receives notification that the host application is being unloaded.</summary>
    /// <param term='custom'>Array of parameters that are host application specific.</param>
    /// <seealso class='IDTExtensibility2' />
    public void OnBeginShutdown(ref Array custom)
    {
    }

    /// <summary>Implements the QueryStatus method of the IDTCommandTarget interface. This is called when the command's availability is updated</summary>
    /// <param term='commandName'>The name of the command to determine state for.</param>
    /// <param term='neededText'>Text that is needed for the command.</param>
    /// <param term='status'>The state of the command in the user interface.</param>
    /// <param term='commandText'>Text requested by the neededText parameter.</param>
    /// <seealso class='Exec' />
    public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
    {
    if(neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
    {
    if(commandName == "ReplaceF1.Connect.ReplaceF1")
    {
    status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported|vsCommandStatus.vsCommandStatusEnabled;
    return;
    }
    }
    }


    /// <summary>Implements the Exec method of the IDTCommandTarget interface. This is called when the command is invoked.</summary>
    /// <param term='commandName'>The name of the command to execute.</param>
    /// <param term='executeOption'>Describes how the command should be run.</param>
    /// <param term='varIn'>Parameters passed from the caller to the command handler.</param>
    /// <param term='varOut'>Parameters passed from the command handler to the caller.</param>
    /// <param term='handled'>Informs the caller if the command was handled or not.</param>
    /// <seealso class='Exec' />
    public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
    {
    if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
    {
    if (commandName == "ReplaceF1.Connect.ReplaceF1")
    {
    // Get a reference to Solution Explorer.
    Window activeWindow = _applicationObject.ActiveWindow;

    ContextAttributes contextAttributes = activeWindow.DTE.ContextAttributes;
    contextAttributes.Refresh();

    List<string> attributes = new List<string>();
    try
    {
    ContextAttributes highPri = contextAttributes == null ? null : contextAttributes.HighPriorityAttributes;
    highPri.Refresh();
    if (highPri != null)
    {
    foreach (ContextAttribute CA in highPri)
    {
    List<string> values = new List<string>();
    foreach (string value in (ICollection)CA.Values)
    {
    values.Add(value);
    }
    string attribute = CA.Name + "=" + String.Join(";", values.ToArray());
    attributes.Add(CA.Name + "=");
    }
    }
    }
    catch (System.Runtime.InteropServices.COMException e)
    {
    // ignore this exception-- means there's no High Pri values here
    string x = e.Message;
    }
    catch (System.Reflection.TargetInvocationException e)
    {
    // ignore this exception-- means there's no High Pri values here
    string x = e.Message;
    }
    catch (System.Exception e)
    {
    System.Windows.Forms.MessageBox.Show(e.Message);
    // ignore this exception-- means there's no High Pri values here
    string x = e.Message;
    }

    // fetch context attributes that are not high-priority
    foreach (ContextAttribute CA in contextAttributes)
    {
    List<string> values = new List<string>();
    foreach (string value in (ICollection)CA.Values)
    {
    values.Add (value);
    }
    string attribute = CA.Name + "=" + String.Join(";", values.ToArray());
    attributes.Add (attribute);
    }

    // Replace this call with whatever you want to do with the help context info
    HelpHandler.HandleF1 (attributes);
    }
    }
    }
    private DTE2 _applicationObject;
    private AddIn _addInInstance;
    }
    }

    关于c# - Visual Studio拦截F1帮助命令,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13942657/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com