Closed MrTanoshii closed 1 year ago
Hi, I’m interested in having a go at this!
Hi, I’m interested in having a go at this!
Of course, you are very much welcome to do so! Thank you for the interest.
I had been looking into it lately too, I think winres
will not work out after all as Rusty AutoClicker uses rust-version = "1.63"
and there is a an open issue https://github.com/mxre/winres/issues/40 starting from 1.61
Another crate to look into is https://crates.io/crates/embed-resource (Windows).
I haven't found any potential solutions for Linux and macOS.
Edit: Linux executables do not have icons
@bryonye Are you still interested in this?
@MrTanoshii Hey how's it going? I just looked up your profile and found this, Would you like me to submit a PR for this? If you haven't made the changes already that is.
Regarding macOS, I think I've read before on how to add an Icon to a macOS binary (will have to look that up again) but I don't really have an access to a macOS, so can't really test it out.
Yes, if you're up for it please do submit a PR. It will be very welcome!
I'm assigning this to you
Alright, I'll submit it tomorrow. It's late here rn.
Hey @MrTanoshii, I wanted to confirm something; Why do we've a lib crate in this package? Am I missing something? Because to me It makes not much sense to have a lib crate here.
The reason I'm mentioning this is because unfortunately winres
doesn't work with packages having both a lib crate and bin crate, as mentioned here https://github.com/mxre/winres/issues/32#issuecomment-933035162 (seems to be due to cargo limitations).
Some possible solutions:
lib.rs
and then use winres
. (very straightforward to do, overkill?)And coming to Icons, should I just use the existing icon-64x64.ico
or rather make a new .ico
of higher res (like 256x256 or 512x512)?
https://github.com/MrTanoshii/rusty-autoclicker/pull/25#discussion_r1015284486
Let's go with 256x256
Hey, just to let you know that I successfully forced resources to be added to the binary crate in a project with both library and binary crate. You can check my workaround there: https://github.com/mxre/winres/issues/32#issuecomment-1304805763.
I'm not involved in this project, but removing lib.rs
is slight overkill imho. Even in the official documentation authors of Rust suggest dividing application into binary and library crates.
I'm not involved in this project, but removing
lib.rs
is slight overkill imho. Even in the official documentation authors of Rust suggest dividing application into binary and library crates.
Hi @ITachiLab, It's not that I wanted to remove lib.rs
just for the sake of making winres
work, but also that I didn't get the point of having a lib.rs
here. We can just have functionalities divided into modules, can't we? What's wrong with that approach?
Also, it just feels weird to me to have app.rs
as part of a lib crate.
Actually I'm new to rust, so it'd be great if you could let me know your thoughts on this. I guess this is probably already have been discussed in some later chapter of the Rust book, which I'm currently going through.
Thanks for mentioning the workaround btw, I'll look into it.
Oh @ITachiLab actually, I remember trying that workaround before I removed the lib crate. It didn't workout on my end unfortunately; using Rust 1.64 and this winres fork.
I guess I'll try it again, just to be sure.
Edit: tried again, didn't work (doesn't work with cdylib
either, just gives different error)
Compiling rusty-autoclicker v2.1.0 (/home/nozwock/hub/dev/forks/rusty-autoclicker)
error: the linking modifiers `+bundle` and `+whole-archive` are not compatible with each other when generating rlibs
error: could not compile `rusty-autoclicker` due to previous error
Here's the build.rs
use std::io;
use winres::WindowsResource;
fn main() -> io::Result<()> {
if std::env::var("CARGO_CFG_TARGET_FAMILY").unwrap() == "windows" {
let mut res = WindowsResource::new();
let env = std::env::var("CARGO_CFG_TARGET_ENV").unwrap();
match env.as_str() {
"gnu" => {
res.set_ar_path("x86_64-w64-mingw32-ar")
.set_windres_path("x86_64-w64-mingw32-windres");
}
"msvc" => {}
_ => panic!("unsupported target-env: {}", env),
};
res.set_icon("assets/icon-256.ico");
res.compile()?;
println!("cargo:rustc-link-arg-bins=resource.lib");
}
Ok(())
}
Hmm, I was using x86_64-pc-windows-gnu
target, maybe this would work fine with x86_64-pc-windows-msvc
but I don't have a windows machine right now to test it.
Hmm, I was using
x86_64-pc-windows-gnu
target, maybe this would work fine withx86_64-pc-windows-msvc
but I don't have a windows machine right now to test it.
From what I can see in winres
source code here: https://github.com/mxre/winres/blob/master/lib.rs#L597, when using GNU tools for compiling, winres
creates libresource.a
instead of resource.lib
, which is understandable because .lib
format is used only by Windows.
With that being said, you can try using one of these for the GNU tools:
println!("cargo:rustc-link-arg-bins=libresource.a");
println!("cargo:rustc-link-arg-bins=-lresource");
Actually I'm new to rust
No worries, me too 😅.
winres
createslibresource.a
instead ofresource.lib
, which is understandable because.lib
format is used only by Windows.
Didn't thought of that.
println!("cargo:rustc-link-arg-bins=-lresource");
This works (with original winres
& Rust 1.65), https://github.com/mxre/winres/issues/40#issuecomment-1276785306. Don't know what the windows equivalent for this would be, since I don't know anything about linking flags.
#[cfg(unix)]
{
println!("cargo:rustc-link-arg=-Wl,--whole-archive");
println!("cargo:rustc-link-arg=-Wl,-Bstatic");
println!("cargo:rustc-link-arg-bins=-lresource");
println!("cargo:rustc-link-arg=-Wl,--no-whole-archive");
}
For Windows and MSVC, having just println!("cargo:rustc-link-arg-bins=resource.lib");
is enough. I'm currently developing a Windows application under Windows and already had gone through the same problem of including resources. https://github.com/ITachiLab/rectangular/blob/master/build.rs#L8
Apparently, MSVC expects resources to appear in linker command line and knows how to deal with them without additional flags.
For Windows and MSVC, having just
println!("cargo:rustc-link-arg-bins=resource.lib");
is enough. I'm currently developing a Windows application under Windows and already had gone through the same problem of including resources. https://github.com/ITachiLab/rectangular/blob/master/build.rs#L8
I see, got it, Thanks!
btw @ITachiLab, I've looked up your project rectangular and it looks like it's completely related to the windows platform. Sorry for being a bother and this is getting off topic but I was hoping maybe you could help me with this Rust windows related issue (https://github.com/nozwock/ventoy-toybox/issues/1) of mine.
TL;DR I need to launch an .exe
as a subprocess with elevated privileges (using UAC prompt) & process pwd set to parent directory of the .exe
.
While searching I came across this post "how do I trigger a new process to be run as admin?".
The API version being used there seems to be quite old 0.10
and also is unsafe
necessary for this case? Any advice on how to approach this?
No worries, we both learn by solving issues ^^.
The API version being used there seems to be quite old 0.10
It's waaaay outdated. The latest version of windows crate is 0.43.0
, this is the version I'm using for Rectangular.
So, in order to run an executable as an administrator, you indeed must use ShellExecuteA
function from Windows API. When working with Rust and Windows API, always browse two things simultaneously, the first and foremost is a documentation for Windows API itself, in our case this will be a page dedicated to ShellExecuteA
: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/nf-shellapi-shellexecutea; the second is a documentation for the analogous function in windows
crate: https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/UI/Shell/fn.ShellExecuteA.html. The crate for Windows stuff is developed by guys from Microsoft, so it's always in sync with Windows API. The crate documentation is rather laconic, but the most important thing is the search bar which you will be using for determining in which module the function lives, and information what features you must include to use the function.
Putting this all together, Cargo.toml
:
[dependencies.windows]
version = "0.43.0"
features = [
"Win32_UI_Shell",
"Win32_Foundation",
"Win32_UI_WindowsAndMessaging"
]
main.rs
:
use windows::{
s, Win32::UI::Shell::ShellExecuteA, Win32::Foundation::GetLastError,
Win32::UI::WindowsAndMessaging::SW_NORMAL
};
fn main() {
unsafe {
let result = ShellExecuteA(
None,
s!("runas"),
s!("C:\\Users\\Itachi\\AppData\\Local\\Programs\\Python\\Python38-32\\python.exe"),
s!("-c \"import os; os.mkdir('test');\""),
s!("C:\\Users\\Administrator"),
SW_NORMAL
);
println!("{}", GetLastError().0);
}
assert!(result.0 > 32);
}
What I'm doing here is running Python as an Administrator, setting C:\Users\Administrator
as the working directory, and I'm passing a simple script to the interpreter; the script simply creates a 'test' directory within the working directory. I ran this program successfully, UAC asked for permissions and the directory has been created in Administrator's home directory, which means it truly has started with correct permissions.
@ITachiLab Thanks a lot! I got it to work through your examples.
Problem
The app icon is currently implemented in the app itself but not on the binary file.
Potential Solutions