Создание пакета VSIX для расширения контекстного меню проводника управления версиями TFS

Я пытаюсь создать пакет VSIX для расширения функциональности контекстного меню правого клика TFS 2012 при нажатии на ветку. Я не хочу использовать надстройку. это должен быть пакет, который другие разработчики могут установить напрямую. Настроенные пункты меню должны появиться в контекстном меню проводника управления версиями после установки расширения. Я не могу получить образец для этого требования или не могу получить надлежащий источник документации. Один из примеров, который я нашел, - это "Инструмент для ветки сообщества TFS", который представляет собой похожую функциональность, которую я ищу, но я не могу получить ее исходный код.

Ценю твою помощь.

3 ответа

Я предполагаю, что вы знакомы с файлом.vsct, командой /menu/groups Guids/Id (все это задокументировано в MSDN). Таким образом, вопрос будет в том, какой это Guid/Id группы внутри контекстного меню Source Control Explorer.

Предполагая, что вы можете захотеть, чтобы ваша команда находилась ниже пункта меню "Получить последнюю версию" контекстного меню файла, код будет выглядеть так:

 <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>

Опираясь на ответ Карлоса Кинтеро: Если вам нужно поместить команду в любое другое место в контекстном меню "Управление исходным кодом", вам нужен правильный идентификатор. Используя EnableVSIPLogging, вы можете найти информацию только для команд и их родительских меню, но не для групп.

Чтобы найти групповые идентификаторы (или любые другие идентификаторы в этом отношении), используемые в Source Control Explorer, вы можете выполнить следующие действия (для VS2015):

  1. Decompile 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 декомпилировать файл.

Теперь у вас есть оригинальный файл.vsct, который использовался для создания плагина TFS. Здесь вы можете посмотреть все идентификаторы.

Чтобы начать поиск элементов меню в TfsMenu.vsct, вы можете включить EnableVSIPLogging:
добавлять HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\14.0\General\EnableVSIPLogging как DWORD32 со значением 1,
Теперь в Visual Studio, когда удерживаете клавиши Ctrl+Shift при наведении меню или щелчке по пунктам меню в Source Control Explorer, появляется сообщение с информацией об этом элементе, включая GUID и идентификатор этого элемента меню / меню.

@ Эрик Я был так рад встретиться с твоим объяснением о том, как извлечь vsct, и очень старался понять, как это сделать. Чтобы разъяснить ваш ответ, я преобразовал его в код. Поделиться здесь на случай, если кому-то будет интересно.

static void Main(string[] args)
{
    /*
        Extract menus from extensions
        http://stackru.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();
}
Другие вопросы по тегам