Visual Studio没有提供从MSBuild任务执行Win32资源编译器的方法,也没有用于创建原始资源的嵌入式功能。因此,您的选择是:
- 按照链接文章中所述手动创建“.res”文件,或者
- 添加构建任务,以便您可以从.csproj调用Win32资源编译器
首先,我将解释五种不同类型的“资源”,它们可以存在于.exe或.dll文件中,其中包括JumpList所需的“Win32资源”。
然后,我将解释如何构建自定义构建任务,允许您在C#或VB.NET可执行文件中嵌入任意Win32资源。
Win32可执行文件中的五种资源类型
在.exe或.dll文件中,有五种不同类型的“资源”:
- Win32资源
- NET Framework“嵌入式资源”
- ResourceSet中的CLR对象
- XAML资源
- WPF资源(ResourceDictionary中的对象)
Win32资源
最初的资源类型是Win32“资源”。这种资源在.rc文件中定义,并且具有编号或名称的资源,每个资源都有一个类型和一组数据。Win32资源编译器rc.exe将.rc文件编译为二进制.res文件,然后可以将其添加到生成的可执行文件中。
使用Win32 FindResource
和LoadResource
函数可以访问Win32资源。
在C++应用程序中通过将它们添加到.rc文件中,将Win32资源嵌入到应用程序中。.rc文件会被编译成一个.res文件并链接到可执行文件中。也可以使用rc.exe程序在事后添加Win32资源。对于C#和VB.NET应用程序,MSBuild可以将预构建的.res文件添加到由Csc或Vbc编译器创建的可执行文件中,或者可以为您构建默认的.res文件。C#和VB.NET都无法从.rc文件构建非默认的.res文件,也没有MSBuild任务可为您完成此操作。
您可以通过在Visual Studio中使用“文件”->“打开”选项打开.exe或.dll文件本身来查看其中的Win32资源。
典型的C、C++或MFC应用程序将具有许多Win32资源,例如每个对话框将由一个资源指定。
典型的WPF应用程序将仅具有C#或VB.NET编译器构建的三个默认Win32资源:版本资源、RT_MANIFEST和应用程序图标。这些资源的内容是根据代码中的程序集属性和.csproj或.vbproj文件中的<ApplicationIcon>
元素构建的。
这是JumpList正在查找的资源类型。
嵌入式资源
一个“嵌入式资源”是.NET Framework的资源。包含这些资源的数据结构由CLR进行管理,以更便于托管代码访问的方式进行管理。每个资源都由字符串名称标识,按照惯例以与资源关联的类的命名空间开头。
嵌入式资源只是具有名称的二进制数据块。实际数据类型由调用者知道或从名称推断,类似于文件系统中的文件。例如,名称以“.jpg”结尾的嵌入式资源很可能是JPEG文件。
使用Assembly.GetManifestResourceStream及其子项GetManifestResourceInfo和GetManifestResourceNames访问嵌入式资源。
通过将文件添加到项目并将构建操作设置为“嵌入式资源”,可以将嵌入式资源嵌入.exe和.dll文件中。
您可以在NET Reflector中打开.exe或.dll文件,并查看“Resources”文件夹以查看嵌入式资源。
嵌入式资源通常在WinForms中使用,但在WPF中几乎从不使用。
多个.NET Framework对象(如字符串和图标)可以组合成单个“资源集”数据结构,存储在.exe作为单个.NET Framework嵌入式资源中。例如,WinForms使用它来存储不易包含在生成的代码中的图标和字符串等内容。
可以使用CLR定义的ResourceManager和ResourceSet类单独检索资源集中的对象。
资源集中的对象由.resx文件在源代码中定义。数据可以直接在.resx文件中(如字符串的情况)或由.resx文件引用(如图标的情况)。当构建项目时,每个.resx文件指定的内容会被序列化为二进制形式,并作为单个内嵌资源存储,其扩展名“.resx”替换为“.resources”。
可以通过在NET Reflector中打开.exe或.dll文件,并打开Resources文件夹,单击“.resources”文件并查看右侧窗格中的项来查看资源集中的对象。
许多WinForms时代的功能通常使用.resx文件和ResourceSets以类似于旧的Win32.rc文件的方式存储多个资源,例如字符串。它们还由WinForms本身用于存储无法放入代码后台的表单设置。
WPF应用程序几乎从不使用资源集中的任意对象,尽管WPF本身使用ResourceSets在内部存储已编译的XAML。
WPF XAML 资源是编译后的 XAML 文件,存储在 ResourceSet 内。资源集内的名称为原始文件名并将“.xaml”替换为“.g.baml”。其内容可以是任何有效的 XAML,最常见的类型包括 Window、Page、UserControl、ResourceDictionary 和 Application。
可以使用 Application.LoadComponent()
或在 WPF 上下文中引用原始 XAML 文件名来加载 WPF 资源。此外,任何带有代码依赖项(由x:Class
指定)的 WPF 资源都将自动加载并应用于创建该类对象时的每个对象,在其 InitializeComponent
调用期间。
通过将 .xaml 文件添加到项目并将其构建操作设置为“Resource”、“Page”或“ApplicationDefinition”,即可创建 WPF 资源。这会导致编译器将文件编译为 BAML 并将其添加到适当的 ResourceSet 中。
可以使用 NET Reflector 并安装 BamlViewer 插件,打开 .exe 或 .dll 文件以查看其中的 XAML 资源,选择菜单中的“Tools→BAML Viewer”,并使用 BAML Viewer 浏览到 .resources 文件内的特定 .g.baml 文件。
ResourceDictionary 中的 WPF 资源
在 WPF 中,几乎所有所谓的“资源”都是 ResourceDictionary 中的条目。ResourceDictionaries 在 XAML 中描述,可以在其他对象(例如 Windows 和 UserControls)中或仅包含 ResourceDictionary 的单独 XAML 文件中描述。每个 ResourceDictionary 都由“x:Key”标识,可以是任何对象类型。资源本身也可以是任何对象类型。
WPF 资源可以使用 {StaticResource}
和 {DynamicResource}
标记扩展来引用 XAML,也可以使用 FindResource
在代码中加载。
将 WPF 资源添加到 ResourceDictionary 中,可以将其添加到包含 ResourceDictionary 的 XAML 文件中,然后在 <ResourceDictionary>
元素内部赋予它们一个 x:Key
属性。
WPF 资源在 WPF 中广泛使用,包括笔刷、样式、数据、几何图形、模板等。
可以通过浏览上述 XAML 资源并查看 ResourceDictionary 标记内部的资源来查看 .exe 或 .dll 中的 WPF 资源。
在 C# 或 VB.NET 可执行文件中包含 Win32 资源
如何轻松地将任意 Win32 资源嵌入 C# 或 VB.NET .exe 中
从上面的讨论中,您会注意到除了 Win32 资源外,可以轻松地将每种类型的资源添加到 C# 或 VB.NET 应用程序中。为了使此过程更加容易,您可以添加一个额外的构建任务和目标,如下所示:
- 构建一个包含单个“Win32ResourceCompiler”构建任务的项目并编译它
- 创建一个“.targets”文件,其中包含一个单独的目标,使用此任务自动将.rc文件构建为.res文件
- 设置项目使用生成的.res文件
该任务非常简单:
public class Win32ResourceCompiler : ToolTask
{
public ITaskItem Source { get; set; }
public ITaskItem Output { get; set; }
protected override string ToolName { get { return "rc.exe"; } }
protected override string GenerateCommandLineCommands()
{
return @"/r /fo """ + Output.ItemSpec + @""" """ + Source.ItemSpec + @"""";
}
protected override string GenerateFullPathToTool()
{
}
}
.targets文件也非常简单。它将是以下内容:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="SomeNamespace.Win32ResourceCompiler" AssemblyFile="Something.dll" />
<PropertyGroup>
<CoreCompileDependsOn>$(CoreCompileDependsOn);CompileWin32RCFile</CoreCompileDependsOn>
</PropertyGroup>
<Target Name="CompileWin32RCFile" Outputs="@(Win32RCFile->'%(filename).res')">
<Win32ResourceCompiler
Source="@(Win32RCFile)"
Output="@(Win32RCFile->'%(filename).res')" />
</Target>
</Project>
现在,在您的.csproj文件中,添加对您的.targets文件的引用:
<Import Project="Win32ResourceCompiler.targets" />
当然,您需要将.rc文件的文件类型设置为Win32RCFile:
<ItemGroup>
<Win32RCFile Include="MyWin32Resources.rc" />
</ItemGroup>
使用这个设置,您可以创建一个传统的 Win32 .rc 文件来指定所有的 Win32 资源,包括版本、清单、应用程序图标以及您想要的其他所有图标。每次编译时,所有这些 Win32 资源都将添加到您的 .exe 文件中。
这需要一点时间来设置,但从长远来看比手动编辑 .res 文件更加令人满意和简单。
您可以像这样在您的 .rc 文件中指定多个图标:
1 ICON ApplicationIcon.ico
2 ICON JumpListIcon.ico
3 ICON AnotherIcon.ico
这里是关于在.rc文件中可以使用的所有资源定义语句的文档。
此外请注意,上述的.targets文件是匆忙打出来的,尚未经过测试。有关MSBuild(.csproj和.targets)文件语法的文档可以在这里和这里找到,.targets文件的好示例可以在c:\Windows\Microsoft.NET\Framework\v3.5目录中找到)。