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

问题描述 投票:0回答:2

在 Rust 桌面应用程序中,始终使用某些版本的窗口

struct
,例如
WNDCLASSW
。当定义
WNDCLASSW
时,可以通过struct成员
hIcon
添加
类图标
。下面的代码摘录演示了如何包含存储在文件中的图标
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();
...

图标文件在程序执行期间加载,并且必须与

exe
文件存储在同一文件夹中。如果图标文件丢失,
LoadImageW()
返回一个
NULL
句柄。将
hIcon
设置为
NULL
是有效的,并且会导致使用标准系统图标。

虽然此方法生成所需的图标,但图标文件在执行期间加载,并且必须与

exe
文件一起传递。这不是一个可接受的解决方案;图标应链接到
exe
文件并在其中传递。

如何将图标链接到 Rust Windows 应用程序并在那里使用它?

我知道这个解决方案,但它在编译过程中会生成数千行错误和警告,并且必须被视为过时的。 此解决方案有效,但它仅添加了Windows File Explorer中显示的

exe图标
,而类图标(在任务栏中)未更改。可以在互联网上找到针对exe图标的其他几个解决方案,但这不是我要寻找的。

windows winapi rust rust-cargo
2个回答
4
投票

在 Windows 上将资源嵌入到可执行映像中的标准过程是编写一个 资源文件 (.rc),让 资源编译器 将其转换为二进制表示形式,并将其传递给链接器。

由于与 Cargo 的链接器交互有点乏味,因此使用现有的 crate 来处理这个问题要容易一些。正如您所发现的,有 winres (这似乎有点过时),所以我将在这里使用 embed-resource

如果你想一起玩,请从创建一个新的二进制板条箱开始

cargo new --bin embed_icon

接下来,将您选择的图标复制到板条箱的根目录中(我使用从此处下载的“rust_lang_logo.ico”)并在同一位置创建资源脚本(embed_icon.rc):

1 ICON "rust_lang_logo.ico"

这一切都是告诉资源编译器它应该寻找一个名为

"rust_lang_logo.ico"
的图标,并在生成二进制输出时为其分配 ID 1。

当然我们还需要一个 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"

这声明了我们将使用的所需

windows
功能,并将嵌入资源作为 build 依赖项导入。剩下的是 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(())
}

除了尝试加载我们刚刚嵌入到二进制文件中的图标之外,这并没有做太多事情。

cargo run
之后,我们...

错误:错误{代码:HRESULT(0x80070714),消息:“指定的图像文件不包含资源部分。” }

以及文件资源管理器中如下所示的二进制文件:

最后一步是实际运行 embed-resource 并将图标链接到可执行映像中。需要一个构建脚本来执行此操作。要添加一个,请在板条箱的根目录中创建一个名为“build.rs”的文件,其中包含以下内容:

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

这将所有内容巧妙地结合在一起。现在运行可执行文件已成功,并且当在文件资源管理器中显示时,二进制文件有一个与之关联的漂亮图标:


注意:上述解决方案使用windows板条箱,其设计是为了方便和更好的安全性。如果您使用 winapiwindows-sys 箱,核心原理仍然是相同的。不过,main.rs 中的代码必须进行调整。


2
投票

本文补充了 IInspectable 的答案 1,为

winapi
的用户添加了信息。

按照答案1所述创建资源文件

<name>.rc
build.rs
,并将这些文件与图标文件
<name>.ico
一起放置在项目根目录中。将构建依赖项添加到
embed-resource
中的
Cargo.toml
。使用这些更改构建的应用程序
exe
文件将嵌入图标文件中的图标。

要更改任务栏中显示的 class icon,请在

hIcon
中设置
WNDCLASSW
成员,如下所示:

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;
© www.soinside.com 2019 - 2024. All rights reserved.