Как развернуть специальный инструмент Visual Studio?

У меня есть собственный инструмент для Visual Studio 2008 SP1. Он состоит из 5 сборок: 3 сборки с кодом, который активно используется в других моих проектах, 1 сборка-оболочка над VS2008 SDK и сборка с инструментом.

Если бы я отлаживал свой инструмент из Visual Studio, используя параметр «Запуск внешней программы» с командной строкой «C: \ Program Files (x86) \ Microsoft Visual Studio 9.0 \ Common7 \ IDE \ devenv.exe» и аргументами «/ ranu / rootsuffix Exp "все работает отлично.

После этого я пытаюсь развернуть его в своей рабочей копии VS, а не в экспериментальном улье, выполняя: gacutil /i Asm1.dll для всех моих сборок и RegAsm Asm1.dll только для сборки с помощью специального инструмента. Ни одна из утилит не выдает ошибок, все работает по плану, даже ключи реестра появляются. Но мой инструмент не работает (возникла ошибка «Не удается найти специальный инструмент TransportGeneratorTool в этой системе») даже после перезагрузки ПК. Что я сделал не так?

Обертка выглядит так:

[ComVisible(true)]
public abstract class CustomToolBase : IVsSingleFileGenerator, IObjectWithSite
{
    #region IVsSingleFileGenerator Members
    int IVsSingleFileGenerator.DefaultExtension(out string pbstrDefaultExtension)
    {
        pbstrDefaultExtension = ".cs";
        return 0;
    }

    int IVsSingleFileGenerator.Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] rgbOutputFileContents, out uint pcbOutput, IVsGeneratorProgress pGenerateProgress)
    {
        GenerationEventArgs gea = new GenerationEventArgs(
            bstrInputFileContents,
            wszInputFilePath,
            wszDefaultNamespace,
            new ServiceProvider(Site as Microsoft.VisualStudio.OLE.Interop.IServiceProvider)
                .GetService(typeof(ProjectItem)) as ProjectItem,
            new GenerationProgressFacade(pGenerateProgress)
                );

        if (OnGenerateCode != null)
        {
            OnGenerateCode(this, gea);
        }

        byte[] bytes = gea.GetOutputCodeBytes();

        int outputLength = bytes.Length;
        rgbOutputFileContents[0] = Marshal.AllocCoTaskMem(outputLength);
        Marshal.Copy(bytes, 0, rgbOutputFileContents[0], outputLength);
        pcbOutput = (uint)outputLength;
        return VSConstants.S_OK;
    }
    #endregion

    #region IObjectWithSite Members
    void IObjectWithSite.GetSite(ref Guid riid, out IntPtr ppvSite)
    {
        IntPtr pUnk = Marshal.GetIUnknownForObject(Site);
        IntPtr intPointer = IntPtr.Zero;
        Marshal.QueryInterface(pUnk, ref riid, out intPointer);
        ppvSite = intPointer;
    }

    void IObjectWithSite.SetSite(object pUnkSite)
    {
        Site = pUnkSite;
    }
    #endregion

    #region Public Members
    public object Site { get; private set; }

    public event EventHandler<GenerationEventArgs> OnGenerateCode;

    [ComRegisterFunction]
    public static void Register(Type type)
    {
        using (var parent = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\VisualStudio\9.0", true))
            foreach (CustomToolRegistrationAttribute ourData in type.GetCustomAttributes(typeof(CustomToolRegistrationAttribute), false))
                ourData.Register(x => parent.CreateSubKey(x), (x, name, value) => x.SetValue(name, value));
    }

    [ComUnregisterFunction]
    public static void Unregister(Type type)
    {
        using (var parent = Registry.LocalMachine.OpenSubKey(@"Software\Microsoft\VisualStudio\9.0", true))
            foreach (CustomToolRegistrationAttribute ourData in type.GetCustomAttributes(typeof(CustomToolRegistrationAttribute), false))
                ourData.Unregister(x => parent.DeleteSubKey(x, false));
    }

    #endregion
}

Мой код инструмента:

[ComVisible(true)]
[Guid("55A6C192-D29F-4e22-84DA-DBAF314ED5C3")]
[CustomToolRegistration(ToolName, typeof(TransportGeneratorTool))]
[ProvideObject(typeof(TransportGeneratorTool))]
public class TransportGeneratorTool : CustomToolBase
{
    private const string ToolName = "TransportGeneratorTool";

    public TransportGeneratorTool()
    {
        OnGenerateCode += GenerateCode;
    }

    private static void GenerateCode(object s, GenerationEventArgs e)
    {
        try
        {
            var serializer = new XmlSerializer(typeof (Parser.System));
            using (var reader = new StringReader(e.InputText))
            using (var writer = new StringWriter(e.OutputCode))
            {
                Generator.System = (Parser.System) serializer.Deserialize(reader);
                Generator.System.Namespace = e.Namespace;
                Generator.GenerateSource(writer);
            }
        }
        catch (Exception ex)
        {
            e.Progress.GenerateError(ex.ToString());
        }
    }
}

Результирующие ключи реестра:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators]

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}]

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}\TransportGeneratorTool]
@="TransportGeneratorTool"
"CLSID"="{55a6c192-d29f-4e22-84da-dbaf314ed5c3}"
"GeneratesDesignTimeSource"=dword:00000001
"GeneratesSharedDesignTimeSource"=dword:00000001

Вот код моего настраиваемого атрибута (он находится в сборке оболочки):

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class CustomToolRegistrationAttribute : RegistrationAttribute
{
    public CustomToolRegistrationAttribute(string name, Type customToolType)
    {
        Name = name;
        CustomToolType = customToolType;
    }

    /// <summary>
    /// The type that implements the custom tool.  This starts 
    /// as MyCustomTool by default in the template.
    /// </summary>
    public Type CustomToolType { get; set; }

    public string Name { get; set; }

    #region RegistrationAttribute abstract member implementations
    public override void Register(RegistrationContext context)
    {
        Register(x => context.CreateKey(x), (x, key, value) => x.SetValue(key, value));
    }

    public void Register<T>(Func<string, T> keyCreator, Action<T, string, object> valueCreator)
    {
        var keyName = CreateKeyName(Name);
        var key = keyCreator(keyName);

        valueCreator(key, string.Empty, Name);
        valueCreator(key, "CLSID", CustomToolType.GUID.ToString("B"));
        valueCreator(key, "GeneratesDesignTimeSource", 1);
        valueCreator(key, "GeneratesSharedDesignTimeSource", 1);

        var disposable = key as IDisposable;
        if (disposable != null)
            disposable.Dispose();
    }

    private static string CreateKeyName(string name)
    {
        return string.Format(@"Generators\{0}\{1}", vsContextGuids.vsContextGuidVCSProject, name);
    }

    public override void Unregister(RegistrationContext context)
    {
        Unregister(context.RemoveKey);
    }

    public void Unregister(Action<string> keyRemover)
    {
        keyRemover(CreateKeyName(Name));
    }

    #endregion
}

person Aen Sidhe    schedule 06.04.2010    source источник
comment
Пожалуйста, разверните не работает: вы получаете сообщение об ошибке, или это похоже на то, что он никогда не устанавливался?   -  person Richard    schedule 06.04.2010
comment
Можете ли вы найти ссылку на свой Asm1.dll в HKLM \ CLSID \ {55a6c192-d29f-4e22-84da-dbaf314ed5c3}?   -  person Timores    schedule 06.04.2010
comment
Такого подраздела нет: HKLM \ CLSID. Я нашел ссылку в HKCR \ CLSID \ {55a6c192-d29f-4e22-84da-dbaf314ed5c3}. Есть только параметр по умолчанию, равный полному имени моего класса TransportGeneratorTool.   -  person Aen Sidhe    schedule 06.04.2010


Ответы (2)


Мое решение - сделать установочный проект. Я получаю настройки реестра из файла pkgdef, добавляя следующее в файл csproj пакета:

<Target Name="GeneratePackageRegistryFiles">
  <Exec Command="&quot;$(VSSDK90Install)VisualStudioIntegration\Tools\Bin\RegPkg.exe&quot; /root:Software\Microsoft\VisualStudio\9.0 /codebase &quot;$(TargetPath)&quot; /regfile:&quot;$(OutDir)$(TargetName).reg&quot;" />
</Target>
<PropertyGroup> 
  <BuildDependsOn>$(BuildDependsOn);GeneratePackageRegistryFiles;</BuildDependsOn>
</PropertyGroup>

При создании образа в каталоге вывода вы должны найти файл .reg, который можно импортировать в проект установки.

Очевидно, что вы можете запустить regpkg.exe из командной строки, если изменение проекта невозможно.

person ErvinS    schedule 09.04.2010
comment
Большое спасибо. RegPkg спаси меня :) - person Aen Sidhe; 09.04.2010
comment
Эээ, я нажимаю, чтобы принять ваш ответ, но он показывает мне следующее: img706.imageshack .us / img706 / 761 / bounty.png Что мне делать? - person Aen Sidhe; 09.04.2010
comment
Насколько я понимаю, вам следует подождать еще час или около того. - person ErvinS; 09.04.2010

Это то, к чему я пришел в последний раз, когда изо всех сил пытался зарегистрировать свой собственный инструмент. Я надеюсь, что эта инструкция достаточно подробна и охватывает все, чтобы вы не тратили много времени на борьбу с ней. Следующая статья MSDN была использована в качестве отправной точки. http://msdn.microsoft.com/en-US/library/bb166527(v=vs.80).aspx К сожалению, вы не можете использовать его отдельно. Что вам действительно нужно сделать, так это:

  1. Убедитесь, что сборка подписана. Почему? Потому что в противном случае вы не сможете поместить его в GAC на шаге 6 ниже. Чтобы подписать сборку, выполните следующие действия:

    1.1. Перейдите на экран Свойства проекта.

    1.2. После этого перейдите на вкладку Подписание.

    1.3. После этого установите флажок Подписать сборку.

  2. Убедитесь, что вы знаете номер версии вашей сборки. Этот номер понадобится вам позже, чтобы указать параметр ASSEMBLY_VERSION. Чтобы получить этот номер, откройте файл AssemblyInfo.cs в папке Свойства вашего проекта и найдите строку, начинающуюся с: [assembly: AssemblyVersion (

  3. Убедитесь, что вы знаете GUID класса генератора. Он понадобится вам позже, чтобы указать параметр GENERATOR_GUID. Чтобы получить этот GUID, откройте файл с классом генератора и найдите атрибут класса Guid, который украшает этот класс, например: [Guid ("17799E85-421B-4684-B59E-650E34ECC718" )]

  4. Построить проект

  5. Получите открытый ключ токена сборки. Для этого вам нужно будет выполнить следующую команду:

    sn.exe -T ASSEMBLY_FILE

    Эта информация понадобится вам позже при использовании PUBLIC_TOKEN_KEY. Файл sn.exe находится в папке C: \ Program Files \ Microsoft SDKs \ Windows \ v8.0A \ bin \ sn.exe. Обратите внимание на номер версии framework (v8.0A) в указанном выше пути к файлу. Он должен соответствовать версии фреймворка, используемой для компиляции проекта.

  6. Поместите сборку в GAC, используя следующую команду:

    gacutil.exe / i ASSEMBLY_FILE / f

    Для регистрации в GAC требуются административные разрешения. Файл gacutil.exe находится в папке C: \ Program Files \ Microsoft SDKs \ Windows \ v8.0A \ bin \ NETFX 4.0 Tools \ gacutil.exe. Обратите внимание на номер версии платформы (v8.0A) в указанном выше пути к файлу. Он должен соответствовать версии фреймворка, используемой для компиляции проекта.

  7. Внесите следующие изменения в файл .REG (см. Ниже). ОБРАТИТЕ ВНИМАНИЕ: и GENERATOR_GUID, и PROJECT_TYPE_GUID должны быть указаны в фигурных скобках: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}

    7.1. Используется номер версии исправления Visual Studio (например: 10.0 или 9.0): VS_VERSION

    7.2. Исправьте GUID генератора: GENERATOR_GUID

    7.3. Исправьте пространство имен сборки: NAMESPACE_NAME.

    7.4. Исправьте имя класса генератора: GENERATOR_TYPE_NAME

    7.5. Чтобы зарегистрировать генератор, Visual Studio необходимо знать, к каким типам проектов можно применить этот генератор. Поэтому вам нужно получить GUID соответствующих типов проектов (C #, VB.NET и т. Д.). Чтобы выяснить GUID типов проектов, вам необходимо открыть файл проекта Visual Studio (* .csproj) в текстовом редакторе и найти GUID в XML-элементе ProjectTypeGuids. Для каждого из этих GUID повторите блок из трех последних записей в файле .REG, заменив PROJECT_TYPE_GUID только что найденным GUID.

    7.6. Исправьте расширение файла, связанного с настраиваемым инструментом: FILE_EXTENSTION

  8. Запустите файл .REG. Для этого вам могут потребоваться права администратора.

.REG файл:

Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\CLSID\GENERATOR_GUID]
@="COM+ class: NAMESPACE_NAME.GENERATOR_TYPE_NAME"
"InprocServer32"="C:\\WINDOWS\\system32\\mscoree.dll"
"ThreadingModel"="Both"
"Class"="NAMESPACE_NAME.GENERATOR_TYPE_NAME"
"Assembly"="NAMESPACE_NAME, Version=ASSEMBLY_VERSION, Culture=Neutral, PublicKeyToken=PUBLIC_TOKEN_KEY"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\Generators]

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\Generators\PROJECT_TYPE_GUID]

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\Generators\PROJECT_TYPE_GUID\\.FILE_EXTENSTION]
@="GENERATOR_TYPE_NAME"

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\Generators\PROJECT_TYPE_GUID\GENERATOR_TYPE_NAME]
@="Code generator for whatever you like"
"CLSID"="GENERATOR_GUID"
"GeneratesDesignTimeSource"=dword:00000001

PS. Приносим извинения за то, что не могу выделить места размещения в файле REG, к сожалению, текстовый редактор, который использует StackOverflow, не может отличить элементы разметки от содержимого.

person Trident D'Gao    schedule 13.10.2012
comment
На всякий случай, если у кого-то - как у меня - установлена ​​64-разрядная версия Windows7: в этом случае ключи реестра находятся в: HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ VisualStudio \ ... Я не знал о Wow6432Node и потратил некоторое время на выяснение - person PeterFromCologne; 13.08.2014