Title text not showing in Win32 application written in Rust - winapi

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;

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.

Why do SetWindowLongW and GetWindowLongW strip pointers' first 2 digits?

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.

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

If I assign wndproc function to wndclass then error occur

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.

Resources