Instantly share code, notes, and snippets.

Embed
What would you like to do?
#![windows_subsystem = "windows"]
extern crate winapi;
extern crate user32;
extern crate kernel32;
use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use std::iter::once;
use std::mem;
use std::ptr::null_mut;
use std::io::Error;
use self::user32::{
DefWindowProcW,
RegisterClassW,
CreateWindowExW,
TranslateMessage,
DispatchMessageW,
GetMessageW,
};
use self::winapi::HWND;
use self::kernel32::GetModuleHandleW;
use self::winapi::winuser::{
MSG,
WNDCLASSW,
CS_OWNDC,
CS_HREDRAW,
CS_VREDRAW,
CW_USEDEFAULT,
WS_OVERLAPPEDWINDOW,
WS_VISIBLE,
};
fn win32_string( value : &str ) -> Vec<u16> {
OsStr::new( value ).encode_wide().chain( once( 0 ) ).collect()
}
struct Window {
handle : HWND,
}
fn create_window( name : &str, title : &str ) -> Result<Window, Error> {
let name = win32_string( name );
let title = win32_string( title );
unsafe {
let hinstance = GetModuleHandleW( null_mut() );
let wnd_class = WNDCLASSW {
style : CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
lpfnWndProc : Some( DefWindowProcW ),
hInstance : hinstance,
lpszClassName : name.as_ptr(),
cbClsExtra : 0,
cbWndExtra : 0,
hIcon: null_mut(),
hCursor: null_mut(),
hbrBackground: null_mut(),
lpszMenuName: null_mut(),
};
RegisterClassW( &wnd_class );
let handle = CreateWindowExW(
0,
name.as_ptr(),
title.as_ptr(),
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
CW_USEDEFAULT,
null_mut(),
null_mut(),
hinstance,
null_mut() );
if handle.is_null() {
Err( Error::last_os_error() )
} else {
Ok( Window { handle } )
}
}
}
fn handle_message( window : &mut Window ) -> bool {
unsafe {
let mut message : MSG = mem::uninitialized();
if GetMessageW( &mut message as *mut MSG, window.handle, 0, 0 ) > 0 {
TranslateMessage( &message as *const MSG );
DispatchMessageW( &message as *const MSG );
true
} else {
false
}
}
}
fn main() {
let mut window = create_window( "my_window", "Hello Windows" ).unwrap();
loop {
if !handle_message( &mut window ) {
break;
}
}
}
@lightern

This comment has been minimized.

lightern commented May 30, 2018

First of all, thank you so much for your your text and gist! I've found this super helpful while trying to learn Rust and windows api at the same time. However I noticed that your guide was a bit outdated since winapi has been merged with user32 and kernel32 and I was thinking it would be great, if you could update your guide for others, since I bet there are many people like me wrestling with this. Also I would love to see #2 part of this guide with tips how to implement menu and some kind of input with button :)

Anyway, here's my sketch on the up to date version (please don't mind the additional comments, those were for me to make things more clear):

// Let's put this so that it won't open console
#![windows_subsystem = "windows"]

#[cfg(windows)] extern crate winapi;
// https://docs.rs/winapi/*/x86_64-pc-windows-msvc/winapi/um/libloaderapi/index.html?search=winuser

use std::ffi::OsStr;
use std::os::windows::ffi::OsStrExt;
use std::iter::once;
use std::mem;
use std::ptr::null_mut;
use std::io::Error;

use self::winapi::shared::windef::HWND;
use self::winapi::um::libloaderapi::GetModuleHandleW;
use self::winapi::um::winuser::{
    DefWindowProcW,
    RegisterClassW,
    CreateWindowExW,
    TranslateMessage,
    DispatchMessageW,
    GetMessageW,
};
use self::winapi::um::winuser::{
    MSG,
    WNDCLASSW,
    CS_OWNDC,
    CS_HREDRAW,
    CS_VREDRAW,
    CW_USEDEFAULT,
    WS_OVERLAPPEDWINDOW,
    WS_VISIBLE,
};


// ----------------------------------------------------

// We have to encode text to wide format for Windows
#[cfg(windows)]
fn win32_string( value : &str ) -> Vec<u16> {
    OsStr::new( value ).encode_wide().chain( once( 0 ) ).collect()
}

// Window struct
#[cfg(windows)]
struct Window {
    handle : HWND,
}

// Create window function 
#[cfg(windows)]
fn create_window( name : &str, title : &str ) -> Result<Window, Error> {
    let name = win32_string( name );
    let title = win32_string( title );

    unsafe {

    	// Create handle instance that will call GetModuleHandleW, which grabs the instance handle of WNDCLASSW (check third parameter)
        let hinstance = GetModuleHandleW( null_mut() );
        
        // Create "class" for window, using WNDCLASSW struct (different from Window our struct)
        let wnd_class = WNDCLASSW {
            style : CS_OWNDC | CS_HREDRAW | CS_VREDRAW,		// Style
            lpfnWndProc : Some( DefWindowProcW ),			// The callbackfunction for any window event that can occur in our window!!! Here you could react to events like WM_SIZE or WM_QUIT.
            hInstance : hinstance,							// The instance handle for our application which we can retrieve by calling GetModuleHandleW.
            lpszClassName : name.as_ptr(),					// Our class name which needs to be a UTF-16 string (defined earlier before unsafe). as_ptr() (Rust's own function) returns a raw pointer to the slice's buffer
            cbClsExtra : 0,									
            cbWndExtra : 0,
            hIcon: null_mut(),
            hCursor: null_mut(),
            hbrBackground: null_mut(),
            lpszMenuName: null_mut(),
        };

        // We have to register this class for Windows to use
        RegisterClassW( &wnd_class );

        // More info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms632680(v=vs.85).aspx
        // Create a window based on registered class
        let handle = CreateWindowExW(
            0,									// dwExStyle 
            name.as_ptr(),						// lpClassName, name of the class that we want to use for this window, which will be the same that we have registered before.
            title.as_ptr(),						// lpWindowName
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,	// dwStyle
            CW_USEDEFAULT,						// Int x
            CW_USEDEFAULT,						// Int y
            CW_USEDEFAULT,						// Int nWidth
            CW_USEDEFAULT,						// Int nHeight
            null_mut(),							// hWndParent
            null_mut(),							// hMenu
            hinstance,							// hInstance
            null_mut() );						// lpParam

        if handle.is_null() {
            Err( Error::last_os_error() )
        } else {
            Ok( Window { handle } )
        }
    }
}

#[cfg(windows)]
// Create message handling function with which to link to hook window to Windows messaging system
// More info: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644927(v=vs.85).aspx
fn handle_message( window : &mut Window ) -> bool {
    unsafe {
        let mut message : MSG = mem::uninitialized();
        
        // Get message from message queue with GetMessageW
        if GetMessageW( &mut message as *mut MSG, window.handle, 0, 0 ) > 0 {
            TranslateMessage( &message as *const MSG ); // Translate message into something meaningful with TranslateMessage
            DispatchMessageW( &message as *const MSG ); // Dispatch message with DispatchMessageW

            true
        } else {
            false
        }
    }
}
#[cfg(windows)]
fn main() {
    let mut window = create_window( "my_window", "Portfolio manager pro" ).unwrap();

    loop {
        if !handle_message( &mut window ) {
            break;
        }
    }
}
@TheSatoshiChiba

This comment has been minimized.

Owner

TheSatoshiChiba commented Aug 2, 2018

Hi.

First of all apologies for the late reply but for some reason I haven't seen this update at all! 😞

Unfortunately I am not using windows for my day-to-day work anymore so I won't be able to update my blog post with all the changes that have been made since I last used the winapi crate. But I will make an update to my blog post referring to your comment so that anyone else who stumbles across it will be aware of the changes. If you made a blog post about this yourself feel free to send me a link so I can include it as well.

@rokit

This comment has been minimized.

rokit commented Aug 31, 2018

Thanks TheSatoshiChiba for the tutorial and lightern for the update. I just wanted to mention that modules like winuser are gated behind a feature flag. You have to explicitly add them to your Cargo.toml.
https://github.com/retep998/winapi-rs/blob/fa4e2c5a6dc495c71e6031bdde2e52870ebf6fdf/README.md#why-am-i-getting-errors-about-unresolved-imports

Here is mine:

[target.'cfg(windows)'.dependencies]
winapi = { version = "0.3.5", features = ["winuser", "libloaderapi"] }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment