Hello World MesssageBox example in Rust

In this post, I’ll show you how to use the winapi crate to display a simple MessageBox on Windows. I’ll also describe the process I used to come up with the code. The end result should look something like this:

Screenshot of messagebox

To get started, let’s create a new project using Cargo:

cargo new --bin hello_world

This will create a new folder called hello_world in your current working directory. The --bin argument tells Cargo that compiling this project will result in an executable (.exe). We now need to figure out what API to call to get a MessageBox. I googled “Windows MessageBox api” and found the MessageBox() function on MSDN. The “Requirements” table states that this function exists in User32.dll, so we’ll need to use the corresponding crate user32-sys. Inside the hello_world folder, you will find a file called Cargo.toml. Open that file in your favorite text editor. We’re going to add dependencies on the winapi and user32-sys crates. Find the line that reads [dependencies] and add the following below it:

winapi = "0.2.7"
user32-sys = "0.2.0"

This tells Cargo that our project depends on the winapi and user32-sys crates. When we build our project, Cargo will automatically download those crates and build them for us.

Now that we have finished setting up the project metadata, we can start writing the code. Open the file src/main.rs in your text editor. Currently it should look like this:

fn main() {
    println!("Hello, world!");
}

The first thing we need to do is tell the Rust compiler that we want to use the winapi and user32-sys crates in this file. To do that, add the following lines to the top of the file:

extern crate user32;
extern crate winapi;

We now need to figure out what the function signature should look like in Rust. Here’s the signature in C:

int WINAPI MessageBox(
  _In_opt_ HWND    hWnd,
  _In_opt_ LPCTSTR lpText,
  _In_opt_ LPCTSTR lpCaption,
  _In_     UINT    uType
);

If you aren’t familiar with native Windows programming, this probably looks very strange to you. For simplicity, you can think of WINAPI, _In_opt_, and _In_ as attributes describing the things that follow their usage. Ignoring those, we get this:

int MessageBox(
  HWND    hWnd,
  LPCTSTR lpText,
  LPCTSTR lpCaption,
  UINT    uType
);

Well that looks a lot more like C. HWND is a handle (pointer) to a window, LPCTSTR is a const string, and UINT is, of course, an unsigned integer. You may be asking, “What kind of string is LPCTSTR? ASCII or Unicode?” and the answer is slightly complicated. In normal Windows C/C++ programming, there is a preprocessor symbol called UNICODE which determines whether a LPCTSTR is a Unicode string or an ASCII string. This preprocessor symbol is also responsible for determining which MessageBox function gets called (You can read more about this here). Looking further down on the MSDN page, we see that the Unicode and ASCII names for this function are MessageBoxW and MessageBoxA respectively. This is important because we will need to call the correct version of the function ourselves. In this example, we’ll call the ASCII version (MessageBoxA)1.

We know from reading the documentation that the first parameter is a pointer to the MessageBox’s parent window and that we can simply pass NULL since the parameter is optional (_In_opt_). We also know that the next two parameters are ASCII strings for the text and title of the MessageBox. The final parameter is a bit flag of display options which control which buttons show up and what icon to use. The documentation contains a list of constants we can choose but for this example, we’ll use MB_OK to get an “Ok” button and MB_ICONINFORMATION to set the icon to the informational exclamation point. Since this is a bit flag, we’ll combine those values with the bitwise OR operator (|) to get a single value. In pseudo-code, this looks like:

MessageBoxA(
    NULL,
    "Hello, world!",
    "MessageBox Example",
    MB_OK | MB_ICONINFORMATION
);

At this point, we need to figure a few things out in order to write our code in Rust:

  1. Where are MessageBoxA, MB_OK, and MB_ICONINFORMATION declared? We need to know this so we can import them.
  2. What is the equivalent of NULL? Idiomatically this would be None but we actually need a raw pointer.
  3. How do we pass a Rust string into a C function?

To find the answer to #1, I opened the documentation for winapi and used the search box at the top to look for those names. I found them in user32 (for MessageBoxA) and winapi::winuser (for MB_OK and MB_ICONINFORMATION). Let’s add use statements for those to our main.rs file right after the extern crate lines at the top:

use user32::MessageBoxA;
use winapi::winuser::{MB_OK, MB_ICONINFORMATION};

This brings those names into scope and allows us to use them without prefixes in the rest of this file.

On to #2. A quick Google search shows that we should use the std::ptr module to get a NULL pointer. Since the winapi crate defines MessageBoxA as taking a HWND value which is a mut pointer, we’ll use the std::ptr::null_mut() function.

Finally, #3. The standard library’s ffi module already has a CString type for precisely this purpose. We simply need to create a new value of this type and use the .as_ptr() function to get a raw pointer to pass to the C API.

Putting this all together, we’ll add a use statement for CString and an unsafe block to call MessageBoxA because it is marked unsafe resulting in:

extern crate user32;
extern crate winapi;

use std::ffi::CString;
use user32::MessageBoxA;
use winapi::winuser::{MB_OK, MB_ICONINFORMATION};

fn main() {
    let lp_text = CString::new("Hello, world!").unwrap();
    let lp_caption = CString::new("MessageBox Example").unwrap();

    unsafe {
        MessageBoxA(
            std::ptr::null_mut(),
            lp_text.as_ptr(),
            lp_caption.as_ptr(),
            MB_OK | MB_ICONINFORMATION
        );
    }
}

You can find all of the code from this post in this repository. Thanks for reading!


  1. Read the follow up post to see how to use the Unicode version of this API. [return]