2010-04-06 750 views
6

Tengo mi propia herramienta personalizada para Visual Studio 2008 SP1. Se compone de 5 conjuntos: 3 conjuntos con código que se utilizan en gran medida en mis otros proyectos, 1 conjunto-envoltura por encima del SDK VS2008 y un conjunto con la herramienta.¿Cómo implementar una herramienta personalizada de Visual Studio?

Si depuro mi herramienta de Visual Studio, usando la opción "Ejecutar programa externo" con la línea de comando "C: \ Archivos de programa (x86) \ Microsoft Visual Studio 9.0 \ Common7 \ IDE \ devenv.exe" y argumentos "/ ranu/rootsuffix Exp" todo funciona perfectamente.

Después de eso estoy tratando de implementarlo en mi copia de trabajo VS, no en la colmena experimental, haciendo: gacutil /i Asm1.dll para todos mis ensamblajes y haciendo RegAsm Asm1.dll solo para ensamblar con herramienta personalizada. Ninguno de los utils imprime ningún error, todo funciona según lo planeado, incluso las claves de registro aparecen. Pero mi herramienta no funciona (se produjo un error "No se puede encontrar la herramienta personalizada 'TransportGeneratorTool' en este sistema") incluso después de reiniciar la PC. ¿Qué hice mal?

Envoltura parece que:

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

Mi código de la herramienta:

[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()); 
     } 
    } 
} 

resultantes claves de registro:

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 

Aquí está el código de mi atributo personalizado (que se encuentra en conjunto de envoltura):

[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 
} 
+0

Por favor, expanda "no funciona": ¿recibe un error o es como si nunca se hubiera instalado? – Richard

+0

Me gusta que nunca se instaló. –

+0

He actualizado la publicación –

Respuesta

6

Mi solución es hacer un proyecto de instalación. Consigo la configuración del registro del archivo de pkgdef añadiendo lo siguiente al archivo csproj del paquete:

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

Cuando la construcción de mirada en el directorio de salida que debe encontrar un archivo .reg que se puede importar en el proyecto de instalación .

Obviamente, puede ejecutar regpkg.exe desde la línea de comandos si la modificación del proyecto no es una opción.

+0

Muchas gracias. RegPkg salvarme :) –

+0

Errr, estoy haciendo clic para aceptar su respuesta, pero me muestra esto: http://img706.imageshack.us/img706/761/bounty.png ¿Qué debo hacer? –

+0

Según tengo entendido, debe esperar una hora más o menos. – ErvinS

4

Esto es con lo que terminé la última vez cuando tuve problemas para registrar mi herramienta personalizada. Espero que esta instrucción sea lo suficientemente detallada y cubra todo para que no pierda mucho tiempo luchando contra ella. El siguiente artículo de MSDN se usó como punto de partida. http://msdn.microsoft.com/en-US/library/bb166527(v=vs.80).aspx Desafortunadamente no puedes usarlo solo. Lo que realmente necesita hacer es:

  1. Asegúrese de que el conjunto esté firmado. ¿Por qué? Porque de lo contrario no podrá ponerlo en GAC en el paso 6 a continuación. Para firmar su ensamblaje, siga estos pasos:

    1.1. Vaya a la pantalla Properties del proyecto.

    1.2. Una vez allí, vaya a la pestaña Signing.

    1.3. Una vez allí, marque Firme la casilla de verificación.

  2. Asegúrese de conocer el número de versión de su conjunto.Necesitará este número para especificar el parámetro ASSEMBLY_VERSION más adelante. Con el fin de obtener este número se abren las AssemblyInfo.cs archivo en las propiedades carpeta de su proyecto y busque la línea que comienza con: [assembly: AssemblyVersion (

  3. Asegúrese de que conoce el GUID de la clase de generador. Necesitará que especifique el parámetro GENERATOR_GUID más tarde. Para obtener este GUID, abra el archivo con la clase de generador y busque el atributo de clase Guid que decora esta clase, algo así como: [Guid ("17799E85-421B-4684-B59E-650E34ECC718")]

  4. Genere el proyecto

  5. Obtenga la clave de token pública del ensamblaje. Con el fin de hacer eso, tendrá que ejecutar el siguiente comando:

    sn.exe -T ASSEMBLY_FILE

    Usted necesitará esta información más adelante cuando para PUBLIC_TOKEN_KEY. El archivo sn.exe se puede encontrar en C: \ Archivos de programa \ Microsoft SDKs \ Windows \ v8.0A \ bin \ sn.exe Preste atención al número de versión del marco (v8.0A) en el camino de archivo arriba. Debe ser coherente con la versión del marco utilizado para compilar el proyecto.

  6. colocar el ensamblado a la GAC ​​mediante el comando siguiente:

    gacutil.exe/i ASSEMBLY_FILE/f

    Primeros registrada en GAC requiere permisos administrativos. El archivo gacutil.exe se puede encontrar en C: \ Archivos de programa \ Microsoft SDKs \ Windows \ v8.0A \ bin \ netfx 4.0 Herramientas \ gacutil.exe Tenga en cuenta el número de versión del marco (v8 .0A) en el archivo de ruta de arriba. Debe ser coherente con la versión del marco utilizado para compilar el proyecto.

  7. Realice los siguientes cambios en el archivo .REG (consulte a continuación). POR FAVOR: que tanto GENERATOR_GUID y PROJECT_TYPE_GUID necesitan ser suministrado con llaves: {XXXXXXXXXXXX-XXXXXXXX-XXXXXXXXXXXX}

    7.1. Se utiliza el número de versión de reparación de Visual Studio (por ejemplo: 10.0 o 9.0): VS_VERSION

    7.2. Repare el GUID del generador: GENERATOR_GUID

    7.3. Corrija el espacio de nombre del ensamblado: NAMESPACE_NAME

    7.4. Repare el nombre de la clase del generador: GENERATOR_TYPE_NAME

    7.5.Para registrar el generador, Visual Studio necesita saber a qué tipos de proyectos se puede aplicar este generador. Por lo tanto, debe obtener los GUID de los tipos de proyecto adecuados (C#, VB.NET, etc.). Para descubrir los GUID de los tipos de proyecto, necesita abrir un archivo de proyecto de estudio visual (* .csproj) en un editor de texto y buscar GUID en el elemento XML ProjectTypeGuids. Para cada uno de estos GUID, repita el bloque de las últimas 3 entradas en el archivo .REG que reemplaza el PROJECT_TYPE_GUID con el GUID que acaba de encontrar.

    7.6. Corrija la extensión del archivo asociado con la herramienta personalizada: FILE_EXTENSTION

  8. Ejecute el archivo .REG. Es posible que necesite tener permisos administrativos para hacerlo.

archivo 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. Disculpe por no poder distinguir los archivos de posición en el archivo REG, desafortunadamente el editor de texto que utiliza StackOverflow no puede distinguir sus elementos de marcado del contenido.

+2

Por si alguien tiene - como yo - una instalación de Windows 7 64 bits: en ese caso las claves del registro se encuentran en: HKEY_LOCAL_MACHINE \ SOFTWARE \ Wow6432Node \ Microsoft \ VisualStudio \ ... yo no sabía acerca de la Wow6432Node y perdido un tiempo averiguando – PeterFromCologne

Cuestiones relacionadas