6

我有自己的 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只为使用自定义工具的程序集做。utils 都没有打印任何错误,所有工作都按计划进行,甚至出现注册表项。但即使在 PC 重新启动后,我的工具也无法工作(发生错误“在此系统上找不到自定义工具 '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
}
4

2 回答 2

6

我的解决方案是制作一个安装项目。我通过将以下内容添加到包的 csproj 文件中,从 pkgdef 文件中获取注册表设置:

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

于 2010-04-09T10:14:26.747 回答
4

这是我上次努力注册自定义工具时的结果。我希望这个说明足够详细,涵盖了所有内容,这样你就不会花太多时间去对抗它。以下 MSDN 文章用作起点。http://msdn.microsoft.com/en-US/library/bb166527(v=vs.80).aspx不幸的是,您不能单独使用它。你真正需要做的是:

  1. 确保程序集已签名。为什么?因为否则您将无法在下面的第 6 步将其放入 GAC。要签署您的程序集,请执行以下步骤:

    1.1。转到项目的“属性”屏幕。

    1.2. 在那里转到“签名”选项卡。

    1.3. 在那里检查Sign the assembly复选框。

  2. 确保您知道程序集的版本号。稍后您将需要此编号来指定ASSEMBLY_VERSION参数。为了获得这个数字,打开项目的Properties文件夹中的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中找到。 注意上面文件路径中框架的版本号(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 需要知道该生成器可以应用于哪些项目类型。因此,您需要获取正确项目类型(C#、VB.NET 等)的 GUID。要找出项目类型的 GUID,您需要在文本编辑器中打开 Visual Studio 项目文件 (*.csproj) 并在ProjectTypeGuids XML 元素中查找 GUID。对于这些 GUID 中的每一个,重复 .REG 文件中最后 3 个条目的块,将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 使用的文本编辑器无法将其标记元素与内容区分开来。

于 2012-10-13T23:17:58.690 回答