I am trying to make application state working in Rust.
I got the Rust application state part from windows-rs sample line 357 and modified it a little to work with my code. The only problem is, it did not work as expected...
As you can see from the comments in the code below, my problem is that after using the mentioned two functions my pointer (state) loses its first two digits.
Every time I run this, the first two digits are always missing.
How can I solve this?
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
unsafe {
match message as u32 {
WM_NCCREATE => {
// ORIGINAL address given to CreateWindowExW: 0xb678ddf730
let cs = lparam as *const CREATESTRUCTW;
let state = (*cs).lpCreateParams as *mut State;
println!("{:?}", state); // OUTPUT: 0xb678ddf730 (correct)
// After using the two functions:
SetWindowLongW(window, GWLP_USERDATA, state as i32);
let state = GetWindowLongW(window, GWLP_USERDATA) as *mut State;
println!("{:?}", state); // OUTPUT: 0x78ddf730 (missing first 2 digits "b6")
1
}
...
Full code:
#![allow(dead_code)]
//#![allow(unused_)]
#![allow(unused_variables)]
use windows_sys::{
Win32::Foundation::*,
Win32::Graphics::Gdi::ValidateRect,
Win32::System::LibraryLoader::GetModuleHandleW,
Win32::UI::WindowsAndMessaging::*,
};
fn u16_str(string: &str) -> Vec<u16> {
let mut result: Vec<u16> = string.encode_utf16().collect();
result.push(0);
result
}
#[derive(Debug)]
struct State {
name: String,
}
fn main() {
unsafe {
let instance = GetModuleHandleW(std::ptr::null());
let wc = WNDCLASSEXW {
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
hCursor: LoadCursorW(0, IDC_ARROW),
hInstance: instance,
lpszClassName: u16_str("asd").as_ptr(),
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some(wndproc),
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: 0,
hbrBackground: 0,
lpszMenuName: std::ptr::null(),
hIconSm: 0,
};
RegisterClassExW(&wc);
let mut state = State {
name: String::from("name"),
};
println!("{:?}", &mut state as *mut _);
CreateWindowExW(
0,
u16_str("asd").as_ptr(),
u16_str("Ablak 1 áéőüűö").as_ptr(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
0, 0, instance, &mut state as *mut _ as _,
);
let mut message = std::mem::zeroed();
while GetMessageW(&mut message, 0, 0, 0) != 0 {
DispatchMessageW(&message);
}
}
}
extern "system" fn wndproc(window: HWND, message: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
unsafe {
match message as u32 {
WM_NCCREATE => {
let cs = lparam as *const CREATESTRUCTW;
let state = (*cs).lpCreateParams as *mut State;
println!("{:?}", state);
SetWindowLongW(window, GWLP_USERDATA, state as i32);
let state = GetWindowLongW(window, GWLP_USERDATA) as *mut State;
println!("{:?}", state);
1
}
WM_PAINT => {
//let state = GetWindowLongPtrW(window, GWLP_USERDATA) as *mut State;
//println!("{:?}", state);
//println!("{}", GetLastError());
println!("paint");
ValidateRect(window, std::ptr::null());
0
}
WM_DESTROY => {
println!("WM_DESTROY");
PostQuitMessage(0);
0
}
_ => DefWindowProcW(window, message, wparam, lparam),
}
}
}
Cargo.toml:
...
[dependencies.windows-sys]
version = "0.36.1"
features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",
"Win32_Graphics_Gdi",
"Win32_Graphics_Direct2D",
"Win32_UI_WindowsAndMessaging",
]
The GetWindowLongW and SetWindowLongW API calls can only store 32-bit values. 32 bits is the size of a pointer for 32-bit architectures. Since you are targeting a 64-bit architecture, you'll have to follow the advice from the documentation:
If you are retrieving a pointer or a handle, this function has been superseded by the GetWindowLongPtrW function. (Pointers and handles are 32 bits on 32-bit Windows and 64 bits on 64-bit Windows.) To write code that is compatible with both 32-bit and 64-bit versions of Windows, use GetWindowLongPtrW.
and
This function has been superseded by the SetWindowLongPtr function. To write code that is compatible with both 32-bit and 64-bit versions of Windows, use the SetWindowLongPtr function.
You'll have to replace
SetWindowLongW(window, GWLP_USERDATA, state as i32);
// ^^^^^^ truncates pointer to 32 bits
with
SetWindowLongPtrW(window, GWLP_USERDATA, state as isize);
The GetWindowLongW calls merely need to be replaced with GetWindowLongPtrW calls. Other changes aren't required here.
Note that while the -Ptr variants are immediately available through the windows and windows-sys crates when targeting a 64-bit architecture, they are missing for 32-bit targets (see this GitHub issue). You can provide your own implementation, e.g.
#[allow(non_snake_case)]
#[cfg(target_pointer_width = "32")]
unsafe fn SetWindowLongPtrW(window: HWND, index: WINDOW_LONG_PTR_INDEX, value: isize) -> isize {
SetWindowLongW(window, index, value as _) as _
}
#[allow(non_snake_case)]
#[cfg(target_pointer_width = "32")]
unsafe fn GetWindowLongPtrW(window: HWND, index: WINDOW_LONG_PTR_INDEX) -> isize {
GetWindowLongA(window, index) as _
}
With that in place you can simply call GetWindowLongPtrW/SetWindowLongPtrW irrespective of target architecture, same as when writing C/C++ code using the Windows SDK headers.
So I was trying to make a win32 overlay with rust, but after some time I realized that due to poor way I organized my code some things were too difficult to add - and I'm trying to rewrite it in a more fitting manner. My particular problem however is that now in rewritten state (using a mem dc for double buffering now), I cant get my bitmap to appear on the screen.
I cant seem to pinpoint the problem as GetLastError returns 0.
My current script for window is below:
use crate::static_helpers::win32_string;
use winapi::um::winuser::{WM_PAINT, WM_TIMER, WNDCLASSW, DefWindowProcW, ShowWindow, SW_NORMAL, WS_POPUP, WS_EX_TRANSPARENT, WS_EX_LAYERED, WS_EX_TOPMOST, WS_EX_TOOLWINDOW, WS_VISIBLE, CreateWindowExW, RegisterClassW, CS_HREDRAW, CS_OWNDC, CS_VREDRAW, SetLayeredWindowAttributes, SW_SHOWMAXIMIZED, LWA_COLORKEY, ReleaseDC, InvalidateRect, EndPaint, PAINTSTRUCT, BeginPaint, GetClientRect, MSG, GetMessageW, SetTimer, USER_TIMER_MINIMUM, TIMERPROC, GetDC, UpdateWindow};
use winapi::shared::windef::{HWND__, HDC__, HWND, RECT, HDC, HBITMAP, HGDIOBJ};
use std::time::SystemTime;
use winapi::shared::minwindef::{UINT, WPARAM, LPARAM, LRESULT, HINSTANCE};
use std::ptr::null_mut;
use winapi::um::wingdi::{CreateSolidBrush, RGB, DeleteDC, SelectObject, BitBlt, GetStockObject, DC_PEN, SRCCOPY, CreateCompatibleDC, BITMAP, CreateBitmap, CreateCompatibleBitmap, ExtFloodFill};
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::errhandlingapi::GetLastError;
pub struct Overlay {
window_class: WNDCLASSW,
// window class stored
window_handle: *mut HWND__,
// window handle stored
mem_dc: Option<*mut HDC__>,
// buffer dc
screen_dc: Option<*mut HDC__>,
// screen dc
bitmap_bg: HBITMAP,
}
impl Overlay {
// redirects event handling to default handler
pub unsafe extern "system" fn proc_msg(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
match msg {
_ => { return DefWindowProcW(hwnd, msg, wparam, lparam); }
}
}
// creates new instance
pub unsafe fn new(class_name: String, window_name: String) -> Self {
// gets module instance handle to register stuff
let hinstance = GetModuleHandleW(null_mut());
// makes window class
let wnd_class = WNDCLASSW {
style: CS_HREDRAW | CS_OWNDC | CS_VREDRAW,
lpfnWndProc: Some(Self::proc_msg),
hInstance: hinstance as HINSTANCE,
lpszClassName: win32_string(&class_name).as_ptr(),
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: null_mut(),
hCursor: null_mut(),
hbrBackground: CreateSolidBrush(RGB(0, 0, 0)), // brush so repaint works
lpszMenuName: null_mut(),
};
// registers class (returns atom but i don`t need it so far)
RegisterClassW(&wnd_class);
/*
creating window
transparent style for event pipethrough, toolwindow style hides from alt+tab and task mgr
popup and visible are to keep it fullscreen with no title bar
*/
let handle: *mut HWND__ = CreateWindowExW(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
win32_string(&class_name).as_ptr(),
win32_string(&window_name).as_ptr(),
WS_POPUP | WS_VISIBLE,
0,
0,
1919,
1079,
null_mut(),
null_mut(),
hinstance,
null_mut(),
) as *mut HWND__;
// getting window dc
let window_dc = GetDC(handle);
//make a buffer dc
let mem_dc = CreateCompatibleDC(window_dc);
//make bg bitmap and fill it to change size of dc
let bmp = CreateCompatibleBitmap(mem_dc, 1920, 1080);
SelectObject(mem_dc, bmp as HGDIOBJ);
ShowWindow(handle, SW_NORMAL);
return Self {
window_class: wnd_class,
window_handle: handle,
mem_dc: Some(mem_dc),
screen_dc: Some(window_dc),
bitmap_bg: bmp,
};
}
pub unsafe fn make_transparent(&mut self) {
ShowWindow(self.window_handle, SW_SHOWMAXIMIZED); // maximize
SetLayeredWindowAttributes(self.window_handle, RGB(0, 0, 0), 0, LWA_COLORKEY); // set transparent
}
pub fn die(&mut self) {
unsafe {
match self.mem_dc {
Some(mut hDC) => {
DeleteDC(hDC);
}
_ => {}
}
match self.screen_dc {
Some(mut hDC) => {
ReleaseDC(self.window_handle, hDC);
}
_ => {}
}
}
}
// takes anything which takes mutable dc, and handle - and returns bool
pub unsafe fn do_loop<T1: FnMut(*mut HWND, *mut HDC__) -> bool>(&mut self, mut cb: T1) {
let mut rc: RECT = RECT::default();
loop {
let mut msg: MSG = std::mem::uninitialized(); // later switch to zeroed
if GetMessageW(&mut msg as *mut MSG, self.window_handle, 0, 0).is_positive() {
//prep dc canvas
ExtFloodFill(self.mem_dc.unwrap(), 1920, 1080, RGB(0, 0, 0), FLOODFILLBORDER);
// passing window handle and buffer dc
if cb(&mut self.window_handle, self.mem_dc.unwrap()) {
// if returned true - change current screen to buffer dc
BitBlt(self.screen_dc.unwrap(), 0, 0, 1920, 1080, self.mem_dc.unwrap(), 0, 0, SRCCOPY);
};
} else {
break;
}
}
}
}
and I attempt to call it in my main.rs like follows:
mod overlay;
mod static_helpers;
mod event_poller;
use static_helpers::TestCat;
use overlay::Overlay;
use winapi::um::libloaderapi::GetModuleHandleW;
use std::ptr::null_mut;
use winapi::shared::windef::HDC;
use winapi::um::wingdi::{BitBlt, SRCCOPY};
fn main() {
let REDRAW: String = String::from("REDRAW");
unsafe {
let mut ticker = event_poller::EventPoller::new();
ticker.add_event((40, "REDRAW".to_owned()));
let cat = TestCat::new("C:\\Users\\grass\\Desktop\\codes\\Rust\\catso_v2\\src\\cat2.bmp");
let mut win: Overlay = Overlay::new("hewwo".to_owned(), "UwU".to_owned());
win.make_transparent();
win.do_loop(|handle, hdc| -> bool {
match (&mut ticker).ask() {
None => {}
Some(events) => {
if events.contains(&REDRAW) {
BitBlt(hdc, 0, 0, 100, 100, cat.src, 0, 0, SRCCOPY);
return true;
};
}
}
false
});
win.die();
}
}
The timer struct (stored in ticker variable) is properly working, but for completeness sake here it is:
use std::time::SystemTime;
pub struct EventPoller {
events: Vec::<(u128, SystemTime, String)>,
}
impl EventPoller {
pub fn new() -> Self {
Self {
events: Vec::new(),
}
}
pub fn add_event(&mut self, event: (u128, String)) {
&self.events.push((event.0, SystemTime::now(), event.1));
}
pub fn ask(&mut self) -> Option<Vec<String>> {
let now = SystemTime::now();
let mut time_passed: u128;
let mut ret: Vec<String> = Vec::new();
for i in 0..(&self.events).len() as usize {
time_passed = now.duration_since((&self.events[i]).1).unwrap().as_millis();
if time_passed > (&self.events[i]).0 {
ret.push((&mut self.events[i]).2.clone());
(&mut self.events[i]).1 = now.clone();
}
}
if ret.len() > 0 {
Some(ret)
} else {
None
}
}
}
same goes for a simple TestCat struct which only serves for testing purposes (see below):
pub struct TestCat {
pub src: *mut HDC__
}
impl TestCat {
pub unsafe fn new(fname: &str) -> Self {
let im_handle = LoadImageW(null_mut(),
win32_string(fname).as_ptr(),
IMAGE_BITMAP,
LR_DEFAULTSIZE as i32,
LR_DEFAULTSIZE as i32,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
let hdc: HDC = CreateCompatibleDC(null_mut());
SelectObject(hdc,im_handle);
Self {src: hdc}
}
}
Few things I attempted to do so far are:
Add a bitmap to a mem_dc at the start to ensure it doesn't contain a 1x1 monochrome one.
call InvalidateRect beforehand with and without erase parameter.
capture WM_DRAW message and preform check for an available update then calling same BitBlt within it.
Below is my old code - which works so far, and serves as the source of confusion as I don't really understand what is that key difference:
transparent_window.rs:
#![windows_subsystem = "windows"]
use std::alloc::{alloc, Layout};
use std::collections::HashMap;
use std::ffi::{c_void, OsStr};
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::ptr::{null, null_mut};
use std::ptr;
use std::time::SystemTime;
use winapi::shared::minwindef::{HINSTANCE, LPARAM, LRESULT, UINT, WPARAM};
use winapi::shared::windef::{HDC, HDC__, HGDIOBJ, HWND, HWND__, POINT, RECT, SIZE};
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::wingdi::{AC_SRC_ALPHA, AC_SRC_OVER, BitBlt, BLENDFUNCTION, CreateCompatibleDC, CreatePen, CreateSolidBrush, DC_PEN, DeleteDC, DeleteObject, DEVMODEW, GetClipBox, GetStockObject, PS_SOLID, Rectangle, RGB, SelectObject, SRCCOPY};
use winapi::um::winuser::{BeginPaint, CDS_FULLSCREEN, ChangeDisplaySettingsW, CreateWindowExW, CS_HREDRAW, CS_OWNDC, CS_VREDRAW, CW_USEDEFAULT, DefWindowProcW, DispatchMessageW, EndPaint, GetClientRect, GetDC, GetMessageW, GetParent, GetWindow, GetWindowLongW, GetWindowRect, GW_HWNDNEXT, GWL_EXSTYLE, IMAGE_BITMAP, InvalidateRect, LoadImageW, LR_LOADFROMFILE, LWA_COLORKEY, MSG, PAINTSTRUCT, RDW_ERASE, RDW_INVALIDATE, RDW_UPDATENOW, RedrawWindow, RegisterClassW, ReleaseDC, ScreenToClient, SetLayeredWindowAttributes, SetWindowLongW, ShowWindow, SW_NORMAL, SW_SHOWMAXIMIZED, TranslateMessage, ULW_COLORKEY, UpdateLayeredWindow, WM_ERASEBKGND, WM_PAINT, WM_TIMER, WNDCLASSW, WS_EX_APPWINDOW, WS_EX_CLIENTEDGE, WS_EX_DLGMODALFRAME, WS_EX_LAYERED, WS_EX_STATICEDGE, WS_EX_TOOLWINDOW, WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_POPUPWINDOW, WS_SYSMENU, WS_THICKFRAME, WS_VISIBLE};
use winapi::um::winuser::{WM_KEYDOWN, WM_KEYUP};
fn win32_string(value: &str) -> Vec<u16> {
OsStr::new(value).encode_wide().chain(once(0)).collect()
}
pub struct BasicWindow {
window_class: WNDCLASSW,
window_handle: *mut HWND__,
mem_DC: Option<*mut HDC__>,
screen_DC: Option<*mut HDC__>,
UpdateClock: SystemTime,
update_frequency: u128,
}
impl BasicWindow {
pub unsafe extern "system" fn proc_msg(hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
match msg {
_ => { return DefWindowProcW(hwnd, msg, wparam, lparam); }
}
}
pub fn new(class_name: String, window_name: String) -> Self {
unsafe {
let hinstance = GetModuleHandleW(null_mut());
let wnd_class = WNDCLASSW {
style: CS_HREDRAW | CS_OWNDC | CS_VREDRAW,
lpfnWndProc: Some(Self::proc_msg),
hInstance: hinstance as HINSTANCE,
lpszClassName: win32_string(&class_name).as_ptr(),
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: null_mut(),
hCursor: null_mut(),
hbrBackground: CreateSolidBrush(RGB(0, 0, 0)),
lpszMenuName: null_mut(),
};
let atom = RegisterClassW(&wnd_class);
let handle: *mut HWND__ = CreateWindowExW(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
win32_string(&class_name).as_ptr(),
win32_string(&window_name).as_ptr(),
WS_POPUP | WS_VISIBLE,
0,
0,
1919,
1079,
null_mut(),
null_mut(),
hinstance,
null_mut(),
) as *mut HWND__;
ShowWindow(handle, SW_NORMAL);
return Self {
window_class: wnd_class,
window_handle: handle,
mem_DC: None,
screen_DC: None,
UpdateClock: SystemTime::now(),
update_frequency: 40,
};
}
}
pub unsafe fn make_transparent(&mut self) {
let show_outcome = ShowWindow(self.window_handle, SW_SHOWMAXIMIZED);
let outcome = SetLayeredWindowAttributes(self.window_handle, RGB(0, 0, 0), 0, LWA_COLORKEY);
println!("Error: {:?}\nSetLayeredWindowAttributes outcome: {:?}\nShowWindow outcome: {:?}",
GetLastError(),
outcome,
show_outcome);
}
pub fn die(&mut self) {
unsafe {
match self.mem_DC {
Some(mut hDC) => {
DeleteDC(hDC);
}
_ => {}
}
match self.screen_DC {
Some(mut hDC) => {
ReleaseDC(self.window_handle, hDC);
}
_ => {}
}
}
}
pub unsafe fn do_loop<T1: FnMut() -> Option<(HDC, u32, u32, u32, u32, u32, u32)>>(&mut self,
mut tile_giver: T1) {
loop {
let mut msg: MSG = std::mem::uninitialized();
if GetMessageW(&mut msg as *mut MSG, self.window_handle, 0, 0).is_positive() {
match msg.message {
WM_PAINT => {
println!("WM_PAINT!!");
let mut ps = PAINTSTRUCT::default();
let dc = BeginPaint(self.window_handle, &mut ps);
let mut rc = RECT::default();
GetClientRect(self.window_handle, &mut rc);
let hdi_obj_original = SelectObject(ps.hdc, GetStockObject(DC_PEN as i32));
match (tile_giver()) {
(Some((hdc, x, y, w, h, x_pos, y_pos))) => {
println!("blt returned: {:?}", BitBlt(dc, x_pos as i32, y_pos as i32, w as i32, h as i32, hdc, x as i32, y as i32, SRCCOPY));
}
_ => {}
}
SelectObject(ps.hdc, hdi_obj_original);
EndPaint(self.window_handle, &ps);
InvalidateRect(self.window_handle,
&mut rc,
1);
}
_ => {}
}
} else {
break;
}
}
}
}
a closure I pass to its do_loop method:
|| -> Option<(HDC, u32, u32, u32, u32, u32, u32)> {
let x = sprite.give_tile();
sprite.skip();
x
}
where the dc passed is acquired as follows:
let im_handle = LoadImageW(null_mut(),
win32_string(fname).as_ptr(),
IMAGE_BITMAP,
LR_DEFAULTSIZE as i32,
LR_DEFAULTSIZE as i32,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
let hdc: HDC = CreateCompatibleDC(null_mut());
SelectObject(hdc,im_handle);
for the sake of providing a reproducible example - here is my cargo.toml file:
[dependencies]
winapi = "0.3.9"
[features]
default=["winapi/winuser","winapi/minwindef","winapi/windef",
"winapi/wingdi","winapi/libloaderapi","winapi/errhandlingapi","winapi/impl-default"]
I suspect that there is some function to trigger the actual visual update after preforming the bit blit like invalidate rect (I'm aware that InvalidateRect just causes WM_PAINT to be fired with or without erasing background, but basically I assume that there might be something I have to call to register the graphical changes outside of BeginPaint and EndPaint ). If anyone can help by either pointing the issue, or if you have will and time tell me what the bare minimum drawing procedure outside of WM_PAINT WindowProc match arm should look like.
Update:
In response to the feedback I've squeezed unnecessary things out of the code and below is what's left:
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::ptr::null_mut;
use std::time::SystemTime;
use winapi::shared::minwindef::HINSTANCE;
use winapi::shared::windef::{HDC__, HWND__};
use winapi::um::errhandlingapi::GetLastError;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::wingdi::{BitBlt, CreateCompatibleDC, CreateSolidBrush, ExtFloodFill, FLOODFILLBORDER, RGB, SelectObject, SRCCOPY};
use winapi::um::winuser::{CreateWindowExW, CS_HREDRAW, CS_OWNDC, CS_VREDRAW, DefWindowProcW, GetDC, GetMessageW, IMAGE_BITMAP, LoadImageW, LR_CREATEDIBSECTION, LR_DEFAULTSIZE, LR_LOADFROMFILE, LWA_COLORKEY, MSG, RegisterClassW, SetLayeredWindowAttributes, ShowWindow, SW_SHOWMAXIMIZED, WNDCLASSW, WS_EX_LAYERED, WS_EX_TOOLWINDOW, WS_EX_TOPMOST, WS_EX_TRANSPARENT, WS_POPUP, WS_VISIBLE};
pub fn win32_string(value: &str) -> Vec<u16> {
OsStr::new(value).encode_wide().chain(once(0)).collect()
}
unsafe fn make_win() -> (*mut HWND__, *mut HDC__, *mut HDC__) {
let hinstance = GetModuleHandleW(null_mut());
let wndclass = WNDCLASSW {
style: CS_HREDRAW | CS_OWNDC | CS_VREDRAW,
lpfnWndProc: Some(DefWindowProcW),
hInstance: hinstance as HINSTANCE,
lpszClassName: win32_string("uwu").as_ptr(),
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: null_mut(),
hCursor: null_mut(),
hbrBackground: CreateSolidBrush(RGB(0, 0, 0)),
lpszMenuName: null_mut(),
};
RegisterClassW(&wndclass);
let handle: *mut HWND__ = CreateWindowExW(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
win32_string("uwu").as_ptr(),
win32_string("owo").as_ptr(),
WS_POPUP | WS_VISIBLE,
0,
0,
1919,
1079,
null_mut(),
null_mut(),
hinstance,
null_mut(),
) as *mut HWND__;
let mut dc = GetDC(handle);
let mut c_mem_dc = CreateCompatibleDC(dc);
return (handle, dc, c_mem_dc);
}
pub unsafe fn make_trasnparent(handle: *mut HWND__) {
ShowWindow(handle, SW_SHOWMAXIMIZED);
SetLayeredWindowAttributes(handle, RGB(0, 0, 0),
0, LWA_COLORKEY);
}
fn main() {
unsafe {
let (handle, dc, c_mem_dc) = make_win();
make_trasnparent(handle);
let mut image = CreateCompatibleDC(null_mut());
SelectObject(image, LoadImageW(null_mut(),
win32_string("C:\\Users\\grass\\Desktop\\codes\\Rust\\catso_v2\\src\\cat2.bmp").as_ptr(),
IMAGE_BITMAP,
LR_DEFAULTSIZE as i32,
LR_DEFAULTSIZE as i32,
LR_LOADFROMFILE | LR_CREATEDIBSECTION));
let mut msg: MSG = std::mem::uninitialized();
let mut back_then = SystemTime::now();
loop {
if GetMessageW(&mut msg as *mut MSG, handle, 0, 0).is_positive() {
let now = SystemTime::now();
if now.duration_since(back_then)
.unwrap()
.as_millis() > 40 {
ExtFloodFill(c_mem_dc, 1920, 1080, RGB(0, 0, 0), FLOODFILLBORDER);
BitBlt(c_mem_dc, 0, 0, 100, 100, image, 0, 0, SRCCOPY);
BitBlt(dc, 0, 0, 1920, 1080, c_mem_dc, 0, 0, SRCCOPY);
back_then = now;
};
} else {
break;
}
}
}
}
The problem is still the same and same cargo.toml is used.
Update:
I've printed out all of the return values for few iterations. So far My loaded image returns a valid handle and so does CreateCompatibleDC. Both BitBlt return 1, hence they succeed. GetLastError always returns zero.
Another thing I've attempted to do is to create a compatible bitmap and select it into c_mem_dc to ensure it has an appropriate bitmap size as stated here: another SO question This however didn't help.
I'm sorry that I don't know much about rust, so I tried the code through C++, and I can indeed reproduce the problem. I think you have a problem with the drawing.
I modified it to the following code, you can refer to it to modify it to rust code:
#include <windows.h>
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
HBITMAP bitmap;
int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR szCmdLine, _In_ int iCmdShow)
{
static TCHAR szAppName[] = TEXT("hello windows");
WNDCLASS wndclass;
wndclass.style = CS_HREDRAW | CS_OWNDC | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
if (!RegisterClass(&wndclass))
{
MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR);
}
HWND hwnd = CreateWindowEx(WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW,
szAppName,
TEXT("the hello program"),
WS_POPUP | WS_VISIBLE,
0,
0,
800,
600,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(hwnd, iCmdShow);
UpdateWindow(hwnd);
SetLayeredWindowAttributes(hwnd, RGB(0, 0, 0),
0, LWA_COLORKEY);
bitmap = (HBITMAP)LoadImage(NULL, L"shaokao.bmp", IMAGE_BITMAP, LR_DEFAULTSIZE,
LR_DEFAULTSIZE, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_PAINT:
{
BITMAP bm;
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
HDC hdcMem = CreateCompatibleDC(hdc);
HBITMAP hbmOld = (HBITMAP)SelectObject(hdcMem, bitmap);
GetObject(bitmap, sizeof(bm), &bm);
BitBlt(hdc, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY);
SelectObject(hdcMem, hbmOld);
DeleteDC(hdcMem);
EndPaint(hwnd, &ps);
break;
}
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
And it works for me.
I'm trying to make a fully transparent window with winapi to later serve as an overlay. It seems to work as far as creating window and registering the class goes, but the UpdateLayeredWindow function seems to have issues with the window handle as GetLastError returns code 6. I'm not sure what exactly causes it.
Without Updating window appears, is interactable, and works ok. After my attempt to update it, it disappears (not transparent though, since events pass to the desktop), and I get error 6.
A puzzling thing is that I still get the messages as the loop doesn't stop until I force quit the process.
#![windows_subsystem = "windows"]
use kernel32::{GetLastError, GetModuleHandleW};
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::ptr;
use std::ptr::{null, null_mut};
use user32::CreateWindowExW;
use winapi::shared::minwindef::{HINSTANCE, LPARAM, LRESULT, UINT, WPARAM};
use winapi::shared::windef::{HDC__, HWND, HWND__, POINT, SIZE};
use winapi::um::wingdi::{
CreateCompatibleDC, DeleteDC, AC_SRC_ALPHA, AC_SRC_OVER, BLENDFUNCTION, RGB,
};
use winapi::um::winuser::{
DefWindowProcW, DispatchMessageW, GetDC, GetMessageW, RegisterClassW, ReleaseDC,
TranslateMessage, UpdateLayeredWindow, CS_HREDRAW, CS_OWNDC, CS_VREDRAW, CW_USEDEFAULT, MSG,
ULW_COLORKEY, WNDCLASSW, WS_EX_LAYERED, WS_EX_TOOLWINDOW, WS_OVERLAPPED, WS_VISIBLE,
};
fn win32_string(value: &str) -> Vec<u16> {
OsStr::new(value).encode_wide().chain(once(0)).collect()
}
struct BasicWindow {
window_class: WNDCLASSW,
window_handle: *mut HWND__,
mem_DC: Option<*mut HDC__>,
screen_DC: Option<*mut HDC__>,
}
impl BasicWindow {
fn new(class_name: String, window_name: String) -> Self {
unsafe {
let hinstance = GetModuleHandleW(null_mut());
let wnd_class = WNDCLASSW {
style: CS_HREDRAW | CS_OWNDC | CS_VREDRAW,
lpfnWndProc: Some(DefWindowProcW),
hInstance: hinstance as HINSTANCE,
lpszClassName: win32_string(&class_name).as_ptr(),
cbClsExtra: 0,
cbWndExtra: 0,
hIcon: null_mut(),
hCursor: null_mut(),
hbrBackground: null_mut(),
lpszMenuName: null_mut(),
};
let atom = RegisterClassW(&wnd_class);
let handle: *mut HWND__ = CreateWindowExW(
WS_EX_LAYERED | WS_EX_TOOLWINDOW,
win32_string(&class_name).as_ptr(),
win32_string(&window_name).as_ptr(),
WS_OVERLAPPED | WS_VISIBLE,
0,
0,
1920,
1080,
null_mut(),
null_mut(),
hinstance,
null_mut(),
) as *mut HWND__;
println!("handle: {:?}", handle);
return Self {
window_class: wnd_class,
window_handle: handle,
mem_DC: None,
screen_DC: None,
};
}
}
pub unsafe fn make_transparent(&mut self) {
self.screen_DC = Some(GetDC(null_mut()));
self.mem_DC = Some(CreateCompatibleDC(GetDC(self.window_handle))); //Some(CreateCompatibleDC(self.screen_DC.unwrap()));
let mut blend_s = BLENDFUNCTION {
BlendOp: AC_SRC_OVER,
BlendFlags: 0,
SourceConstantAlpha: 255,
AlphaFormat: AC_SRC_ALPHA,
};
let outcome = UpdateLayeredWindow(
self.window_handle,
self.screen_DC.unwrap(),
&mut POINT { x: 0, y: 0 },
&mut SIZE { cx: 1920, cy: 1080 },
self.mem_DC.unwrap(),
&mut POINT { x: 0, y: 0 },
RGB(255, 255, 255),
&mut blend_s,
ULW_COLORKEY,
);
println!("outcome: {:?}", outcome);
println!("err was: {:?}", GetLastError());
}
// pub unsafe fn event_job(hWnd: HWND__, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT {
//
// }
}
fn main() {
let mut win: BasicWindow = BasicWindow::new("hewwo".to_owned(), "UwU".to_owned());
unsafe {
win.make_transparent();
loop {
let mut msg: MSG = std::mem::uninitialized();
if GetMessageW(&mut msg as *mut MSG, win.window_handle, 0, 0).is_positive() {
TranslateMessage(&msg as *const MSG);
DispatchMessageW(&msg as *const MSG);
} else {
break;
}
}
match win.mem_DC {
Some(mut hDC) => {
println!("releasing mem_dc with outcome: {:?}", DeleteDC(hDC));
println!(
"releasing screen_dc with outcome: {:?}",
ReleaseDC(win.window_handle, win.screen_DC.unwrap())
);
}
_ => {}
}
}
}
Cargo.toml:
[package]
name = "catso"
version = "0.1.0"
authors = ["HerbyBoi <grassyZest#gmail.com>"]
edition = "2018"
[dependencies]
winapi = "0.3.9"
widestring = "0.4.3"
user32-sys = "0.2.0"
kernel32-sys="0.2.2"
[features]
default=["winapi/winuser","winapi/minwindef","winapi/windef","winapi/wingdi"]
I coded simple windowing app with rust and winapi. When I assigned DefWindowProcW function to lpfnWndProc field of WndClass structure, code worked fine. However, when I assign the WndProc function I defined, the window does not appear and even message box for exception handling also does not appear. I want to know why it doesn't work and how fix it.
This is the WndClass structure I defined.
let WndClass = WNDCLASSW {
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some( DefWindowProcW ),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance,
hIcon: LoadIconW( null_mut(), IDC_ARROW ),
hCursor: LoadCursorW( null_mut() , IDI_APPLICATION ),
hbrBackground: GetStockObject(WHITE_BRUSH as i32) as HBRUSH,
lpszMenuName: null_mut(),
lpszClassName:lpszClass.as_ptr(),
};
And this is whole code.
extern crate winapi;
use self::winapi::{
shared::{
windef::{HWND, HBRUSH},
minwindef::{LPARAM, WPARAM, UINT, LRESULT},
},
um::{
wingdi::{GetStockObject, WHITE_BRUSH},
libloaderapi::GetModuleHandleW,
winuser::{
RegisterClassW,
CreateWindowExW,
GetMessageW,
TranslateMessage,
DispatchMessageW,
PostQuitMessage,
DefWindowProcW,
LoadIconW,
LoadCursorW,
MessageBoxW,
ShowWindow,
MSG,
WS_OVERLAPPEDWINDOW,
WS_VISIBLE,
CW_USEDEFAULT,
WNDCLASSW,
SW_SHOWDEFAULT,
CS_HREDRAW,
CS_VREDRAW,
MB_OK,
IDC_ARROW,
IDI_APPLICATION
},
}
};
use std::ptr::{ null, null_mut };
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use std::iter::once;
#[cfg(windows)]
fn win32_string( value : &str ) -> Vec<u16> {
OsStr::new( value ).encode_wide().chain( once( 0 ) ).collect()
}
unsafe fn handle_message( handle: HWND, message: &mut MSG ) -> bool {
if GetMessageW(message, handle, 0, 0) > 0 {
TranslateMessage(message);
DispatchMessageW(message);
true
} else {
false
}
}
fn main() {
unsafe{
let mut hWnd: HWND;
let mut Message: MSG = std::mem::zeroed();
let mut hInstance = GetModuleHandleW( std::ptr::null_mut() );
let lpszClass = win32_string("First Window");
let mut g_hInst = hInstance;
let WndClass = WNDCLASSW {
style: CS_HREDRAW | CS_VREDRAW,
lpfnWndProc: Some( DefWindowProcW ),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance,
hIcon: LoadIconW( null_mut(), IDC_ARROW ),
hCursor: LoadCursorW( null_mut() , IDI_APPLICATION ),
hbrBackground: GetStockObject(WHITE_BRUSH as i32) as HBRUSH,
lpszMenuName: null_mut(),
lpszClassName:lpszClass.as_ptr(),
};
RegisterClassW( &WndClass );
hWnd = CreateWindowExW(
0,
lpszClass.as_ptr(),
lpszClass.as_ptr(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
null_mut(),
null_mut(),
hInstance,
null_mut()
);
if hWnd.is_null() {
let title = win32_string("Error!");
let text = win32_string("Creating window is faield");
let ret = MessageBoxW(0 as HWND, text.as_ptr(), title.as_ptr(), MB_OK);
} else {
loop {
if !handle_message(0 as HWND, &mut Message) {
break;
}
}
}
}
}
#[cfg(windows)]
unsafe extern "system" fn WndProc(hWnd: HWND, iMessage: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT {
match iMessage {
WM_DESTROY => {
PostQuitMessage(0);
0
},
_ => DefWindowProcW(hWnd, iMessage, wParam, lParam)
}
}
You did not quote this win32 constant: WM_DESTROY, if you use the if condition like:
if iMessage == WM_DESTROY {
PostQuitMessage(0);
}
Then you will get the compile error:
error[E0425]: cannot find value `WM_DESTROY` in this scope
Just add WM_DESTROY:
use self::winapi::{
shared::{
windef::{HWND, HBRUSH},
minwindef::{LPARAM, WPARAM, UINT, LRESULT,ATOM},
},
um::{
errhandlingapi::GetLastError,
wingdi::{GetStockObject, WHITE_BRUSH},
libloaderapi::GetModuleHandleW,
winuser::{
RegisterClassW,
...,
IDI_APPLICATION,
WM_DESTROY
},
}
};
In addition, you need to show the window with ShowWindow after create it:
...
lpfnWndProc: Some( WndProc ),
...
if hWnd.is_null() {
let title = win32_string("Error!");
let text = win32_string("Creating window is faield");
let ret = MessageBoxW(0 as HWND, text.as_ptr(), title.as_ptr(), MB_OK);
} else {
println!("successful");
ShowWindow(hWnd, SW_SHOWDEFAULT);
loop {
if !handle_message(0 as HWND, &mut Message) {
break;
}
}
}
Then it works for me.