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

发布于 2024-09-05 03:13:50 字数 515 浏览 2 评论 0原文

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

newScene.IconResourcePath = System.Reflection.Assembly.GetEntryAssembly().Location;
newScene.IconResourceIndex = 0;

我已使用以下方法使其工作:http://dennisdel.com/?p=38

但是,这似乎不是最好的方法,而且看起来就像应该有一种更简单的方法可以将图标资源嵌入到我的应用程序中,同时仍然保留在我的程序的应用程序属性中选中的“图标和清单”选项。

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

有什么建议吗?

I am trying to embed an icon into my my WPF application so that I can pull it out for use as an icon in the Windows 7 JumpList using the following code:

newScene.IconResourcePath = System.Reflection.Assembly.GetEntryAssembly().Location;
newScene.IconResourceIndex = 0;

I've gotten it to work using the following method: http://dennisdel.com/?p=38

However, it doesn't seem like the best approach and it seems like there should be an easier way to embed an icon resource into my application while still leaving the "Icon and Manifest" option checked in the Application properties for my program.

I've tried numerous methods including setting the icon build action as a resource and an embedded resource, but every time I open my .exe in a resource editor, the icon does not appear.

Any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

So要识趣 2024-09-12 03:13:50

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 资源(内部的对象) a ResourceDictionary)

Win32 资源

原始类型的资源是 Win32“资源”。这种资源在 .rc 文件中定义,并且具有编号或命名的资源,每个资源都有一个类型和一个数据块。

Win32 资源编译器 rc.exe 将 .rc 文件编译为二进制 .res 文件,然后可以将其添加到生成的可执行文件中。

Win32 资源可使用 Win32 FindResourceLoadResource 函数来访问。

Win32 资源通过将其添加到 .rc 文件来嵌入到 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 属性以及 .csproj 或 .vbproj 文件中的 元素构造的。

这就是 JumpList 正在寻找的资源类型。

嵌入式资源

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

嵌入式资源只是带有名称的二进制数据块。实际的数据类型要么由调用者已知,要么从名称推断,类似于文件系统中的文件。例如,名称以“.jpg”结尾的嵌入资源很可能是 JPEG 文件。

使用 Assembly.GetManifestResourceStream 及其同级 GetManifestResourceInfoGetManifestResourceNames 访问嵌入式资源。

通过将文件添加到项目并将生成操作设置为“嵌入资源”,嵌入式资源将嵌入到 .exe 和 .dll 文件中。

您可以通过在 NET Reflector 中打开 .exe 或 .dll 并查看“资源”文件夹来查看 .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”文件,然后查看右侧窗格中的项目。

许多 WinForms 时代的功能通常使用 .resx 文件和资源集,其方式类似于旧的 Win32 .rc 文件,以将多个资源(例如字符串)存储在一起。 WinForms 本身也使用它们来存储无法放入后台代码的表单上的设置。

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

XAML 资源

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

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

WPF 资源是通过将 .xaml 文件添加到项目并将其生成操作设置为“资源”、“页面”或“应用程序定义”来创建的。这会导致编译器将文件编译为 BAML 并将其添加到适当的 ResourceSet。

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

ResourceDictionary 中的 WPF 资源

在 WPF 中,几乎所有所谓的“资源”都是 ResourceDictionary 中的条目。 ResourceDictionaries 在 XAML 中进行描述,或者在 Windows 和 UserControls 等其他对象中,或者在仅包含 ResourceDictionary 的单独 XAML 文件中。每个都由“x:Key”标识,它可以是任何对象类型。资源本身也可以是任何对象类型。

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

通过将 WPF 资源添加到包含 元素内的 ResourceDictionary 的 XAML 文件并为其赋予 x:Key 属性,可以将 WPF 资源添加到 ResourceDictionary。

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

您可以通过如上所述浏览 XAML 资源并查看 ResourceDictionary 中的每个资源来查看 .exe 或 .dll 中的 WPF 资源标签以查看资源本身。


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

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

从上面的讨论中您会注意到,添加每种类型的C# 或 VB.NET 应用程序的资源(Win32 资源除外)。为了使这变得简单,您可以添加额外的构建任务和目标。方法如下:

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

.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)文件语法的文档可以找到 此处此处 ,并且可以在 c:\Windows\Microsoft.NET\Framework\v3.5 目录中找到 .targets 文件的良好示例)。

Visual Studio does not come with a way to execute the Win32 resource compiler from a MSBuild task, and none of its embedded functionality for creating resources created raw resources. Because of this your choices are:

  1. Create the .res file "by hand" as described in the linked article, or
  2. Add a build task so you can call the Win32 resource compiler from your .csproj

First I will explain the differences between the five different kinds of "resources" that can exist in a .exe or .dll file, including "Win32 Resources" that JumpList requires.

I will then explain how to construct a custom build task that allows you to embed arbitrary Win32 Resources in a C# or VB.NET executable.


The five kinds of resources in a Win32 executable

There are five different kinds of "resources" that can exist in a .exe or .dll file:

  • Win32 Resources
  • NET Framework "Embedded Resources"
  • CLR Objects within a ResourceSet
  • XAML Resources
  • WPF Resources (Objects within a ResourceDictionary)

Win32 Resources

The original kind of resource was a Win32 "Resource". This kind of resource is defined in a .rc file and has either numbered or named resources, each of which has a type and a blob of data. The
Win32 resource compiler, rc.exe, compiles the .rc file into a binary .res file can then be added to the resulting executable.

Win32 resources are accessed using the Win32 FindResource and LoadResource functions.

Win32 resources are embedded into C++ applications by adding them to the .rc file, which is compiled to a .res file and linked into the executable. They can also be added after the fact using the rc.exe program. For C# and VB.NET applications, MSBuild can add a prebuilt .res file to the executable it creates via the Csc or Vbc compiler or it can build a default one for you. Neither C# nor VB.NET has the ability to build non-default .res files from .rc files, and there is no MSBuild task to do this for you.

You can view the Win32 Resources in a .exe or .dll by opening the .exe or .dll file itself in Visual Studio using File -> Open.

A typical C, C++ or MFC application will have many Win32 Resources, for example every dialog box will be specified by a resource.

A typical WPF application will have just the three default Win32 resources constructed by the C# or VB.NET compiler: The version resource, the RT_MANIFEST, and the application icon. The contents of these resources are constructed from Assembly attributes in the code and the <ApplicationIcon> element in the .csproj or .vbproj file file.

This is the kind of resource that the JumpList is looking for.

Embedded Resources

An "embedded resource" is a NET Framework resource. The data structure containing these resources is managed by the CLR in a manner more conducive for access by managed code. Each resource is identified by a string name, which by convention begins with the namespace of the class the resource is associated with.

An embedded resource is just a blob of binary data with a name. The actual data type is either known by the caller or inferred from the name, similar to files in a file system. For example, an embedded resource with a name ending in ".jpg" is likely to be a JPEG file.

Embedded resources are accessed using Assembly.GetManifestResourceStream and its siblings GetManifestResourceInfo and GetManifestResourceNames.

Embedded resources are embedded into .exe and .dll files by adding the file to the project and setting the build action to "Embedded Resource".

You can view the Embedded Resources in a .exe or .dll by opening it in NET Reflector and looking at the "Resources" folder.

Embedded resources are commonly used in WinForms but almost never with WPF.

Resource Sets (.resx/.resources)

Multiple NET Framework objects such as strings and icons can be combined together into a single "Resource Set" data struture that is stored in the .exe as a single NET Framework Embedded Resource. For example this is used by WinForms to store things like icons and strings that are not easy to include in the generated code.

Objects in a Resource Set can be retrieved individually using the ResourceManager and ResourceSet classes defined by the CLR.

Objects in a Resource Set are defined in source code by a .resx file. The data can be directly in the .resx file (as in the case of strings) or referenced by the .resx file (as in the case of icons). When the project is built, the content specified by each .resx files is serialized into a binary form and stored as a single Embedded Resource with the extension ".resx" replaced by ".resources".

You can view the objects in a Resource Set by opening the .exe or .dll in NET Reflector, opening the Resources folder, clicking on a ".resources" file, and looking at the items in the right-hand pane.

Many WinForms-era features commonly used .resx files and ResourceSets in a manner similar to the old Win32 .rc files, to store multiple resources such as strings all together. They are also used by WinForms itself for storing settings on a form that cannot go in the code behind.

WPF applications almost never uses arbitrary objects in ResourceSets, though WPF itself uses ResourceSets internally to store compiled XAML.

XAML Resources

A WPF XAML Resource is a compiled XAML file that is stored inside a ResourceSet. The name inside the resource set is the original filename with ".xaml" replaced with ".g.baml". The content can be any valid XAML, the most common types being Window, Page, UserControl, ResourceDictionary, and
Application.

WPF Resources can be loaded using Application.LoadComponent() or by referencing the original XAML file name in a WPF context. In addition, any WPF Resource that has code behind (as specified by x:Class) will automatically be loaded and applied to each object that is created of that class during its InitializeComponent call.

WPF Resources are created by adding a .xaml file to your project and setting its build action to "Resource", "Page", or "ApplicationDefinition". This causes the compiler to compile the file to BAML and add it to the appropriate ResourceSet.

You can view the XAML resources in .exe or .dll by opening it in NET Reflector with the BamlViewer add-in installed, selecting Tools -> BAML Viewer from the menu, and using the BAML Viewer to browse to the specific .g.baml file inside the .resources.

WPF Resources within a ResourceDictionary

In WPF, almost all of what are known as "resources" are entries in a ResourceDictionary. The ResourceDictionaries are described in XAML, either within other objects such as Windows and UserControls, or in separate XAML files that contain only a ResourceDictionary. Each is identified by an "x:Key", which can be any object type. The resources themselves can also be any object type.

WPF resources can be referenced in XAML using the {StaticResource} and {DynamicResource} markup extensions, or can be loaded in code using FindResource.

WPF resources are added to a ResourceDictionary by adding them to the XAML file that contains the ResourceDictionary inside the <ResourceDictionary> element and giving them a x:Key attribute.

WPF resources are used extensively in WPF, including brushes, styles, data, geometries, templates, etc.

You can view the WPF Resources in a .exe or .dll by browsing the XAML Resources as described above and for each one looking inside the ResourceDictionary tags to see the resources themselves.


Including Win32 Resources in a C# or VB.NET executable

How to easily embed arbitrary Win32 Resources into a C# or VB.NET .exe

You will note from the discussion above that it is easy to add every type of resource to your C# or VB.NET application except for Win32 Resources. To make this easy you can add an additional build task and target. Here is how:

  1. Construct a project containing a single "Win32ResourceCompiler" build task and compile it
  2. Create a .targets file that contains a single target that uses this task to automatically build a .rc file into a .res
  3. Set your project to use the resulting .res file

The task is extremely simple:

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

The .targets file is also very simple. It will be something along these lines:

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

Now in your .csproj file, add a reference to your .targets file:

<Import Project="Win32ResourceCompiler.targets" />

And of course you need to give your .rc file a file type of Win32RCFile:

<ItemGroup>
  <Win32RCFile Include="MyWin32Resources.rc" />
</ItemGroup>

With this setup you can create a traditional Win32 .rc file to specify all your Win32 resources, including your version, manifest, application icon, and as many additional icons as you want. Every time you compile, all these Win32 resources will be added to your .exe file.

This takes a little while to set up but is much more satisfying and simpler in the long run than manually editing a .res file.

You can specify multiple icons in your .rc file like this:

1 ICON ApplicationIcon.ico
2 ICON JumpListIcon.ico
3 ICON AnotherIcon.ico

Here is the documentation for all the resource definition statements you can use in a .rc file.

Also note that the above .targets file was typed up on the spur of the moment and has not been tested. Documentation on the syntax of MSBuild (.csproj and .targets) files can be found here and here, and good examples of .targets files can be found in the c:\Windows\Microsoft.NET\Framework\v3.5 directory).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文