gpt4 book ai didi

c# - 如何在Visual Studio中隐藏自定义工具生成的文件

转载 作者:行者123 更新时间:2023-12-02 22:11:01 24 4
gpt4 key购买 nike

我希望隐藏我的自定义工具生成的文件,但是找不到有关此操作的任何文档。

我要寻找的示例是文件后面的WPF代码。这些文件不会显示在Visual Studio项目 View 中,而是随项目一起编译,并且在IntelliSense中可用。文件后面的WPF代码(例如Window1.g.i.cs)由自定义工具生成。

最佳答案

解决方案是创建一个Target,将您的文件添加到Compile ItemGroup中,而不是在.csproj文件中显式添加它们。这样,Intellisense将看到它们并将它们编译到您的可执行文件中,但是它们不会在Visual Studio中显示。

简单示例

您还需要确保将目标添加到CoreCompileDependsOn属性,以便它在编译器运行之前执行。

这是一个非常简单的示例:

<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);AddToolOutput</CoreCompileDependsOn>
</PropertyGroup>

<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="HiddenFile.cs" />
</ItemGroup>
</Target>

如果将其添加到.csproj文件的底部(在 </Project>之前),即使您的“HiddenFile.cs”也不会包含在Visual Studio中,也将包含在您的编译中。

使用单独的.targets文件

通常不将其直接放置在.csproj文件中,而是将其放置在单独的.targets文件中,并用以下符号包围:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
...
</Project>

并使用 <Import Project="MyTool.targets">导入您的.csproj。即使是一次性情况,也建议使用.targets文件,因为它会将您的自定义代码与Visual Studio维护的.csproj中的内容分开。

构造生成的文件名

如果要创建通用工具和/或使用单独的.targets文件,则可能不想显式列出每个隐藏文件。相反,您想从项目中的其他设置生成隐藏文件名。例如,如果您希望所有资源文件在“obj”目录中具有相应的工具生成的文件,则目标将是:
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="@(Resource->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
</ItemGroup>
</Target>

“IntermediateOutputPath”属性是众所周知的“obj”目录,但是,如果您的.targets最终用户对此进行了自定义,则您的中间文件仍会保存在同一位置。如果您希望生成的文件位于主项目目录中,而不是在“obj”目录中,则可以将其保留。

如果只希望您的自定义工具处理现有项目类型的一些文件?例如,您可能想为所有具有“.xyz”扩展名的页面和资源文件生成文件。
<Target Name="AddToolOutput">
<ItemGroup>
<MyToolFiles Include="@(Page);@(Resource)" Condition="'%(Extension)'=='.xyz' />
<Compile Include="@(MyToolFiles->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')"/>
</ItemGroup>
</Target>

请注意,您不能在顶级ItemGroup中使用像%(Extension)这样的元数据语法,但是可以在Target中使用。

使用自定义项目类型(也称为Build Action)

以上处理具有现有项目类型(例如页面,资源或编译)的文件(Visual Studio将此称为“构建操作”)。如果您的商品是一种新型文件,则可以使用自己的自定义商品类型。例如,如果您的输入文件称为“Xyz”文件,则您的项目文件可以将“Xyz”定义为有效的项目类型:
<ItemGroup>
<AvailableItemName Include="Xyz" />
</ItemGroup>

之后,Visual Studio将允许您在文件属性的“生成操作”中选择“Xyz”,从而将其添加到您的.csproj中:
<ItemGroup>
<Xyz Include="Something.xyz" />
</ItemGroup>

现在,您可以使用“Xyz”项目类型来创建工具输出的文件名,就像我们之前使用“Resource”项目类型所做的那样:
<Target Name="AddToolOutput">
<ItemGroup>
<Compile Include="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')" />
</ItemGroup>
</Target>

使用自定义项目类型时,您可以通过将项目映射到其他项目类型(也称为“构建操作”)来使项目也由内置机制处理。如果您的“Xyz”文件实际上是.cs文件或.xaml,或者需要制作它们,这将非常有用

EmbeddedResources。例如,您可以使带有Xyz的“Build Action”的所有文件也被编译:
<ItemGroup>
<Compile Include="@(Xyz)" />
</ItemGroup>

或者,如果您的“Xyz”源文件应存储为嵌入式资源,则可以这样表示:
<ItemGroup>
<EmbeddedResource Include="@(Xyz)" />
</ItemGroup>

请注意,如果将第二个示例放入目标中,则该示例将不起作用,因为直到核心编译之前才对目标进行评估。为了使它在Target中起作用,您必须在PrepareForBuildDependsOn属性中列出目标名称,而不是CoreCompileDependsOn。

从MSBuild调用您的自定义代码生成器

就创建.targets文件而言,您可以考虑直接从MSBuild调用工具,而不是使用单独的预构建事件或Visual Studio有缺陷的“自定义工具”机制。

去做这个:
  • 创建一个类库项目,并引用Microsoft.Build.Framework
  • 添加代码以实现您的自定义代码生成器
  • 添加一个实现ITask的类,并在Execute方法中调用您的自定义代码生成器
  • 在.targets文件中添加UsingTask元素,并在目标中添加对新任务
  • 的调用

    这是实现ITask所需的全部:
    public class GenerateCodeFromXyzFiles : ITask
    {
    public IBuildEngine BuildEngine { get; set; }
    public ITaskHost HostObject { get; set; }

    public ITaskItem[] InputFiles { get; set; }
    public ITaskItem[] OutputFiles { get; set; }

    public bool Execute()
    {
    for(int i=0; i<InputFiles.Length; i++)
    File.WriteAllText(OutputFiles[i].ItemSpec,
    ProcessXyzFile(
    File.ReadAllText(InputFiles[i].ItemSpec)));
    }

    private string ProcessXyzFile(string xyzFileContents)
    {
    // Process file and return generated code
    }
    }

    这是UsingTask元素和一个调用它的Target:
    <UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />


    <Target Name="GenerateToolOutput">

    <GenerateCodeFromXyzFiles
    InputFiles="@(Xyz)"
    OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">

    <Output TaskParameter="OutputFiles" ItemGroup="Compile" />

    </GenerateCodeFromXyzFiles>
    </Target>

    请注意,此目标的Output元素将输出文件列表直接放置到Compile中,因此不需要使用单独的ItemGroup来执行此操作。

    旧的“自定义工具”机制有何缺陷,为什么不使用它

    关于Visual Studio的“自定义工具”机制的注释:在NET Framework 1.x中,我们没有MSBuild,因此我们不得不依靠Visual Studio来构建我们的项目。为了在生成的代码上获得Intellisense,Visual Studio具有一种称为“自定义工具”的机制,可以在文件的“属性”窗口中对其进行设置。该机制从几个方面有根本性的缺陷,这就是为什么将其替换为MSBuild目标的原因。 “自定义工具”功能的一些问题是:
  • 每当编辑和保存文件时(而不是在编译项目时),“自定义工具”都会构造生成的文件。这意味着从外部修改文件的任何内容(例如版本控制系统)都不会更新生成的文件,并且您通常会在可执行文件中获得陈旧的代码。
  • 除非收件人同时拥有Visual Studio和“自定义工具”,否则“自定义工具”的输出必须随源树一起提供。
  • 必须在注册表中安装“自定义工具”,不能简单地从项目文件中引用它。
  • “自定义工具”的输出未存储在“obj”目录中。

  • 如果您使用的是旧的“自定义工具”功能,强烈建议您切换到使用MSBuild任务。它可以与Intellisense很好地配合使用,并允许您甚至不安装Visual Studio即可构建项目(您只需要NET Framework)。

    自定义构建任务何时运行?

    通常,您的自定义构建任务将运行:
  • 如果生成的文件不是最新的
  • ,则在Visual Studio打开解决方案时在后台运行
  • 每当您在Visual Studio中保存输入文件之一时,在后台
  • 每次构建时,如果生成的文件不是最新的
  • 任何时候重建

  • 更准确地说:
  • 在Visual Studio启动时以及每次在Visual Studio中保存任何文件时,都会运行IntelliSense增量版本。如果输出文件丢失,则任何输入文件都将比生成器输出更新,这将运行生成器。
  • 每当您在Visual Studio中使用任何“生成”或“运行”命令(包括菜单选项并按F5键)时,或者从命令行运行“MSBuild”时,都会运行常规增量生成。像IntelliSense增量构建一样,如果生成的文件不是最新的
  • ,它也将仅运行生成器
  • 每当您在Visual Studio中使用任何“重新构建”命令时,或者从命令行运行“MSBuild/t:Rebuild”时,都会运行常规的完整构建。如果有输入或输出,它将始终运行发电机。

  • 您可能需要强制生成器在其他时间运行,例如某些环境变量发生更改时,或者强制其在后台同步运行。
  • 要使生成器即使在没有任何输入文件更改的情况下也可以重新运行,最好的方法通常是向目标添加一个附加Input,这是存储在“obj”目录中的虚拟输入文件。然后,只要环境变量或某些外部设置发生变化而迫使您的生成器工具重新运行,则只需触摸此文件(即创建该文件或更新其修改日期)。
  • 要强制生成器同步运行,而不是等待IntelliSense在后台运行,只需使用MSBuild来构建您的特定目标。这可以像执行“MSBuild/t:GenerateToolOutput”一样简单,或者VSIP可以提供一种调用自定义构建目标的内置方法。或者,您可以简单地调用Build命令并等待其完成。

  • 请注意,本节中的“输入文件”是指Target元素的“Inputs”属性中列出的任何内容。

    最后的笔记

    您可能会从Visual Studio收到警告,它不知道是否信任您的自定义工具.targets文件。要解决此问题,请将其添加到HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\MSBuild\SafeImports注册表项中。

    这是一个实际的.targets文件的摘要,其中包含所有内容:
    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <PropertyGroup>
    <CoreCompileDependsOn>$(CoreCompileDependsOn);GenerateToolOutput</CoreCompileDependsOn>
    </PropertyGroup>

    <UsingTask TaskName="MyNamespace.GenerateCodeFromXyzFiles" AssemblyFile="MyTaskProject.dll" />


    <Target Name="GenerateToolOutput" Inputs="@(Xyz)" Outputs="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">

    <GenerateCodeFromXyzFiles
    InputFiles="@(Xyz)"
    OutputFiles="@(Xyz->'$(IntermediateOutputPath)%(FileName)%(Extension).g.cs')">

    <Output TaskParameter="OutputFiles" ItemGroup="Compile" />

    </GenerateCodeFromXyzFiles>
    </Target>

    </Project>

    如果您有任何疑问或这里有任何您不理解的地方,请告诉我。

    关于c# - 如何在Visual Studio中隐藏自定义工具生成的文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2961753/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com