将图标嵌入WPF应用程序作为资源

Sco*_*den 11 wpf resources embedded-resource

我试图在我的WPF应用程序中嵌入一个图标,以便我可以使用以下代码将其拉出来作为Windows 7 JumpList中的图标使用:

newScene.IconResourcePath = System.Reflection.Assembly.GetEntryAssembly().Location;
newScene.IconResourceIndex = 0;
Run Code Online (Sandbox Code Playgroud)

我已经使用以下方法使用它:http://dennisdel.com/?p = 38

但是,它似乎不是最好的方法,似乎应该有一种更简单的方法将图标资源嵌入到我的应用程序中,同时仍然在我的程序的应用程序属性中选中" Icon and Manifest "选项.

我尝试了很多方法,包括将图标构建操作设置为资源和嵌入式资源,但每次在资源编辑器中打开我的.exe时,都不会显示图标.

有什么建议?

Ray*_*rns 27

Visual Studio没有提供从MSBuild任务执行Win32资源编译器的方法,也没有用于创建资源的嵌入式功能创建原始资源.因此,您的选择是:

  1. 如链接文章中所述,"手动"创建.res文件,或
  2. 添加构建任务,以便从.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 FindResourceLoadResource函数访问Win32资源.

通过将它们添加到.rc文件中,将Win32资源嵌入到C++应用程序中,该文件被编译为.res文件并链接到可执行文件中.它们也可以在事后使用rc.exe程序添加.对于C#和VB.NET应用程序,MSBuild可以将预先构建的.res文件添加到它通过Csc或Vbc编译器创建的可执行文件中,或者它可以为您构建一个默认文件.C#和VB.NET都没有能力从.rc文件构建非默认.res文件,并且没有MSBuild任务可以为您执行此操作.

您可以使用文件 - >打开在Visual Studio中打开.exe或.dll文件本身来查看.exe或.dll中的Win32资源.

典型的C,C++或MFC应用程序将具有许多Win32资源,例如,每个对话框都将由资源指定.

典型的WPF应用程序将只具有由C#或VB.NET编译器构造的三个默认Win32资源:版本资源,RT_MANIFEST和应用程序图标.这些资源的内容由代码中的Assembly属性和<ApplicationIcon>.csproj或.vbproj文件中的元素构成.

这是JumpList正在寻找的那种资源.

嵌入式资源

"嵌入式资源"是.NET Framework资源.包含这些资源的数据结构由CLR以更有利于托管代码访问的方式进行管理.每个资源都由字符串名称标识,按照约定,该名称以与资源关联的类的名称空间开头.

嵌入式资源只是一个带有名称的二进制数据.实际数据类型可以是调用者已知的,也可以从名称中推断出来,类似于文件系统中的文件.例如,名称以".jpg"结尾的嵌入式资源可能是JPEG文件.

使用Assembly.GetManifestResourceStream及其兄弟姐妹GetManifestResourceInfo和访问嵌入式资源GetManifestResourceNames.

嵌入式资源通过将文件添加到项目并将构建操作设置为"嵌入式资源"而嵌入到.exe和.dll文件中.

您可以通过在.NET Reflector中打开它并查看"Resources"文件夹来查看.exe或.dll中的嵌入式资源.

嵌入式资源通常在WinForms中使用,但几乎从不使用WPF.

资源集(.resx/.resources)

多个.NET Framework对象(如字符串和图标)可以组合在一起形成一个"资源集"数据结构,该数据结构作为单个.NET Framework嵌入式资源存储在.exe中.例如,WinForms使用它来存储诸如图标和字符串之类的东西,这些东西不容易包含在生成的代码中.

可以使用CLR定义的ResourceManagerResourceSet类单独检索资源集中的对象.

资源集中的对象由源代码中的.resx文件定义.数据可以直接在.resx文件中(如字符串的情况)或由.resx文件引用(如图标的情况).构建项目时,每个.resx文件指定的内容将序列化为二进制形式,并存储为单个嵌入式资源,扩展名为".resx",替换为".resources".

您可以通过打开.NET Reflector中的.exe或.dll,打开Resources文件夹,单击".resources"文件,然后查看右侧窗格中的项目来查看资源集中的对象.

许多WinForms时代的特性通常使用.resx文件和ResourceSets,其方式与旧的Win32 .rc文件类似,用于存储多个资源,如字符串.它们也被WinForms本身用于在不能进入代码的表单上存储设置.

WPF应用程序几乎从不在ResourceSets中使用任意对象,尽管WPF本身在内部使用ResourceSets来存储已编译的XAML.

XAML资源

WPF XAML资源是存储在ResourceSet中的已编译XAML文件.资源集内的名称是原始文件名,".xaml"替换为".g.baml".内容可以是任何有效的XAML,最常见的类型是Window,Page,UserControl,ResourceDictionary和
Application.

WPF资源可以使用Application.LoadComponent()或通过引用WPF上下文中的原始XAML文件名来加载.此外,任何具有代码隐藏(由指定x:Class)的WPF资源将自动加载并应用于在InitializeComponent调用期间由该类创建的每个对象.

通过将.xaml文件添加到项目并将其构建操作设置为"Resource","Page"或"ApplicationDefinition"来创建WPF资源.这会导致编译器将文件编译为BAML并将其添加到适当的ResourceSet.

您可以通过在安装了BamlViewer加载项的NET Reflector中打开它,从菜单中选择工具 - > BAML查看器,并使用BAML查看器浏览到特定的.g.baml,来查看.exe或.dll中的XAML资源. .resources中的文件.

ResourceDictionary中的WPF资源

在WPF中,几乎所有被称为"资源"的都是ResourceDictionary中的条目.ResourceDictionaries在XAML中描述,或者在其他对象(如Windows和UserControls)中,或者在仅包含ResourceDictionary的单独XAML文件中描述.每个都由"x:Key"标识,可以是任何对象类型.资源本身也可以是任何对象类型.

WPF资源可以使用{StaticResource}{DynamicResource}标记扩展在XAML中引用,也可以使用代码加载到代码中FindResource.

通过将WPF资源添加到包含<ResourceDictionary>元素内的ResourceDictionary 并为其提供x:Key属性的XAML文件,将其添加到ResourceDictionary .

WPF资源在WPF中广泛使用,包括画笔,样式,数据,几何,模板等.

您可以通过浏览上面描述的XAML资源以及查看ResourceDictionary标记内的每个资源来查看.exe或.dll中的WPF资源,以查看资源本身.


在C#或VB.NET可执行文件中包含Win32资源

如何轻松地将任意Win32资源嵌入到C#或VB.NET .exe中

您将从上面的讨论中注意到,除了 Win32资源之外,很容易将每种类型的资源添加到C#或VB.NET应用程序中.为了简化这一过程,您可以添加其他构建任务和目标.方法如下:

  1. 构造一个包含单个"Win32ResourceCompiler"构建任务的项目并进行编译
  2. 创建一个包含单个目标的.targets文件,该目标使用此任务自动将.rc文件构建到.res文件中
  3. 将项目设置为使用生成的.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()
  {
    // TODO: Return path to rc.exe in your environment
  }
}
Run Code Online (Sandbox Code Playgroud)

.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>
Run Code Online (Sandbox Code Playgroud)

现在在.csproj文件中,添加对.targets文件的引用:

<Import Project="Win32ResourceCompiler.targets" />
Run Code Online (Sandbox Code Playgroud)

当然,您需要为.rc文件提供Win32RCFile的文件类型:

<ItemGroup>
  <Win32RCFile Include="MyWin32Resources.rc" />
</ItemGroup>
Run Code Online (Sandbox Code Playgroud)

通过此设置,您可以创建传统的Win32 .rc文件来指定所有Win32资源,包括您的版本,清单,应用程序图标以及任意数量的其他图标.每次编译时,所有这些Win32资源都将添加到.exe文件中.

设置需要一些时间,但从长远来看比手动编辑.res文件更令人满意和简单.

您可以在.rc文件中指定多个图标,如下所示:

1 ICON ApplicationIcon.ico
2 ICON JumpListIcon.ico
3 ICON AnotherIcon.ico
Run Code Online (Sandbox Code Playgroud)

以下是您可以在.rc文件中使用的所有资源定义语句的文档.

另请注意,上面的.targets文件是在一时冲动时输入的,尚未经过测试.可以在此处此处找到有关MSBuild(.csproj和.targets)文件语法的文档,并且可以在c:\ Windows\Microsoft.NET\Framework\v3.5目录中找到.targets文件的良好示例.