If I assign wndproc function to wndclass then error occur - winapi

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.

Related

Why do window contents disappear at redraw?

A desktop application written in Rust and winapi opens a window and adds text lines in several RedrawWindow() calls. The text lines come up as expected and persist at (some) resizing, moving and obscuring the window. However, when the window is minimized and opened again, only the last text line is shown; the previous lines are gone. What do I miss in the code?
//[dependencies]
//winapi = { version = "0.3.9", features = ["wingdi", "winuser", "libloaderapi", "combaseapi", "objbase", "shobjidl", "winerror"] }
use std::error::Error;
use std::ptr::{null, null_mut};
use std::sync::Mutex;
use std::thread;
use std::time;
use winapi::shared::minwindef::*;
use winapi::shared::windef::*;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::*;
// Custom signal to inform that new text is available
pub const WM_WEBUPDT: UINT = 0xFEDC;
/// Turns a Rust string slice into a null-terminated utf-16 vector.
pub fn wide_null(s: &str) -> Vec<u16> {
s.encode_utf16().chain(Some(0)).collect()
}
static TEXT: Mutex<String> = Mutex::new(String::new());
static mut UPPER_LINE: i32 = 0;
// Window procedure to handle events
pub unsafe extern "system" fn window_proc(
hwnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM,
) -> LRESULT {
match msg {
WM_CLOSE => {
DestroyWindow(hwnd);
}
WM_DESTROY => {
PostQuitMessage(0);
}
WM_WEBUPDT => {
RedrawWindow(hwnd, null(), null_mut(), RDW_INVALIDATE);
UPPER_LINE += 25;
}
WM_PAINT => {
let t: String = TEXT.lock().unwrap().clone();
let mut ps: PAINTSTRUCT = std::mem::zeroed();
let hdc: HDC;
hdc = BeginPaint(hwnd, &mut ps);
let mut rec: RECT = std::mem::zeroed();
GetClientRect(hwnd, &mut rec);
rec.top += 4 + UPPER_LINE;
let txt = wide_null(&t);
DrawTextW(
hdc,
txt.as_ptr(),
txt.len().try_into().unwrap(),
&mut rec,
DT_TOP | DT_LEFT,
);
EndPaint(hwnd, &ps);
}
_ => return DefWindowProcW(hwnd, msg, wparam, lparam),
}
return 0;
}
// Declare class and instantiate window
fn create_main_window(name: &str, title: &str) -> Result<HWND, Box<dyn Error>> {
let name = wide_null(name);
let title = wide_null(title);
unsafe {
let hinstance = GetModuleHandleW(null_mut());
let mut wc: WNDCLASSW = core::mem::zeroed();
wc.lpfnWndProc = Some(window_proc);
wc.hInstance = hinstance;
wc.hbrBackground = COLOR_WINDOWFRAME as HBRUSH;
wc.lpszClassName = name.as_ptr();
// Register window class
if RegisterClassW(&wc) == 0 {
MessageBoxW(
null_mut(),
wide_null("Window Registration Failed!").as_ptr(),
wide_null("Error").as_ptr(),
MB_ICONEXCLAMATION | MB_OK,
);
return Err("Window Registration Failed".into());
};
// Create a window based on registered class
let handle = CreateWindowExW(
0, // dwExStyle
name.as_ptr(), // lpClassName
title.as_ptr(), // lpWindowName
WS_OVERLAPPEDWINDOW | WS_VISIBLE, // dwStyle
810, // Int x
390, // Int y
300, // Int nWidth
300, // Int nHeight
null_mut(), // hWndParent
null_mut(), // hMenu
hinstance, // hInstance
null_mut(), // lpParam
);
if handle.is_null() {
MessageBoxW( null_mut(),
wide_null("Window Creation Failed!").as_ptr(),
wide_null("Error!").as_ptr(), MB_ICONEXCLAMATION | MB_OK,
);
return Err("Window Creation Failed!".into());
}
Ok(handle)
}
}
// Message handling loop
fn run_message_loop(hwnd: HWND) -> WPARAM {
unsafe {
let mut msg: MSG = std::mem::zeroed();
loop {
// Get message from message queue
if GetMessageW(&mut msg, hwnd, 0, 0) > 0 {
TranslateMessage(&msg);
DispatchMessageW(&msg);
} else {
// Return on error (<0) or exit (=0) cases
return msg.wParam;
}
}
}
}
fn main() {
*TEXT.lock().unwrap() = "This is first line".to_string();
let hwnd = create_main_window("Main Window", "Main Window").expect("Window creation failed!");
unsafe {
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
}
let hwnd2 = hwnd as usize;
thread::spawn(move || { unsafe {
thread::sleep(time::Duration::from_millis(100));
*TEXT.lock().unwrap() = "This is second line".to_string();
PostMessageW (hwnd2 as HWND, WM_WEBUPDT, 0 as WPARAM, 0 as LPARAM);
thread::sleep(time::Duration::from_millis(100));
*TEXT.lock().unwrap() = "This is third line".to_string();
PostMessageW (hwnd2 as HWND, WM_WEBUPDT, 0 as WPARAM, 0 as LPARAM);
}
}
);
run_message_loop(hwnd);
}
As far as I'm concerned and my test, without handling WM_ERASEBKGND,
The default handling for the WM_ERASEBKGND message is to fill the area
with the current window background color.
Also see my answer.
At the rest, as comments said, Windows buffers nothing after Minimized Windows.
Minimizing a window speeds up system performance by reducing the
amount of work an application must do when updating its main window.

Title text not showing in Win32 application written in Rust

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;

Do I need some extra steps to register the changes to the DeviceContext?

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.

UpdateLayeredWindow returns code 6 (ERROR_INVALID_HANDLE)

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"]

WM_PAINT message, BeginPaint loop

I can't understand why I got a loop on BeginPaint function. I have already read posts about this kind of loop but almost all of them recommend: "Don't forget to use BeginPaint function on WM_PAINT message, because it entails subsequent WM_PAINT messages otherwise". This isn't my case. May you give me some advices?
This is my windowclass ("CWindow"):
class CWindow {
public:
CWindow();
virtual ~CWindow();
bool RegisterClass(HINSTANCE hInstance);
bool CreateWnd(HINSTANCE hInstance);
bool Show(int nShow);
private:
HWND handleWindow;
ATOM atom;
bool isRegistered;
bool isCreated;
static LRESULT CALLBACK WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
void OnPaint();
void OnDestroy();
};
WndProc function.
LRESULT CALLBACK CWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
CWindow* windowPtr = reinterpret_cast<CWindow*> ( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
PAINTSTRUCT ps;
HDC hdc;
switch( msg ) {
case WM_PAINT:
// There is a loop right here!
hdc = BeginPaint( windowPtr->handleWindow, &ps );
// The code below doesn't executed!
RECT rect;
(void)GetClientRect(windowPtr->handleWindow, &rect);
(void)DrawText(hdc, TEXT("Hello, Windows 98!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER);
EndPaint( windowPtr->handleWindow, &ps );
break;
case WM_DESTROY:
windowPtr->OnDestroy();
break;
default:
return DefWindowProc( hWnd, msg, wParam, lParam );
}
return 0;
}
RegisterClass
bool CWindow::RegisterClass(HINSTANCE hInstance)
{
const TCHAR app_name[] = TEXT("HelloWin");
WNDCLASSEX windowClass;
ZeroMemory( &windowClass, sizeof(windowClass) );
windowClass.cbSize = sizeof(windowClass);
windowClass.style = CS_HREDRAW | CS_VREDRAW;
windowClass.lpfnWndProc = WindowProc;
windowClass.cbClsExtra = 0;
windowClass.cbWndExtra = 0;
windowClass.hInstance = hInstance;
windowClass.hIcon = 0;
windowClass.hIcon = 0;
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW);
windowClass.hbrBackground = 0;
windowClass.lpszMenuName = NULL;
windowClass.lpszClassName = app_name;
windowClass.hIconSm = NULL;
atom = RegisterClassEx( &windowClass );
DWORD errorCode = GetLastError();
if( errorCode ) {
isRegistered = 0;
std::wcout << L"ErrorCode: " << errorCode << std::endl;
} else {
isRegistered = 1;
}
return isRegistered;
}
CreateWindow
bool CWindow::CreateWnd(HINSTANCE hInstance)
{
handleWindow = CreateWindow((PCTSTR)atom, // window class name or atom
TEXT("The Hello Program"), // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
DWORD errorCode = GetLastError();
if( !handleWindow ) {
isCreated = 0;
} else {
isCreated = 1;
}
return isCreated;
}
Show
bool CWindow::Show(int nShow)
{
if( isCreated ) {
ShowWindow( handleWindow, nShow );
return UpdateWindow( handleWindow );
} else {
return 0;
}
}
WinMain
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevINstance, LPSTR lpCmdLine, int nShow )
{
CWindow window;
window.RegisterClass( hInstance );
window.CreateWnd( hInstance );
window.Show( nShow );
int response = 0;
MSG msg;
while( GetMessage( &msg, 0, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return 0;
}
Since you never call SetWindowLongPtr,
CWindow* windowPtr = reinterpret_cast<CWindow*>( GetWindowLongPtr( hWnd, GWLP_USERDATA ) );
returns a nullptr, that you subsequently try to dereference:
BeginPaint( windowPtr->handleWindow, &ps )
That will trigger an access violation exception, causing the BeginPaint call to never even get executed, leaving the invalid region as is. As a consequence, the system keeps generating WM_PAINT messages. That's the same issue as not calling BeginPaint altogether.1
To solve this, you'll either have to attach the window handle to the window instance by calling SetWindowLongPtr, or simply use the hWnd parameter that's passed into your CWindow::WindowProc.
1 Note that the system silently handles unhandled exceptions in your WindowProc on 64-bit versions of Windows under certain conditions.

Resources