In a Rust desktop application, some version of the window struct is always used, e.g., WNDCLASSW. When WNDCLASSW is defined, a class icon may be added through the struct member hIcon. The code excerpt below demonstrates how to include the icon stored in the file 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. If the icon file is missing, LoadImageW() returns a NULL handle. Setting hIcon to NULL is valid and causes the use of a standard system icon.
While this approach produces the required icon, the icon file is loaded during execution and must be delivered along with the exe file. This isn't an acceptable solution; the icon should be linked to the exe file and delivered within it.
How do I link an icon to a Rust Windows application and use it there?
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. Several other solutions for the exe icon may be found on the internet, but this is not what I'm looking for.
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.
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. As you've discovered, there's winres (which appears to be a bit outdated), so I'll be using embed-resource here.
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:
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.
Of course we need a Cargo.toml as well:
[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. What's left is 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...
Error: Error { code: HRESULT(0x80070714), message: "The specified image file did not contain a resource section." }
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. 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:
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. If you are using the winapi or windows-sys crate the core principles are still the same. The code in main.rs would have to be adjusted, though.
This text complements Answer 1 by IInspectable, adding information for the users of 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. Add the build dependency to embed-resource in Cargo.toml. The application exe file built with these changes will embed the icon from the icon file.
To change the class icon that is shown in the taskbar, set the hIcon member in WNDCLASSW as follows:
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;
Related
I'm using Visual Studio Community 2019 Version 16.10.4. I've created a Win32 C++ app that displays a bmp picture. I've been trying to embed the picture into the complied .exe but not having success. My goal is to create the .exe without the need for any additional files or folders.
I did the following:
Right click on the App name in the Solution Explorer
Select Add from the dropdown and New Item from the popup box
Select Resource in the left column and Resource File(.rc) in the right column, then the Add button at the bottom
A pop up occurred: warning RC4005: '_APS_NEXT_RESOURCE_VALUE':redefinition, select OK
In the Resource View, right click on Resource.rc and select Add Resource… at the bottom
Select Bitmap and select New
There were a few more steps, but I lost track after trying to resolve the issue.
In the Solution Explorer under Resource Files I have Resource.rc and art1.bmp. For art1.bmp I have it so it is not excluded from the build. A resource.h file was also created.
resource.h has the following:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by Resource.rc
//
#define IDB_BITMAP1 103
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 104
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
In the .rc file, it has the following (and more)
IDB_BITMAP1 BITMAP "art1.bmp"
I have art1.bmp in the project root, Release folder and Debug folder.
After I compile the code into Release x86, the picture shows up without any issues (same in Debug) when I run the .exe. If I delete the picture from the Release folder, the picture no longer shows up.
Any suggestions on what step or steps I missed or what I need to change so the picture will be part of the .exe and not a seperate file?
A big thank you to Igor, With his help, this issue is finally resolved.
My original code (which was not posted at the time) was:
bitmap = (HBITMAP)LoadImageA(NULL, "art1.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
Igor correctly asked "do you use IDB_BITMAP1 anywhere in your code?" which it was not.
I manage to get farther, but it still failed:
bitmap = (HBITMAP)LoadImageA(NULL, MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
I had tried replacing LR_LOADFROMFILE with NULL without success. The final key Igor provided was GetModuleHandle(nullptr) and NULL for UINT.
bitmap = (HBITMAP)LoadImageA(GetModuleHandleA(nullptr), MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, NULL);
I met this problem. I have a simple Win32 program which is like the boilerplate which I can get from selecting a "Win32 project" under Visual Studio 2010's "Template --> Visual C++".
I found all other Windows based program like Adobe Reader, Windows Explorer having the feature which is: you enlarging the main window to a new size and then select "Close" or "Exit" from File menu or system menu to close it, then you launch the program again, the main window would be of the size that you adjusted to last time. However that program I got from Visual Studio as the bootstrap does not have such feature.
I researched more on it but cannot find which setting in either WndClass or CreateWindow that I can tweak to make that happen. Does anyone know it, thank you for your help in advance.
The simplest way to do this is with the GetWindowPlacement() and SetWindowPlacement() functions. These manage the window size and state (minimized/maximized/restored) for you.
Call GetWindowPlacement() when you want to record your window's current state:
WINDOWPLACEMENT wp = {0};
wp.length = sizeof(wp);
if (GetWindowPlacement(hWnd, &wp))
{
// save wp values somewhere...
}
You can then save the values of the WINDOWPLACEMENT structure somewhere in your program's configuration files - either in the registry or on disk.
To restore your window's information, load the saved values into the WINDOWPLACEMENT structure, then call the SetWindowPlacement() function:
if (values were previously saved)
{
WINDOWPLACEMENT wp = {0};
wp.length = sizeof(wp);
// load wp values from somewhere...
SetWindowPlacement(hWnd, &wp);
}
You will need to save the position (X, Y) and size (Height, Width) of the window yourself, and set those values when the program starts up again.
Depending on the nature of the program, you might set this in a configuration file, a registry key, or a database (among other options).
I have been searching for clues on this issue for some time now, with no results. So, here goes...
I have an application that I want to have a simple button to open a file dialog window. There are other buttons on the main window that will read or create/write the file (after doing the appropriate checks for the function selected). I used to use the QFileDialog::getSaveFileName() function without issues, but with Windows 7, this fails if the file exists AND is read-only. I switched to the getOpenFileName() to get around this issue, but now the file dialog fails if the user tries to select a non-existent file (irrelevant on a save operation).
Is there a way to add a "Create New File" icon to the file dialog, or add it to the right-click menu within the file dialog window? I would really hate to have to rewrite the app just because of (yet another) Windows behavior change.
QFileDialog::getOpenFileName() should only be used for opening existing files. If you type in a name of a file that doesn't exist and the system complains, this is proper behaviour. It's correctly telling you that you can't open a file that doesn't exist.
If you want to write to an existing file or create a new file, you should be using QFileDialog::getSaveFileName()
If you're trying to write to an existing file that is marked as Read-Only in the operating system and you get an error saying that the file is Read-Only, then the error is correct. You should not be allowed to write to a read-only file, that's just what Read-Only means.
From what you've explained, there are no errors here. Everything's happening as it should be. If you're trying to force the system to do something different, don't. You should rather try and think of doing things a different way.
Ok, since this was never really answered here, and I have since figured out a solution, I thought I would update this with the code snippet I am using.
void MainWindow::on_tbBrowse_clicked()
{
// Use the location of already entered file
QString fileLocation = leFile->text();
QFileInfo fileinfo(fileLocation);
// See if there is a user-defined file extension.
QString fileType = qgetenv("DiskImagerFiles");
if (fileType.length() && !fileType.endsWith(";;"))
{
fileType.append(";;");
}
fileType.append(tr("Disk Images (*.img *.IMG);;*.*"));
// create a generic FileDialog
QFileDialog dialog(this, tr("Select a disk image"));
dialog.setNameFilter(fileType);
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setViewMode(QFileDialog::Detail);
dialog.setConfirmOverwrite(false);
if (fileinfo.exists())
{
dialog.selectFile(fileLocation);
}
else
{
dialog.setDirectory(myHomeDir);
}
if (dialog.exec())
{
// selectedFiles returns a QStringList - we just want 1 filename,
// so use the zero'th element from that list as the filename
fileLocation = (dialog.selectedFiles())[0];
if (!fileLocation.isNull())
{
leFile->setText(fileLocation);
QFileInfo newFileInfo(fileLocation);
myHomeDir = newFileInfo.absolutePath();
}
setReadWriteButtonState();
updateHashControls();
}
}
setReadWriteButtonState() will enable the buttons according to the file state:
if file is read-only, only Read button is enabled
if file doesn't exist, only Write button is enabled
The entire code is available for others to review at https://sourceforge.net/projects/win32diskimager/. I hope this helps the next person that is looking for a solution to this. Just please include attribution if you use our code.
I'm writing a Windows application in Visual C++ 2008 and I want to embed the calculator (calc.exe) that comes with Windows in it. Does anyone know if that is possible, and if it is, can you give me hints on how I could achieve that?
Yes, it's possible to embed the calc in your own application but it will still run in it's own process space. There may also be some restrictions imposed by UAC but that will depend on how calc is launched. All you need to do is change the parent of the main calc window and change it's style to WS_CHILD.
void EmbedCalc(HWND hWnd)
{
HWND calcHwnd = FindWindow(L"CalcFrame", NULL);
if(calcHwnd != NULL)
{
// Change the parent so the calc window belongs to our apps main window
SetParent(calcHwnd, hWnd);
// Update the style so the calc window is embedded in our main window
SetWindowLong(calcHwnd, GWL_STYLE, GetWindowLong(calcHwnd, GWL_STYLE) | WS_CHILD);
// We need to update the position as well since changing the parent does not
// adjust it automatically.
SetWindowPos(calcHwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}
}
Microsoft has various technologies to support embedding, most famously OLE which is a COM-based technology. This is, for example, how you can embed an Excel spreadsheet in your application. However, I'm fairly certain that calc does not implement any of the required interfaces for that to happen.
So that only leaves you with hacky solutions, like trying to launch it yourself and play games with the window hierarchy, or try to present it to users and then copy the results out through the clipboard, etc. This is all technically possible, but not a good idea. In fact, it's probably more difficult than just writing your own calculator app... depending on what you want to to enable users to do. If you explain why you want to do this someone may have some better solutions to propose.
I have a DLL file that is running under a parent piece of software. The parent software uses MFC and displays a main GUI window. What I want to do is create my own little modeless dialog box to pop up on the screen and sit right next to the main window. To do this I think I need the HWND of the parent's main window so I can find it's RECTangle and then move my DLL's window to where I want it.
If at all possible, I'd like not to change the source of the parent. How could I do this?
A second, possibly related, question is whether I should use MFC, or plain windows API, to create my DLL's dialog box. I want the method that will be easier to get it going, and I have read on MSDN that I may need the parent MFC application to send me messages for my MFC to work, which I'd rather not do.
I don't know if you can create an MFC dll that can find MFC objects created in another module. I'm pretty sure that MFC can be used to create dialogs from DLLs, regardless of whether or not MFC is used in the application.
That said, if you are not already an MFC expert, and have no real wish to become one, creating a dialog box from a dll is quite easy. MFC's CDialog is really a thin wrapper over CreateDialog so you don't gain much.
I can think of at least two approaches to find the application window if the app/dll interface doesn't hand it over:
Use FindWindow to locate the window based on its title, or classname. As frameworks like MFC sometimes generate window classnames dynamically this might be problematic.
Use EnumWindows to enumerate all top level windows. GetWindowThreadProcessId can test if it belongs to the current process.
Call GetGUIThreadInfo on the main thread. This gets you a bunch of HWNDs. Pick any valid one (not all values may be filled) and find its top level ancestor with GetAncestor(GA_ROOT). Unlike EnumWindows, this doesn't require enumeration, and unlike FindWindow this doesn't require specialized knowledge
For obtaining the PID of the parent — not the HWND — look at this that I found on http://www.codeexperts.com/showthread.php?1380-get-parent-process-id-from-child-process-id&p=2845&viewfull=1#post2845
DWORD GetParentProcessID(DWORD dwProcessID)
{
DWORD dwParentProcessID = -1 ;
HANDLE hProcessSnapshot ;
PROCESSENTRY32 processEntry32 ;
hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) ;
if(hProcessSnapshot != INVALID_HANDLE_VALUE)
{
processEntry32.dwSize = sizeof(PROCESSENTRY32) ;
if(Process32First(hProcessSnapshot, &processEntry32))
{
do
{
if (dwProcessID == processEntry32.th32ProcessID)
{
dwParentProcessID = processEntry32.th32ParentProcessID ;
break ;
}
}
while(Process32Next(hProcessSnapshot, &processEntry32)) ;
CloseHandle(hProcessSnapshot) ;
}
}
return dwParentProcessID ;
}