【问题标题】:Creating VSIX package for TFS Source control explorer context menu extension为 TFS 源代码管理资源管理器上下文菜单扩展创建 VSIX 包
【发布时间】:2015-04-23 18:08:00
【问题描述】:

我正在尝试创建 VSIX 包以在单击分支时扩展 TFS 2012 源代码控制右键单击上下文菜单的功能。 我不想使用加载项。这必须是其他开发人员可以直接安装的包。 安装扩展后,自定义菜单项需要出现在源代码管理资源管理器上下文菜单中。我无法获得任何满足此要求的样本或无法获得正确的文档来源。我找到的一个示例是“TFS 社区分支工具”,这是我正在寻找的类似功能,但我无法获得它的源代码。

感谢您的帮助。

【问题讨论】:

    标签: visual-studio-2012 vsix vsixmanifest


    【解决方案1】:

    我假设您熟悉 .vsct 文件、命令/菜单/组 Guids/Id 的东西(所有这些都记录在 MSDN 中)。因此,问题将是源代码管理资源管理器的上下文菜单中组的 Guid/Id。

    猜测您可能希望您的命令位于文件上下文菜单的“获取最新版本”菜单项下方,代码为:

     <Commands package="guidVSMyPackagePkg">
    
         <Buttons>
          <Button guid="guidVSMyPackageCmdSet" id="cmdidMyCommand" priority="0x0100" type="Button">
             <Parent guid="guidSourceControlExplorerMenuGroup" id="SourceControlExplorerMenuGroupId"/>
            <Strings>
              <ButtonText>My Command</ButtonText>
            </Strings>
          </Button>
        </Buttons>
      </Commands>
    
      <Symbols>
        <GuidSymbol name="guidVSMyPackagePkg" value="{...}" />
    
        <GuidSymbol name="guidVSMyPackageCmdSet" value="{...}">
          <IDSymbol name="cmdidMyCommand" value="0x0100" />
        </GuidSymbol>
    
         <GuidSymbol name="guidSourceControlExplorerMenuGroup" value="{ffe1131c-8ea1-4d05-9728-34ad4611bda9}">
             <IDSymbol name="SourceControlExplorerMenuGroupId" value="0x1111" />
         </GuidSymbol>
       </Symbols>
    

    【讨论】:

    • 嗨 Carlos,感谢您提供的信息,我可以在源代码管理资源管理器中添加命令。
    • 现在单击该命令我可以打开我的自定义表单,我只需要右键单击并单击命令的 TFS 路径。我怎样才能在那里获得 TFS 路径。
    • @CarlosQuintero:这行得通。但是,您假设新命令应该出现在“获取最新版本”之后,并提供了该组的组 ID (0x1111)。但是,我需要在合并之后将新项目添加到“分支和合并”菜单的顶部。我将如何查找该组的 GUID 和 ID? EnableVSIPLogging 仅显示有关命令及其父菜单的信息,而不是组。
    【解决方案2】:

    基于 Carlos Quintero 的回答: 如果您需要将命令放在源代码管理资源管理器上下文菜单中的任何其他位置,则需要正确的 ID。使用 EnableVSIPLogging,您只能找到命令及其父菜单的信息,而不是组。

    要查找源代码管理资源管理器中使用的组 ID(或任何其他 ID),您可以按照以下步骤操作(对于 VS2015):

    1. 反编译 Microsoft.VisualStudio.TeamFoundation.VersionControl.dll(例如使用 JetBrains dotPeek)。
    2. 打开Resources\HatPackage.resources
    3. 查找 1000.ctmenu 并复制 Base64 数据。
    4. 将数据从 Base64 转换为字节。
    5. 将文件中的字节保存为TfsMenu.cto(扩展名需要为.cto,并且需要位于具有写入权限的位置才能进行下一步操作)。
    6. 运行"C:\Program Files (x86)\Microsoft Visual Studio 14.0\VSSDK\VisualStudioIntegration\Tools\Bin\vsct.exe" TfsMenu.cto TfsMenu.vsct反编译文件。

    现在您有了用于制作 TFS 插件的原始 .vsct 文件。在这里您可以查找所有 ID。

    为了让您开始在 TfsMenu.vsct 中查找菜单项,您可以启用 EnableVSIPLogging:
    HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\14.0\General\EnableVSIPLogging 添加为DWORD32,其值为1
    现在,在 Visual Studio 中,当在源代码管理资源管理器中悬停菜单或单击菜单项时按住 Ctrl+Shift 时,会弹出一个消息框,其中包含有关该项目的信息,包括该菜单/菜单项的 GUID 和 ID

    【讨论】:

      【解决方案3】:

      @Erik 我很高兴看到您对提取 vsct 的解释,因为我非常努力地想知道如何做这件事。只是为了说明您的答案,我将其转换为代码。在这里分享,以防有人感兴趣。

      static void Main(string[] args)
      {
          /*
              Extract menus from extensions
              http://stackoverflow.com/questions/29831181/creating-vsix-package-for-tfs-source-control-explorer-context-menu-extension
           */
      
          try
          {
              string vsctPath = ConfigurationManager.AppSettings["VSCTPath"];
              if (!File.Exists(vsctPath))
              {
                  WriteConsole("The path to the vsct.exe could not be found. Please edit the app.config to set the right executable path.", ConsoleColor.Yellow);
                  return;
              }
      
              //TODO: Convert to a command line argument
              string dllPath = @"C:\Program Files (x86)\Microsoft SQL Server\130\Tools\Binn\ManagementStudio\Extensions\Application\Microsoft.SqlServer.Management.SqlStudio.Explorer.dll";
      
      
              var assembly = Assembly.LoadFrom(dllPath);
      
              if (assembly == null)
              {
                  WriteConsole("Could not load assembly.", ConsoleColor.Yellow);
                  return;
              }
      
              var resourceName = assembly.GetManifestResourceNames().FirstOrDefault(n => Regex.IsMatch(n, @"VSPackage\.resources", RegexOptions.IgnoreCase));
      
              if (String.IsNullOrWhiteSpace(resourceName))
              {
                  WriteConsole("Could find VSPackage.resources in assembly.", ConsoleColor.Yellow);
                  return;
              }
      
              var resourceManager = new ResourceManager(Path.GetFileNameWithoutExtension(resourceName), assembly);
      
              if (resourceManager == null)
              {
                  WriteConsole("Could find load the resource " + resourceName + ".", ConsoleColor.Yellow);
                  return;
              }
      
              var menus = resourceManager.GetObject("Menus.ctmenu") as byte[];
      
              if (menus == null)
              {
                  WriteConsole("Could find Menus.ctmenu resource in VSPackage.resources.", ConsoleColor.Yellow);
                  return;
              }
      
              string dir = Path.Combine(Path.GetTempPath(), "PackageMenus");
              string fileName = Path.GetFileNameWithoutExtension(dllPath) + ".cto";
      
              Directory.CreateDirectory(dir);
              Directory.SetCurrentDirectory(dir);
      
              File.WriteAllBytes(Path.Combine(dir, fileName), menus);
      
              string processArgs = String.Format(@"{0} {1}.vsct", fileName, fileName);
              var pi = new ProcessStartInfo(vsctPath, processArgs);
      
              pi.UseShellExecute = false;
              pi.RedirectStandardError = true;
              pi.RedirectStandardOutput = true;
              var ret = Process.Start(pi);
      
              var output = ret.StandardOutput.ReadToEnd();
              var errors = ret.StandardError.ReadToEnd();
      
              Console.WriteLine(output);
      
              if (!string.IsNullOrWhiteSpace(errors))
              {
                  Console.Write("Errors: ");
                  WriteConsole(errors, ConsoleColor.Red);
              }
              else
              {
                  Console.WriteLine("New files written to: " + dir);
              }
          }
          catch(Exception ex)
          {
              WriteConsole(ex.ToString(), ConsoleColor.Red);
          }
          finally
          {
              Console.WriteLine("\r\nPress any key to continue.");
              Console.ReadKey(true);
          }
      
      }
      
      private static void WriteConsole(string message, ConsoleColor color = ConsoleColor.White)
      {
          Console.ForegroundColor = color;
          Console.WriteLine(message);
          Console.ResetColor();
      }
      

      【讨论】:

        猜你喜欢
        • 2011-12-24
        • 2015-09-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-04-05
        • 2023-04-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多