Create a gist now

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.

Show comment
Hide comment
@lightern

lightern 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;
        }
    }
}

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;
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment