Как развернуть пользовательский инструмент Visual Studio?
У меня есть свой собственный инструмент для Visual Studio 2008 SP1. Он состоит из 5 сборок: 3 сборки с кодом, которые интенсивно используются в других моих проектах, 1 сборка-обертка над SDK VS2008 и сборка с инструментом.
Если бы я отлаживал свой инструмент в Visual Studio, используя опцию "Запускать внешнюю программу" с командной строкой "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe" и аргументы "/ranu /rootuffix 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
}
2 ответа
Мое решение состоит в том, чтобы сделать проект установки. Я получаю параметры реестра из файла pkgdef, добавив следующее в файл csproj пакета:
<Target Name="GeneratePackageRegistryFiles">
<Exec Command=""$(VSSDK90Install)VisualStudioIntegration\Tools\Bin\RegPkg.exe" /root:Software\Microsoft\VisualStudio\9.0 /codebase "$(TargetPath)" /regfile:"$(OutDir)$(TargetName).reg"" />
</Target>
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn);GeneratePackageRegistryFiles;</BuildDependsOn>
</PropertyGroup>
При сборке смотрите в выходном каталоге, вы должны найти файл.reg, который вы можете импортировать в проекте установки.
Очевидно, что вы можете запустить regpkg.exe из командной строки, если изменение проекта невозможно.
Это то, с чем я столкнулся в прошлый раз, когда изо всех сил пытался зарегистрировать свой пользовательский инструмент. Я надеюсь, что эта инструкция достаточно подробная и охватывает все, поэтому вы не будете тратить много времени на борьбу с ней. Следующая статья MSDN была использована в качестве отправной точки. http://msdn.microsoft.com/en-US/library/bb166527(v=vs.80).aspx К сожалению, вы не можете использовать его в одиночку. Что вам действительно нужно сделать, это:
Убедитесь, что сборка подписана. Зачем? Потому что иначе вы не сможете поместить его в GAC на шаге 6 ниже. Чтобы подписать вашу сборку, выполните следующие действия:
1.1. Перейдите на экран свойств проекта.
1.2. После этого перейдите на вкладку " Подписание ".
1.3. После этого установите флажок Подписать сборку.
Убедитесь, что вы знаете номер версии вашей сборки. Этот номер понадобится вам позже, чтобы указать параметр ASSEMBLY_VERSION. Чтобы получить это число, откройте файл AssemblyInfo.cs в папке " Свойства " вашего проекта и найдите строку, начинающуюся с: [assembly: AssemblyVersion(
Убедитесь, что вы знаете GUID класса генератора. Он понадобится вам позже, чтобы указать параметр GENERATOR_GUID. Чтобы получить этот GUID, откройте файл с классом генератора и найдите атрибут класса Guid, который украшает этот класс, что-то вроде: [Guid("17799E85-421B-4684-B59E-650E34ECC718")]
Постройте проект
Получить открытый токен-ключ сборки. Для этого вам нужно будет выполнить следующую команду:
sn.exe -T ASSEMBLY_FILE
Эта информация понадобится вам позже для PUBLIC_TOKEN_KEY. Файл sn.exe можно найти в C:\Program Files\Microsoft SDKs\Windows\v8.0A\bin\sn.exe. Обратите внимание на номер версии платформы (v8.0A) в пути к файлу выше. Он должен соответствовать версии фреймворка, использованного для компиляции проекта.
Поместите сборку в 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) в пути к файлу. выше. Он должен соответствовать версии фреймворка, использованного для компиляции проекта.
Внесите следующие изменения в файл.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 повторите блок из 3 последних записей в файле.REG, заменив PROJECT_TYPE_GUID на только что найденный GUID.
7,6. Исправьте расширение файла, связанного с пользовательским инструментом: FILE_EXTENSTION
Запустите файл.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, к сожалению, текстовый редактор, который использует Stackru, не может отличить элементы разметки от содержимого.