简体   繁体   English

如何将图标链接到 Rust Windows 应用程序

[英]How to link an icon to a Rust Windows application

In a Rust desktop application, some version of the window struct is always used, eg, WNDCLASSW .在 Rust 桌面应用程序中,始终使用 window struct的某个版本,例如WNDCLASSW When WNDCLASSW is defined, a class icon may be added through the struct member hIcon .定义WNDCLASSW时,可以通过struct成员hIcon添加一个class 图标 The code excerpt below demonstrates how to include the icon stored in the file Icon.ico .下面的代码摘录演示了如何包含存储在文件Icon.ico中的图标。

...
        let hicon: HICON = LoadImageW(
            0 as HINSTANCE, 
            wide_null("Icon.ico").as_ptr(), 
            IMAGE_ICON, 0, 0, LR_LOADFROMFILE
        ) as HICON;

        let hinstance = GetModuleHandleW(null_mut());

        let mut wc: WNDCLASSW = std::mem::zeroed();
        wc.lpfnWndProc = Some(window_proc);
        wc. hInstance = hinstance;
        wc.hIcon = hicon;
        wc.lpszClassName = name.as_ptr();
...

The icon file is loaded during the program execution and must be stored in the same folder as the exe file.图标文件是在程序执行过程中加载的,必须和exe文件存放在同一个文件夹中。 If the icon file is missing, LoadImageW() returns a NULL handle.如果图标文件丢失, LoadImageW()返回一个NULL句柄。 Setting hIcon to NULL is valid and causes the use of a standard system icon.hIcon设置为NULL是有效的,并且会导致使用标准系统图标。

While this approach produces the required icon, the icon file is loaded during execution and must be delivered along with the exe file.虽然此方法会生成所需的图标,但图标文件是在执行期间加载的,并且必须与exe文件一起交付。 This isn't an acceptable solution;这不是一个可接受的解决方案; the icon should be linked to the exe file and delivered within it.该图标应链接到exe文件并在其中传送。

How do I link an icon to a Rust Windows application and use it there?如何将图标链接到 Rust Windows 应用程序并在那里使用它?

I am aware of this solution , but it generates thousands of lines of errors and warnings during compilation and must be seen as outdated.我知道这个解决方案,但它在编译期间会产生数千行错误和警告,并且必须被视为过时的。 This solution works, but it only adds the exe icon shown in Windows File Explorer , while the class icon (in the taskbar) is unchanged. 此解决方案有效,但它仅添加了 Windows File Explorer中显示的exe 图标,而class 图标(在任务栏中)未更改。 Several other solutions for the exe icon may be found on the inte.net, but this is not what I'm looking for.可以在 inte.net 上找到其他几个针对exe 图标的解决方案,但这不是我要找的。

The standard procedure to embed resources into an executable image on Windows is to author a resource file (.rc), have the resource compiler translate that into its binary representation, and pass that to the linker.将资源嵌入到 Windows 上的可执行映像的标准过程是创建一个资源文件(.rc),让资源编译器将其转换为二进制表示形式,并将其传递给 linker。

Since it's somewhat tedious to interact with the linker from Cargo, it's a fair bit easier to use an existing crate to deal with this.由于与 Cargo 的 linker 交互有点乏味,因此使用现有的箱子来处理这个问题要容易一些。 As you've discovered, there's winres (which appears to be a bit outdated), so I'll be using embed-resource here.正如您所发现的,有winres (似乎有点过时),所以我将在这里使用embed-resource

If you want to play along, start by creating a new binary crate如果你想一起玩,首先创建一个新的二进制包

cargo new --bin embed_icon

Next, copy an icon of your choosing into the crate's root directory (I'm using "rust_lang_logo.ico" downloaded from here ) and create the resource script (embed_icon.rc) in the same location:接下来,将您选择的图标复制到 crate 的根目录中(我使用的是从此处下载的“rust_lang_logo.ico”)并在同一位置创建资源脚本 (embed_icon.rc):

1 ICON "rust_lang_logo.ico"

All this does is tell the resource compiler that it should look for an icon called "rust_lang_logo.ico" , and assign it an ID of 1 when producing its binary output.所有这一切都是告诉资源编译器它应该寻找一个名为"rust_lang_logo.ico"的图标,并在生成其二进制文件 output 时为其分配一个 ID 1。

Of course we need a Cargo.toml as well:当然我们还需要一个 Cargo.toml:

[package]
name = "embed_icon"
version = "0.0.0"
edition = "2021"

[dependencies.windows]
version = "0.43.0"
features = [
    "Win32_Foundation",
    "Win32_UI_WindowsAndMessaging",
    "Win32_System_LibraryLoader",
]

[build-dependencies]
embed-resource = "1.8"

This is declaring the required windows features we'll be using, and imports embed-resource as a build dependency.这声明了我们将使用的所需windows功能,并将嵌入资源导入为构建依赖项。 What's left is src/main.rs剩下的就是 src/main.rs

use windows::{
    core::{Result, PCWSTR},
    Win32::{
        System::LibraryLoader::GetModuleHandleW,
        UI::WindowsAndMessaging::{LoadImageW, IMAGE_ICON, LR_DEFAULTSIZE},
    },
};

fn main() -> Result<()> {
    let _icon = unsafe {
        LoadImageW(
            GetModuleHandleW(None)?,
            PCWSTR(1 as _), // Value must match the `nameID` in the .rc script
            IMAGE_ICON,
            0,
            0,
            LR_DEFAULTSIZE,
        )
    }?;

    Ok(())
}

This doesn't do much, other than trying to load the icon we just embedded into the binary.除了尝试加载我们刚刚嵌入到二进制文件中的图标外,这没什么用。 A cargo run later, and we have... cargo run ,我们有...

Error: Error { code: HRESULT(0x80070714), message: "The specified image file did not contain a resource section."错误:错误 { 代码:HRESULT(0x80070714),消息:“指定的图像文件不包含资源部分。” } }

and a binary that looks like this in File Explorer:和一个在文件资源管理器中看起来像这样的二进制文件:

没有嵌入资源的截图

The final step is to actually run embed-resource and have the icon linked into the executable image.最后一步是实际运行 embed-resource 并将图标链接到可执行映像中。 A build script is required to do this.需要构建脚本来执行此操作。 To add one create a file called "build.rs" in the crate's root directory with the following contents:要添加一个,请在 crate 的根目录中创建一个名为“build.rs”的文件,其中包含以下内容:

fn main() {
    embed_resource::compile("embed_icon.rc");
}

This neatly brings it all together.这巧妙地将它们结合在一起。 Running the executable is now successful, and the binary has a nice icon associated with it when displayed in File Explorer:运行可执行文件现在成功了,二进制文件在文件资源管理器中显示时有一个与之关联的漂亮图标:

带有嵌入资源的屏幕截图


Note: The solution above uses the windows crate, which is designed for convenience and better safety.注意:上面的解决方案使用windows crate,这是为了方便和更好的安全性而设计的。 If you are using the winapi or windows-sys crate the core principles are still the same.如果您使用的是winapiwindows-sys crate,核心原则仍然相同。 The code in main.rs would have to be adjusted, though.不过,必须调整 main.rs 中的代码。

This text complements Answer 1 by IInspectable , adding information for the users of winapi .此文本补充了winapi的回答 1,为winapi的用户添加了信息。

Create the resource file <name>.rc and build.rs as described in Answer 1, and place these files along with the icon file <name>.ico in the project root directory.按照回答 1 中的说明创建资源文件<name>.rcbuild.rs ,并将这些文件与图标文件<name>.ico一起放在项目根目录中。 Add the build dependency to embed-resource in Cargo.toml .将构建依赖项添加到Cargo.toml中的embed-resource The application exe file built with these changes will embed the icon from the icon file.使用这些更改构建的应用程序exe文件将嵌入图标文件中的图标。

To change the class icon that is shown in the taskbar, set the hIcon member in WNDCLASSW as follows:要更改任务栏中显示的class 图标,请按如下方式设置WNDCLASSW中的hIcon成员:

hIcon = LoadImageW( 
    GetModuleHandleW(0 as LPCWSTR), // hInst; the .exe file
    MAKEINTRESOURCEW( 1 ),          // name; use the resource number in the .rc file
    IMAGE_ICON,                     // type
    0,                              // cx
    0,                              // cy
    0                               // fuLoad
    ) as HICON;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM