After following the Opening a Win32 Window tutorial, when I use the cargo run command, it shows a window that does not have a title.
#![allow(unused)]
#[link(name = "Kernel32")]
extern "system" {
pub fn GetModuleHandleW(lpModuleName: LPCWSTR) -> HMODULE;
}
#[link(name = "User32")]
extern "system" {
pub fn RegisterClassW(lpWndClass: *const WNDCLASSW) -> ATOM;
pub fn GetLastError() -> DWORD;
pub fn CreateWindowExW(
dwExStyle: DWORD,
lpClassName: LPCWSTR,
lpWindowName: LPCWSTR,
dwStyle: DWORD,
X: c_int,
Y: c_int,
nWidth: c_int,
nHeight: c_int,
hWndParent: HWND,
hMenu: HMENU,
hInstance: HINSTANCE,
lpParam: LPVOID,
) -> HWND;
pub fn ShowWindow(hWnd: HWND, nCmdShow: c_int) -> BOOL;
pub fn DefWindowProcW(hWnd: HWND, Msg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT;
pub fn PostQuitMessage(nExitCode: c_int);
pub fn DestroyWindow(hWnd: HWND) -> BOOL;
pub fn GetMessageW(
lpMsg: *mut MSG,
hWnd: HWND,
wMsgFilterMin: UINT,
wMsgFilterMax: UINT,
) -> BOOL;
pub fn TranslateMessage(lpMsg: *const MSG) -> BOOL;
pub fn DispatchMessageW(lpMsg: *const MSG) -> LRESULT;
pub fn UpdateWindow(hWnd: HWND) -> BOOL;
}
pub type c_int = i32;
pub type c_uint = u32;
pub type HANDLE = PVOID;
pub type HBRUSH = HANDLE;
pub type HCURSOR = HICON;
pub type HICON = HANDLE;
pub type HINSTANCE = HANDLE;
pub type HWND = HANDLE;
pub type LONG_PTR = isize;
pub type LPARAM = LONG_PTR;
pub type LPCWSTR = *const WCHAR;
pub type LRESULT = LONG_PTR;
pub type PVOID = *mut core::ffi::c_void;
pub type UINT = c_uint;
pub type UINT_PTR = usize;
pub type WCHAR = wchar_t;
pub type wchar_t = u16;
pub type WPARAM = UINT_PTR;
pub type HMODULE = HINSTANCE;
pub type ATOM = WORD;
pub type WORD = c_ushort;
pub type c_ushort = u16;
pub type DWORD = u32;
pub type HMENU = HANDLE;
pub type LPVOID = *mut core::ffi::c_void;
pub type BOOL = c_int;
pub type LONG = c_long;
pub type c_long = i32;
pub type WNDPROC = Option<
unsafe extern "system" fn(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT,
>;
#[repr(C)]
pub struct WNDCLASSW {
pub style: UINT,
pub lpfnWndProc: WNDPROC,
pub cbClsExtra: c_int,
pub cbWndExtra: c_int,
pub hInstance: HINSTANCE,
pub hIcon: HICON,
pub hCursor: HCURSOR,
pub hbrBackground: HBRUSH,
pub lpszMenuName: LPCWSTR,
pub lpszClassName: LPCWSTR,
}
#[repr(C)]
pub struct MSG {
pub hwnd: HWND,
pub message: UINT,
pub wParam: WPARAM,
pub lParam: LPARAM,
pub time: DWORD,
pub pt: POINT,
pub lPrivate: DWORD,
}
#[repr(C)]
pub struct POINT {
pub x: LONG,
pub y: LONG,
}
impl Default for MSG {
#[inline]
#[must_use]
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
impl Default for WNDCLASSW {
#[inline]
#[must_use]
fn default() -> Self {
unsafe { core::mem::zeroed() }
}
}
pub fn wide_null(s: &str) -> Vec<u16> {
s.encode_utf16().chain(Some(0)).collect()
}
const WS_OVERLAPPED: u32 = 0x00000000;
const WS_CAPTION: u32 = 0x00C00000;
const WS_SYSMENU: u32 = 0x00080000;
const WS_THICKFRAME: u32 = 0x00040000;
const WS_MINIMIZEBOX: u32 = 0x00020000;
const WS_MAXIMIZEBOX: u32 = 0x00010000;
const WS_OVERLAPPEDWINDOW: u32 =
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
const CW_USEDEFAULT: c_int = 0x80000000_u32 as c_int;
const SW_SHOW: i32 = 5;
const WM_NCCREATE: u32 = 0x0081;
const WM_DESTROY: u32 = 0x0002;
const WM_CLOSE: u32 = 0x0010;
extern "system" fn window_proc(hwnd: HWND, uMsg: UINT, wParam: WPARAM, lParam: LPARAM) -> LRESULT {
unsafe {
match uMsg {
WM_NCCREATE => {
return 1;
}
WM_DESTROY => {
PostQuitMessage(0);
return 0;
}
WM_CLOSE => {
DestroyWindow(hwnd);
return 0;
}
_ => {
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
}
}
}
}
fn main() {
let hInstance = unsafe { GetModuleHandleW(core::ptr::null()) };
let sample_window_class_wn = wide_null("Sample window class");
let mut wc = WNDCLASSW::default();
wc.lpfnWndProc = Some(window_proc);
wc.hInstance = hInstance;
wc.lpszClassName = sample_window_class_wn.as_ptr();
unsafe {
if RegisterClassW(&wc) == 0 {
println!(
"Not able to register window class. Error code: {}",
GetLastError()
);
}
}
let sample_window_name_wn = wide_null("sample window");
let hwnd = unsafe {
CreateWindowExW(
0,
sample_window_class_wn.as_ptr(),
sample_window_name_wn.as_ptr(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
core::ptr::null_mut(),
core::ptr::null_mut(),
hInstance,
core::ptr::null_mut(),
)
};
unsafe {
ShowWindow(hwnd, SW_SHOW);
}
unsafe {
UpdateWindow(hwnd);
}
let mut msg = MSG::default();
unsafe {
while GetMessageW(&mut msg, hwnd, 0, 0) > 0 {
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
}
The problem is the handling of WM_NCCREATE. The MSDN documentation is not very clear about this, but this message is used so that the window class is able to filter and/or modify the creation parameters of the window. These parameters are received in the lParam argument that is actually a CREATESTRUCT*.
The idea is that you do the cast with CREATESTRUCT *cs = (CREATESTRUCT *)lParam;, then you inspect and/or modify the contents of that struct and finally you pass on the message to DefWindowProc() to continue creation or return 0 to abort.
But it looks like the linked tutorial is doing return 1 without forwarding it to DefWindowProc(). Just don't do that: most messages should be forwarded to DefWindowProc() except those you know you should not.
In your particular example, you can remove the return 1 from the WM_NCCREATE branch and move the DefWindowProc() call to outside of the match:
match uMsg {
WM_NCCREATE => {
//...
//no return
}
WM_DESTROY => {
PostQuitMessage(0);
return 0;
}
WM_CLOSE => {
DestroyWindow(hwnd);
return 0;
}
_ => {}
}
DefWindowProcW(hwnd, uMsg, wParam, lParam)
NOTE: Although the Windows source code is not (yet) publicly available, the next best thing is the source code of Wine. You can check what DefWindowProc(WM_NCCREATE) does here:
case WM_NCCREATE:
if (lParam)
{
CREATESTRUCTW *cs = (CREATESTRUCTW *)lParam;
DEFWND_SetTextW( hwnd, cs->lpszName );
result = 1;
if(cs->style & (WS_HSCROLL | WS_VSCROLL))
{
SCROLLINFO si = {sizeof si, SIF_ALL, 0, 100, 0, 0, 0};
SetScrollInfo( hwnd, SB_HORZ, &si, FALSE );
SetScrollInfo( hwnd, SB_VERT, &si, FALSE );
}
}
break;
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"]