Click here to Skip to main content
15,880,392 members
Articles / Desktop Programming / Win32
Tip/Trick

Win32 GUI Programming In Rust Language

Rate me:
Please Sign up or sign in to vote.
4.94/5 (13 votes)
12 Dec 2016CPOL3 min read 77.3K   995   11   26
In this tip, we will see how we can use the Rust language to do GUI (Graphical User Interface) programming. As an example program, we will create a simple Window. Using Win32 API functions.

Introduction

Rust is a general-purpose, multi paradigm, compiled programming language. It is designed to be a fast, secured and type-safe language. The language is Open-Source and developed by Mozilla Research.

According to https://en.wikipedia.org/wiki/Rust_(programming_language) -

Although its development is sponsored by Mozilla, it is an open community project.

Rust supports both Functional and Object-Oriented programming. It supports C/C++ like pointer manipulation. So what we do in C/C++ language, we can do in Rust language, but in a very different way.

Rust Language Featuring (Taken From https://www.rust-lang.org/):

  • zero-cost abstractions
  • move semantics
  • guaranteed memory safety
  • threads without data races
  • trait-based generics
  • pattern matching
  • type inference
  • minimal runtime
  • efficient C bindings

In this tip, we will see how we can use this language to do GUI (Graphical User Interface) programming. As an example program, we will create a simple Window. Using Win32 API functions.

Using the Code

As far as I know, libraries are called Crates in Rust language. We are going to use various crates libraries in this project. libc, winapi, etc. Those crates are available in Rust crate host website (https://crates.io/).

Crates are automatically downloaded when you build your project using Cargo tool.

The Cargo is a tool for Rust language that helps in managing Rust projects. It comes with Rust official installer. The Cargo tool downloads all the dependencies needed for the project, builds dependencies and builds the project. Cargo tool helps programmer to build Rust project nicely. So we better use Cargo to make things easier.

To use the Cargo tool in our project, first we have to follow some rule. We have to create a root folder. The folder name should be your project name. Inside the root folder, we need to create an another folder called ‘src’ for keeping project source files. Because the Cargo tool expects the project source files inside a src directory and then, inside the root folder, we need to create a configuration file called ‘Cargo.toml’. The file is in TOML format which is similar to INI file but has some extra advantage.

Create the ‘Cargo.toml’ file and write the following lines of code to it:

[package]

name = "simple_window"
version = "0.0.1"
authors = [ "Your name <you@example.com>" ]

Then, add the following dependencies in the ‘Cargo.toml’ file:

[dependencies]

libc = "0.1.10"
winapi = "0.2.4"
user32-sys = "0.1.2"
kernel32-sys = "0.1.4"

First, we extern the following necessary crates in our main source file called ‘main.rs’. I assume that you are familiar with pure Win32 API functions:

C++
extern crate kernel32;
extern crate user32;
extern crate winapi;
extern crate libc;

Then we ‘use’ necessary types and functions from those libraries:

C++
use winapi::windef::HWND;
use winapi::windef::HMENU;
use winapi::windef::HBRUSH;
use winapi::minwindef::HINSTANCE;

use winapi::minwindef::UINT;
use winapi::minwindef::DWORD;
use winapi::minwindef::WPARAM;
use winapi::minwindef::LPARAM;
use winapi::minwindef::LRESULT;
use winapi::winnt::LPCWSTR;

use winapi::winuser::WS_OVERLAPPEDWINDOW;
use winapi::winuser::WS_VISIBLE;
use winapi::winuser::WNDCLASSW;

use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;

The following function is used to convert normal string to wide string:

C++
fn to_wstring(str : &str) -> Vec<u16> {
    let v : Vec<u16> =
            OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect();
    v
}

This is our window message handler function. Currently, it only processes the WM_DESTROY message to exit our window properly on close event.

C++
pub unsafe extern "system" fn window_proc(h_wnd :HWND, 
	msg :UINT, w_param :WPARAM, l_param :LPARAM) -> LRESULT
{
    if msg == winapi::winuser::WM_DESTROY {
        user32::PostQuitMessage(0);
    }
    return user32::DefWindowProcW(h_wnd, msg, w_param, l_param);
}

You may have noticed that we have used an ‘unsafe’ keyword in the above codes. According to https://doc.rust-lang.org/book/unsafe.html:

Rust’s main draw is its powerful static guarantees about behavior. But safety checks are conservative by nature: there are some programs that are actually safe, but the compiler is not able to verify this is true. To write these kinds of programs, we need to tell the compiler to relax its restrictions a bit. For this, Rust has a keyword, unsafe. Code using unsafe has less restrictions than normal code does.

So that’s why we have to use the unsafe keyword.

The following function hides the Console Window since we are building a GUI application. (Actually, I don’t know how to use ‘subsytem’ with Cargo tool! If someone knows, then please comment to improve this tip. :-> )

C++
fn hide_console_window() 
{
    let window = unsafe {
        kernel32::GetConsoleWindow()
    };

    if window != std::ptr::null_mut() {
        unsafe {
            user32::ShowWindow (window, winapi::SW_HIDE)
        };
    }
}

The following is the entry point function of our program. We create our window inside this function by using the typical Win32 GUI programming style:

C++
fn main()
{
  // Here our unsafe code goes - 
  unsafe 
  {
    // First we hide the console window - 
    hide_console_window();

    // Then we initialize WNDCLASS structure - 

    let class_name = to_wstring("my_window");

    let wnd = WNDCLASSW {
        style: 0,
        lpfnWndProc: Some(window_proc), 
        cbClsExtra: 0,
        cbWndExtra: 0,
        hInstance: 0 as HINSTANCE,
        hIcon: user32::LoadIconW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
        hCursor: user32::LoadCursorW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
        hbrBackground: 16 as HBRUSH,
        lpszMenuName: 0 as LPCWSTR,
        lpszClassName: class_name.as_ptr(),
    };

    // We register our class - 
    user32::RegisterClassW(&wnd);

    let h_wnd_window = user32::CreateWindowExW(0, class_name.as_ptr(), 
                       to_wstring("Simple Window").as_ptr(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, 
                       0, 0, 400, 400, 0 as HWND, 0 as HMENU, 0 as HINSTANCE, std::ptr::null_mut());

    let mut msg = winapi::winuser::MSG {
        hwnd : 0 as HWND,
        message : 0 as UINT,
        wParam : 0 as WPARAM,
        lParam : 0 as LPARAM,
        time : 0 as DWORD,
        pt : winapi::windef::POINT { x: 0, y: 0, },
    };

    user32::ShowWindow(h_wnd_window, winapi::SW_SHOW);

    // Finally we run the standard application loop - 
    loop
    {   
        let pm = user32::GetMessageW(&mut msg, 0 as HWND, 0, 0);
        if pm == 0 {
            break;
        }

        if msg.message == winapi::winuser::WM_QUIT {
            break;
        }

        user32::TranslateMessage(&mut msg);
        user32::DispatchMessageW(&mut msg);
    }      
  }
}

Conclusion

Rust is a nice programming language. Programmers can learn this language to enhance their skill on programming.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Bangladesh Bangladesh
Hi, I'm Shah Farhad Reza. I'm a desktop and web software developer.

Recently I've developed an web based ERP (Enterprise Resource Planning) Software for a manufacturing company. The software is in use and working effectively fulfilling its goal (Alhamdulillah) - [February 10, 2023]

The areas of my expertise are the followings:

- OS Kernel developing.
- Programming language's compiler design and implement.
- Expert in C, C++ and Visual Basic and have basic knowledge on C#, D, Java.
- A few times used the Microsoft's new language F#.
- SQL Database programming.
- I've basic knowledge on lowest level programming language like assembly.
- Learning Mozilla’s Rust & Google’s GO programming language for software development.
- Code optimization for performance.
- Multi-threaded programming in C/C++ and Java.
- Know various advanced computer algorithm and have used them to develop graphics and simulation programs. Also working with Linear Algebra and keen to learn Quadratic Algebra in future.
- Graphics and Game programming (Both 2D and 3D).

Currently, I'm doing research on programming language and its compiler development. I've made various kind of software and now I want to share my experiences with people.


Comments and Discussions

 
SuggestionHide terminal Pin
Member 1613988614-Nov-23 8:28
Member 1613988614-Nov-23 8:28 
SuggestionI don't know Win32 but I do know some Rust and... Pin
Stephan Sokolow15-Dec-16 9:09
professionalStephan Sokolow15-Dec-16 9:09 
GeneralRe: I don't know Win32 but I do know some Rust and... Pin
Farhad Reza15-Dec-16 9:59
Farhad Reza15-Dec-16 9:59 
QuestionMany problems in the code... Pin
raymai9727-Nov-16 1:48
raymai9727-Nov-16 1:48 
AnswerRe: Many problems in the code... Pin
Farhad Reza28-Nov-16 3:25
Farhad Reza28-Nov-16 3:25 
AnswerRe: Many problems in the code... Pin
Farhad Reza11-Dec-16 23:44
Farhad Reza11-Dec-16 23:44 
QuestionCompiled from source binary doesn't work. Pin
Member 128271091-Nov-16 7:08
Member 128271091-Nov-16 7:08 
AnswerRe: Compiled from source binary doesn't work. Pin
Member 128271093-Nov-16 6:32
Member 128271093-Nov-16 6:32 
GeneralRe: Compiled from source binary doesn't work. Pin
Farhad Reza14-Dec-16 6:43
Farhad Reza14-Dec-16 6:43 
QuestionWindows 10 Pin
Member 79573371-Nov-16 6:24
Member 79573371-Nov-16 6:24 
QuestionBug in convert to wide_string Pin
nolemor18-Dec-15 21:39
nolemor18-Dec-15 21:39 
Hi,

I just wanted to point out that this code

Rust
fn to_wstring(str : &str) -> *const u16 {

    let v : Vec<u16> =
            OsStr::new(str).encode_wide(). chain(Some(0).into_iter()).collect();
    v.as_ptr()
}


Is incorrect because v will be deallocated and the pointer that is returned here actually points to freed memory.

According to the documentation here https://doc.rust-lang.org/std/vec/struct.Vec.html#method.as_ptr[^]

"The caller must ensure that the slice outlives the pointer this function returns, or else it will end up pointing to garbage."

So the correct way here is to return v and then use .as_ptr() when using the data to ensure that data is still alive.
AnswerRe: Bug in convert to wide_string Pin
Farhad Reza19-Dec-15 2:00
Farhad Reza19-Dec-15 2:00 
GeneralRe: Bug in convert to wide_string Pin
outroot15-Jun-16 14:23
outroot15-Jun-16 14:23 
GeneralRe: Bug in convert to wide_string Pin
Farhad Reza14-Dec-16 6:42
Farhad Reza14-Dec-16 6:42 
SuggestionPlease don't stop writting Pin
Member 11497653-Dec-15 8:51
Member 11497653-Dec-15 8:51 
GeneralRe: Please don't stop writting Pin
Farhad Reza4-Dec-15 21:24
Farhad Reza4-Dec-15 21:24 
Questionconsole Pin
neko651-Dec-15 1:08
neko651-Dec-15 1:08 
AnswerRe: console Pin
Farhad Reza1-Dec-15 1:25
Farhad Reza1-Dec-15 1:25 
GeneralRe: console Pin
neko651-Dec-15 2:25
neko651-Dec-15 2:25 
GeneralRe: console Pin
Farhad Reza1-Dec-15 2:43
Farhad Reza1-Dec-15 2:43 
SuggestionRe: console Pin
Stephan Sokolow15-Dec-16 8:27
professionalStephan Sokolow15-Dec-16 8:27 
GeneralRe: console Pin
Farhad Reza15-Dec-16 9:55
Farhad Reza15-Dec-16 9:55 
GeneralRe: console Pin
Rob Grainger2-Feb-17 11:44
Rob Grainger2-Feb-17 11:44 
AnswerRe: console Pin
Evgeni Nabokov22-Feb-20 9:14
Evgeni Nabokov22-Feb-20 9:14 
QuestionThanks! Pin
RossCanning27-Nov-15 10:27
RossCanning27-Nov-15 10:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.