Win32 OpenGL Window in rust: unable to load certain function pointers - winapi

#[cfg(windows)] extern crate winapi;
#[cfg(windows)] use winapi::shared::windef::HWND;
#[cfg(windows)] use winapi::shared::windef::HMENU;
#[cfg(windows)] use winapi::shared::windef::HBRUSH;
#[cfg(windows)] use winapi::shared::minwindef::HINSTANCE;
#[cfg(windows)] use winapi::shared::minwindef::UINT;
#[cfg(windows)] use winapi::shared::minwindef::DWORD;
#[cfg(windows)] use winapi::shared::minwindef::WPARAM;
#[cfg(windows)] use winapi::shared::minwindef::LPARAM;
#[cfg(windows)] use winapi::shared::minwindef::LRESULT;
#[cfg(windows)] use winapi::um::winnt::LPCWSTR;
#[cfg(windows)] use winapi::um::winuser::WS_OVERLAPPEDWINDOW;
#[cfg(windows)] use winapi::um::winuser::WS_VISIBLE;
#[cfg(windows)] use winapi::um::winuser::WNDCLASSW;
#[cfg(windows)] use std::os::windows::ffi::OsStrExt;
#[cfg(windows)] use std::ffi::OsStr;
#[cfg(windows)] use std::os::raw::c_void;
#[cfg(windows)] use winapi::um::libloaderapi::GetProcAddress;
#[cfg(windows)] use gl::types::*;
// Vertex data
static VERTEX_DATA: [GLfloat; 6] = [0.0, 0.5, 0.5, -0.5, -0.5, -0.5];
// Shader sources
static VS_SRC: &'static str = "
#version 150
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}";
static FS_SRC: &'static str = "
#version 150
out vec4 out_color;
void main() {
out_color = vec4(1.0, 1.0, 1.0, 1.0);
}";
mod gl
{
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
#[cfg(windows)]
fn to_wstring(str : &str) -> Vec<u16>
{
let v : Vec<u16> =
OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect();
v
}
#[cfg(windows)]
pub unsafe extern "system" fn window_proc(h_wnd :HWND,
msg :UINT, w_param :WPARAM, l_param :LPARAM) -> LRESULT
{
if msg == winapi::um::winuser::WM_DESTROY {
winapi::um::winuser::PostQuitMessage(0i32);
}
return winapi::um::winuser::DefWindowProcW(h_wnd, msg, w_param, l_param);
}
fn draw_gl_scene(window_width: i32, window_height: i32,
h_dc: winapi::shared::windef::HDC)
{
unsafe
{
let module = winapi::um::libloaderapi::LoadLibraryW(to_wstring("opengl32.dll") .as_ptr());
// gl::load_with(|s| GetProcAddress(module,
// s.as_ptr() as *const i8) as *const c_void);
gl::Viewport::load_with(|s| GetProcAddress(module,
s.as_ptr() as *const i8) as *const c_void);
if gl::Viewport::is_loaded() {
gl::Viewport(0, 0, window_width, window_height);
} else {
panic!("gl::Viewport was not loaded")
}
gl::ClearColor::load_with(|s| GetProcAddress(module,
s.as_ptr() as *const i8) as *const c_void);
if gl::ClearColor::is_loaded() {
gl::ClearColor(0.12109375f32, 0.12109375f32, 0.12109375f32, 1.0f32);
} else {
panic!("gl::ClearColor was not loaded")
}
gl::GetError::load_with(|s| GetProcAddress(module,
s.as_ptr() as *const i8) as *const c_void);
let error_check_value: GLenum = gl::GetError();
/*
gl::CreateShader::load_with(|s| GetProcAddress(module,
s.as_ptr() as *const i8) as *const c_void);
let mut vertex_shader_id = 0u32;
if gl::CreateShader::is_loaded() {
vertex_shader_id = gl::CreateShader(gl::VERTEX_SHADER);
} else {
panic!("gl::CreateShader was not loaded")
}
*/
/*
let c_str = std::ffi::CString::new(VS_SRC.as_bytes()).unwrap();
gl::ShaderSource::load_with(|s| GetProcAddress(module,
s.as_ptr() as *const i8) as *const c_void);
if gl::ShaderSource::is_loaded() {
gl::ShaderSource(vertex_shader_id, 1, &c_str.as_ptr(), std::ptr::null_mut());
} else {
// panic!("gl::ShaderSource was not loaded")
}
gl::CompileShader::load_with(|s| GetProcAddress(module,
s.as_ptr() as *const i8) as *const c_void);
if gl::CompileShader::is_loaded() {
gl::CompileShader(vertex_shader_id);
} else {
// panic!("gl::CompileShader was not loaded")
}
let mut vertex_array_id: GLuint = 0u32;
gl::GenVertexArrays::load_with(|s| GetProcAddress(module,
s.as_ptr() as *const i8) as *const c_void);
if gl::GenVertexArrays::is_loaded() {
gl::GenVertexArrays(1, &mut vertex_array_id as *mut u32);
} else {
panic!("gl::GenVertexArrays was not loaded")
}
*/
gl::Clear::load_with(|s| GetProcAddress(module,
s.as_ptr() as *const i8) as *const c_void);
if gl::Clear::is_loaded() {
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
} else {
panic!("gl::Clear was not loaded")
}
winapi::um::wingdi::SwapBuffers(h_dc);
}
}
fn main()
{
unsafe
{
let class_name = to_wstring("OpenGL");
let h_instance = winapi::um::libloaderapi::GetModuleHandleW(std::ptr::null_mut());
let wnd = WNDCLASSW {
style: 0,
lpfnWndProc: Some(window_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: h_instance,
hIcon: winapi::um::winuser::LoadIconW(0 as HINSTANCE,
winapi::um::winuser::IDI_APPLICATION),
hCursor: winapi::um::winuser::LoadCursorW(0 as HINSTANCE,
winapi::um::winuser::IDI_APPLICATION),
hbrBackground: 16 as HBRUSH,
lpszMenuName: 0 as LPCWSTR,
lpszClassName: class_name.as_ptr(),
};
winapi::um::winuser::RegisterClassW(&wnd);
let window_width = 640;
let window_height = 480;
let h_wnd_window = winapi::um::winuser::CreateWindowExW(0, class_name.as_ptr(),
to_wstring("OpenGL Example").as_ptr(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, window_width, window_height, 0 as HWND, 0 as HMENU, h_instance,
std::ptr::null_mut());
let mut msg = winapi::um::winuser::MSG {
hwnd : 0 as HWND,
message : 0 as UINT,
wParam : 0 as WPARAM,
lParam : 0 as LPARAM,
time : 0 as DWORD,
pt : winapi::shared::windef::POINT { x: 0, y: 0, },
};
let h_dc = winapi::um::winuser::GetDC(h_wnd_window);
let pfd = winapi::um::wingdi::PIXELFORMATDESCRIPTOR {
nSize: std::mem::size_of::<winapi::um::wingdi::PIXELFORMATDESCRIPTOR>() as u16,
nVersion: 1,
dwFlags: winapi::um::wingdi::PFD_DRAW_TO_WINDOW
| winapi::um::wingdi::PFD_SUPPORT_OPENGL | winapi::um::wingdi::PFD_DOUBLEBUFFER,
iPixelType: winapi::um::wingdi::PFD_TYPE_RGBA,
cColorBits: 64,
cRedBits: 0,
cRedShift: 0,
cGreenBits: 0,
cGreenShift: 0,
cBlueBits: 0,
cBlueShift: 0,
cAlphaBits: 0,
cAlphaShift: 0,
cAccumBits: 0,
cAccumRedBits: 0,
cAccumGreenBits: 0,
cAccumBlueBits: 0,
cAccumAlphaBits: 0,
cDepthBits: 32,
cStencilBits: 8,
cAuxBuffers: 0,
iLayerType: winapi::um::wingdi::PFD_MAIN_PLANE,
bReserved: 0,
dwLayerMask: 0,
dwVisibleMask: 0,
dwDamageMask: 0,
};
let pixel_format = winapi::um::wingdi::ChoosePixelFormat(h_dc,
&pfd as *const winapi::um::wingdi::PIXELFORMATDESCRIPTOR);
winapi::um::wingdi::SetPixelFormat(h_dc, pixel_format,
&pfd as *const winapi::um::wingdi::PIXELFORMATDESCRIPTOR);
let h_rc = winapi::um::wingdi::wglCreateContext(h_dc);
winapi::um::wingdi::wglMakeCurrent(h_dc, h_rc);
winapi::um::winuser::ShowWindow(h_wnd_window, winapi::um::winuser::SW_SHOW);
loop
{
if winapi::um::winuser::PeekMessageW(&mut msg, 0u32 as HWND, 0u32, 0u32,
winapi::um::winuser::PM_REMOVE) > 0i32 {
if msg.message == winapi::um::winuser::WM_QUIT {
break;
} else {
winapi::um::winuser::TranslateMessage(&mut msg);
winapi::um::winuser::DispatchMessageW(&mut msg);
}
} else {
draw_gl_scene(window_width, window_height, h_dc);
}
}
}
}
The above code is the version of my implementation of the outdated project located here: https://www.codeproject.com/tips/1053658/win-gui-programming-in-rust-language/
Using the latest version of winapi and gl_generator as the sole two dependencies, I expanded on the starter code to successfully render an OpenGL context in tandem with the Win32 API as opposed to the glfw as suggested by the contributors of the cargo crate gl-rs. When run on a Windows machine, this project as it is presents a dark screen, as seen in this link.
Any further attempt to render something more substantial has been fruitless thus far, due to an inability to load any function pointers that relate to shaders or vertex buffer objects.
If you comb over the function draw_gl_scene, you'll see that I have a generic "load_with" function commented out and a series of function-specific "load_with" functions alongside execution code or panics depending on whether the function in question "is_loaded". The generic "load_with" I've curbed for the time being until writing function-specific "load-with" functions is no longer tenable.
As for CreateShader, ShaderSource, CompileShader, and GenVertexArrays, none of them successfully load if I uncomment any of them. I'm sure this is a user error, The odds that one of winapi and gl-rs or an anomalistic interaction between winapi and gl_generator is the cause is unlikely. I'm at my wit's end as to how to resolve this. I appreciate your time if you've read this far.

Read the wiki and this too about loading OGL pointers.
Briefly, for Windows:
GetProcAddress is only valid for OpenGL <= 1.1
wglGetProcAddress is only valid for OpenGL > 1.1
Also, the use of shaders requires OpenGL >= 2.0. Better go with OGL >= 3.2 Core Profile. For this context:
You need a proper context for OpenGL >= 3.0, which is created with the use of wglCreateContextAttribsARB.
You need a valid and current context to get the pointer to wglCreateContextAttribsARB itself by using wglGetProcAddress.
This context may be temporary. It can be created with wglCreateContext. You can delete it after you got that pointer; and the create the "real" context.
.

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

Using winapi in Rust to get a bitmap from a window

.. Hi, I have this code :
#[cfg(windows)] extern crate winapi;
use winapi::um::winuser::{FindWindowW, GetClientRect, GetWindowDC, GetDC, ReleaseDC};
use winapi::um::wingdi::{CreateBitmap, CreateCompatibleBitmap, CreateCompatibleDC, SelectObject, GetObjectW, SaveDC, DeleteDC, DeleteObject};
use std::ptr::null_mut;
use std::mem::zeroed;
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
#[cfg(windows)]
fn find_window(name: &str) {
let window: Vec<u16> = OsStr::new(name).encode_wide().chain(once(0)).collect();
let hwnd = unsafe { FindWindowW(null_mut(), window.as_ptr()) };
if hwnd != null_mut() {
println!("Window found");
let mut my_rect = unsafe { zeroed::<winapi::shared::windef::RECT>() };
let _client_rect = unsafe { GetClientRect(hwnd, &mut my_rect) };
let w = my_rect.right - my_rect.left;
let h = my_rect.bottom - my_rect.top;
let hwnd_dc = unsafe { GetWindowDC(hwnd) };
let mem_dc = unsafe { CreateCompatibleDC(hwnd_dc) };
let bmp = unsafe { CreateCompatibleBitmap(mem_dc, w, h) };
//SelectObject(mem_dc, bmp); <== Problem is here
//DeleteObject(bmp); <== Same problem here
unsafe { DeleteDC(mem_dc) };
unsafe { ReleaseDC(hwnd, hwnd_dc) };
}
else {
println!("Window not found");
}
}
fn main() {
find_window("Firefox"); // just for test
}
If I cargo run this, I get :
SelectObject(mem_dc, bmp);
| ^^^ expected enum `std::ffi::c_void`, found enum `winapi::shared::windef::HBITMAP__`
So I get it, it doesn't want that bitmap pointer, but in the win32 documentation, it says that this function (SelectObject) accepts a Device Context (mem_dc) and an object created by some function like CreateCompatibleBitmap.
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-selectobject
The binding in Rust accepts the two same arguments but it seems to want that std::ffi::c_void but looking a the Rust doc, I don't get what it is exactly.
Can you help me finding what's wrong ? Thanks.
Add:
use winapi::shared::windef::{HBITMAP, HBITMAP__, HGDIOBJ, HWND, POINT, RECT, SIZE};
...
unsafe {SelectObject(mem_dc, bmp as HGDIOBJ)};
unsafe {DeleteObject(bmp as HGDIOBJ)};

Resources