Home > Mobile >  Why a Windows application in Rust and winapi doesn't redraw text on WM_PAINT messages?
Why a Windows application in Rust and winapi doesn't redraw text on WM_PAINT messages?

Time:12-27

I have a Windows application written in Rust and winapi that opens a window and writes text in the client area. The window procedure is as follows:

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_PAINT => {
            println!( "WM_PAINT {}", text.len() );
            let mut ps: PAINTSTRUCT = std::mem::zeroed();
            let t: String = text.to_string();
            let hdc: HDC;
            hdc = BeginPaint(hwnd, &mut ps);
            let mut rec: RECT = RECT {top: 0, bottom: 0, left: 0, right: 0};
            GetClientRect(hwnd, &mut rec);
            rec.top  = 4;
            rec.left  = 6;
            rec.bottom -= 4;
            rec.right -= 6;
            let txt = to_wstring( &t );
            DrawTextW(hdc, txt.as_ptr(), txt.len().try_into().unwrap(), &mut rec, DT_TOP|DT_LEFT);
            EndPaint(hwnd, &ps);
            ReleaseDC(hwnd, hdc);
        }
        _ => return DefWindowProcW(hwnd, msg, wparam, lparam),
    }
    return 0;
}

The text in the client area is drawn when the window starts. Then I continuously change the text and send WM_PAINT messages to the window procedure. The messages are received; I can see it in the println output. However, the new text is not redrawn. The new text is drawn only when I resize the window; it is not drawn when I move it on the screen, even though it causes a train of WM_PAINT messages.

I've also tried ExtTextOutW and TextOutW instead of DrawTextW, but it doesn't change anything except that these two functions cannot handle multi-line texts.

Since the resizing solves the problem, I've also tried to modify the drawing rectangle rec at each WM_PAINT message, but it doesn't help.

What am I missing?

EDIT: Below I include the whole program, including the dependencies, to make reproducing the problem possible.

//#![cfg(windows)]
//#![windows_subsystem = "windows"]
//
//[dependencies]
//winapi = { version = "0.3.9", features = ["wingdi", "winuser", "libloaderapi", "combaseapi", "objbase", "shobjidl", "winerror"] }
//lazy_static = "1.4.0"

use std::thread;
use std::time;
use std::error::Error;
use std::ptr::null_mut;
use winapi::shared::minwindef::*;
use winapi::shared::windef::*;
use winapi::um::libloaderapi::{GetModuleHandleW};
use winapi::um::winuser::*;
use lazy_static::{lazy_static};

// Get a win32 lpstr from a &str, converting u8 to u16 and appending '\0'
fn to_wstring(value: &str) -> Vec<u16> {
    use std::os::windows::ffi::OsStrExt;
    std::ffi::OsStr::new(value).encode_wide()
    .chain(std::iter::once(0)).collect()
}

// Window procedure function to handle events
//pub unsafe extern "system" fn window_proc(
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_PAINT => {
            println!( "WM_PAINT {}", TEXT.len() );
            let mut ps: PAINTSTRUCT = std::mem::zeroed();
            let t: String = TEXT.to_string();
            let hdc: HDC;
            hdc = BeginPaint(hwnd, &mut ps);
            let mut rec: RECT = RECT {top: 0, bottom: 0, left: 0, right: 0};
            GetClientRect(hwnd, &mut rec);
            rec.top  = 4;
            rec.left  = 6;
            rec.bottom -= 4;
            rec.right -= 6;
            let txt = to_wstring( &t );
            DrawTextW(hdc, txt.as_ptr(), txt.len().try_into().unwrap(), &mut rec, DT_TOP|DT_LEFT);
            EndPaint(hwnd, &ps);
            ReleaseDC(hwnd, hdc);
        }
        _ => 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 = to_wstring(name);
    let title = to_wstring(title);

    unsafe {
        // Get handle to the file used to create the calling process
        let hinstance = GetModuleHandleW(null_mut());

        // Create and register window class
        let wnd_class = WNDCLASSEXW {
            cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
            style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
            lpfnWndProc: Some(window_proc),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: hinstance, // Handle to the instance that contains the window procedure for the class
            hIcon: LoadIconW(null_mut(), IDI_APPLICATION),
            hCursor: LoadCursorW(null_mut(), IDC_ARROW),
            hbrBackground: COLOR_WINDOWFRAME as HBRUSH,
            lpszMenuName: null_mut(),
            lpszClassName: name.as_ptr(),
            hIconSm: LoadIconW(null_mut(), IDI_APPLICATION),
        };

        // Register window class
        if RegisterClassExW(&wnd_class) == 0 {
            MessageBoxW(
                null_mut(),
                to_wstring("Window Registration Failed!").as_ptr(),
                to_wstring("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
            710,                                // Int x
            580,                                // Int y
            700,                                // Int nWidth
            400,                                // Int nHeight
            null_mut(),                         // hWndParent
            null_mut(),                         // hMenu
            hinstance,                          // hInstance
            null_mut(),                         // lpParam
        );

        if handle.is_null() {
            MessageBoxW(
                null_mut(),
                to_wstring("Window Creation Failed!").as_ptr(),
                to_wstring("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() {
    unsafe { 
        TEXT = T_CONST.clone(); 
    }
    let hwnd = create_main_window("Main Window", "Main Window")
        .expect("Window creation failed!");
    unsafe {
        ShowWindow(hwnd, SW_SHOW);
        UpdateWindow(hwnd);
    }
    thread::spawn( || {
        loop {
            thread::sleep(time::Duration::from_millis(2000));
            unsafe {
                let t = TEXT.clone();
                TEXT = t   &T_CONST.clone();
                PostMessageW (HWND_BROADCAST as HWND, WM_PAINT, 0 as WPARAM, 0 as LPARAM);
            }
        }
    });
    run_message_loop(hwnd);
}

static mut TEXT: String = String::new();

lazy_static! {
static ref T_CONST: String = "This is the first line of the text
and this is the third line
and now comes the last line
".to_string();
}

CodePudding user response:

Problems I found:

  • Don't call ReleaseDC after EndPaint, that's already part of EndPaint.
  • Only regions that weren't rendered yet get updated. You can see that if you drag the window out of the screen and back in, then the regions that were previously off-screen get updated. You can manually invalidate regions via calling InvalidateRect or RedrawWindow. In your case, you probably want to call RedrawWindow instead of sending a WM_PAINT message.
use lazy_static::lazy_static;
use std::error::Error;
use std::ptr::{null, null_mut};
use std::thread;
use std::time;
use winapi::shared::minwindef::*;
use winapi::shared::windef::*;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::*;

// Get a win32 lpstr from a &str, converting u8 to u16 and appending '\0'
fn to_wstring(value: &str) -> Vec<u16> {
    use std::os::windows::ffi::OsStrExt;
    std::ffi::OsStr::new(value)
        .encode_wide()
        .chain(std::iter::once(0))
        .collect()
}

// Window procedure function to handle events
//pub unsafe extern "system" fn window_proc(
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_PAINT => {
            println!("WM_PAINT {}", TEXT.len());
            let mut ps: PAINTSTRUCT = std::mem::zeroed();
            let t: String = TEXT.to_string();
            let hdc: HDC;
            hdc = BeginPaint(hwnd, &mut ps);
            let mut rec: RECT = RECT {
                top: 0,
                bottom: 0,
                left: 0,
                right: 0,
            };
            GetClientRect(hwnd, &mut rec);
            rec.top  = 4;
            rec.left  = 6;
            rec.bottom -= 4;
            rec.right -= 6;
            let txt = to_wstring(&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 = to_wstring(name);
    let title = to_wstring(title);

    unsafe {
        // Get handle to the file used to create the calling process
        let hinstance = GetModuleHandleW(null_mut());

        // Create and register window class
        let wnd_class = WNDCLASSEXW {
            cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
            style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
            lpfnWndProc: Some(window_proc),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: hinstance, // Handle to the instance that contains the window procedure for the class
            hIcon: LoadIconW(null_mut(), IDI_APPLICATION),
            hCursor: LoadCursorW(null_mut(), IDC_ARROW),
            hbrBackground: COLOR_WINDOWFRAME as HBRUSH,
            lpszMenuName: null_mut(),
            lpszClassName: name.as_ptr(),
            hIconSm: LoadIconW(null_mut(), IDI_APPLICATION),
        };

        // Register window class
        if RegisterClassExW(&wnd_class) == 0 {
            MessageBoxW(
                null_mut(),
                to_wstring("Window Registration Failed!").as_ptr(),
                to_wstring("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
            710,                              // Int x
            580,                              // Int y
            700,                              // Int nWidth
            400,                              // Int nHeight
            null_mut(),                       // hWndParent
            null_mut(),                       // hMenu
            hinstance,                        // hInstance
            null_mut(),                       // lpParam
        );

        if handle.is_null() {
            MessageBoxW(
                null_mut(),
                to_wstring("Window Creation Failed!").as_ptr(),
                to_wstring("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() {
    unsafe {
        TEXT = T_CONST.clone();
    }
    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 || loop {
        thread::sleep(time::Duration::from_millis(2000));
        unsafe {
            let t = TEXT.clone();
            TEXT = t   &T_CONST.clone();
            RedrawWindow(
                hwnd2 as HWND,
                null(),
                null_mut(),
                RDW_INVALIDATE | RDW_ERASE,
            );
        }
    });
    run_message_loop(hwnd);
}

static mut TEXT: String = String::new();

lazy_static! {
    static ref T_CONST: String = "This is the first line of the text
and this is the third line
and now comes the last line
"
    .to_string();
}

There are a couple more things I would like to remark:

  • Global static mut String is inherently unsafe and incompatible with multiple threads. In fact, the way you implemented it here is undefined behaviour, because the WM_PAINT handler could run at the same time as the string gets modified. Use static Mutex<String> instead.
  • lazy_static! { static ref T_CONST: String } is quite pointless; there is a mechanism specifically meant for const strings, it's called const &str.
  • &T_CONST.clone() is pointless, &T_CONST (or T_CONST.as_str() if it's wrapped in a lazy_static) does exactly the same; or after refactoring it to a const &str, simply T_CONST.
  • HWND_BROADCAST as HWND is pointless, HWND_BROADCAST is already HWND.
  • Most of the WinAPI functions return BOOL for error handling, which you completely ignore. I won't fix that, though, because it's more work than I'm willed to put into this :)

With most of those fixed, this is how the code would look like:

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::*;

// Get a win32 lpstr from a &str, converting u8 to u16 and appending '\0'
fn to_wstring(value: &str) -> Vec<u16> {
    use std::os::windows::ffi::OsStrExt;
    std::ffi::OsStr::new(value)
        .encode_wide()
        .chain(std::iter::once(0))
        .collect()
}

// Window procedure function to handle events
//pub unsafe extern "system" fn window_proc(
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_PAINT => {
            let t = TEXT.lock().unwrap().clone();
            println!("WM_PAINT {}", t.len());
            let mut ps: PAINTSTRUCT = std::mem::zeroed();
            let hdc: HDC;
            hdc = BeginPaint(hwnd, &mut ps);
            let mut rec: RECT = RECT {
                top: 0,
                bottom: 0,
                left: 0,
                right: 0,
            };
            GetClientRect(hwnd, &mut rec);
            rec.top  = 4;
            rec.left  = 6;
            rec.bottom -= 4;
            rec.right -= 6;
            let txt = to_wstring(&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 = to_wstring(name);
    let title = to_wstring(title);

    unsafe {
        // Get handle to the file used to create the calling process
        let hinstance = GetModuleHandleW(null_mut());

        // Create and register window class
        let wnd_class = WNDCLASSEXW {
            cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
            style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
            lpfnWndProc: Some(window_proc),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: hinstance, // Handle to the instance that contains the window procedure for the class
            hIcon: LoadIconW(null_mut(), IDI_APPLICATION),
            hCursor: LoadCursorW(null_mut(), IDC_ARROW),
            hbrBackground: COLOR_WINDOWFRAME as HBRUSH,
            lpszMenuName: null_mut(),
            lpszClassName: name.as_ptr(),
            hIconSm: LoadIconW(null_mut(), IDI_APPLICATION),
        };

        // Register window class
        if RegisterClassExW(&wnd_class) == 0 {
            MessageBoxW(
                null_mut(),
                to_wstring("Window Registration Failed!").as_ptr(),
                to_wstring("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
            710,                              // Int x
            580,                              // Int y
            700,                              // Int nWidth
            400,                              // Int nHeight
            null_mut(),                       // hWndParent
            null_mut(),                       // hMenu
            hinstance,                        // hInstance
            null_mut(),                       // lpParam
        );

        if handle.is_null() {
            MessageBoxW(
                null_mut(),
                to_wstring("Window Creation Failed!").as_ptr(),
                to_wstring("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() = T_CONST.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 || loop {
        thread::sleep(time::Duration::from_millis(2000));
        *TEXT.lock().unwrap()  = T_CONST;
        unsafe {
            RedrawWindow(
                hwnd2 as HWND,
                null(),
                null_mut(),
                RDW_INVALIDATE | RDW_ERASE,
            );
        }
    });
    run_message_loop(hwnd);
}

static TEXT: Mutex<String> = Mutex::new(String::new());

const T_CONST: &str = "This is the first line of the text
and this is the third line
and now comes the last line
";

Alternative: Don't use a thread

The best practice for working with UI is to not use threads; or if you do, only use UI-related calls from the main thread.

Here is a version with a timer that achieves the same thing:

use std::error::Error;
use std::ptr::{null, null_mut};
use std::sync::Mutex;
use winapi::shared::minwindef::*;
use winapi::shared::windef::*;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::*;

// Get a win32 lpstr from a &str, converting u8 to u16 and appending '\0'
fn to_wstring(value: &str) -> Vec<u16> {
    use std::os::windows::ffi::OsStrExt;
    std::ffi::OsStr::new(value)
        .encode_wide()
        .chain(std::iter::once(0))
        .collect()
}

const IDT_TIMER1: usize = 1;

// Window procedure function to handle events
//pub unsafe extern "system" fn window_proc(
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_TIMER => match wparam {
            IDT_TIMER1 => {
                *TEXT.lock().unwrap()  = T_CONST;
                RedrawWindow(hwnd, null(), null_mut(), RDW_INVALIDATE | RDW_ERASE);
            }
            _ => (),
        },
        WM_PAINT => {
            let t = TEXT.lock().unwrap().clone();
            println!("WM_PAINT {}", t.len());
            let mut ps: PAINTSTRUCT = std::mem::zeroed();
            let hdc: HDC;
            hdc = BeginPaint(hwnd, &mut ps);
            let mut rec: RECT = RECT {
                top: 0,
                bottom: 0,
                left: 0,
                right: 0,
            };
            GetClientRect(hwnd, &mut rec);
            rec.top  = 4;
            rec.left  = 6;
            rec.bottom -= 4;
            rec.right -= 6;
            let txt = to_wstring(&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 = to_wstring(name);
    let title = to_wstring(title);

    unsafe {
        // Get handle to the file used to create the calling process
        let hinstance = GetModuleHandleW(null_mut());

        // Create and register window class
        let wnd_class = WNDCLASSEXW {
            cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
            style: CS_OWNDC | CS_HREDRAW | CS_VREDRAW,
            lpfnWndProc: Some(window_proc),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hInstance: hinstance, // Handle to the instance that contains the window procedure for the class
            hIcon: LoadIconW(null_mut(), IDI_APPLICATION),
            hCursor: LoadCursorW(null_mut(), IDC_ARROW),
            hbrBackground: COLOR_WINDOWFRAME as HBRUSH,
            lpszMenuName: null_mut(),
            lpszClassName: name.as_ptr(),
            hIconSm: LoadIconW(null_mut(), IDI_APPLICATION),
        };

        // Register window class
        if RegisterClassExW(&wnd_class) == 0 {
            MessageBoxW(
                null_mut(),
                to_wstring("Window Registration Failed!").as_ptr(),
                to_wstring("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
            710,                              // Int x
            580,                              // Int y
            700,                              // Int nWidth
            400,                              // Int nHeight
            null_mut(),                       // hWndParent
            null_mut(),                       // hMenu
            hinstance,                        // hInstance
            null_mut(),                       // lpParam
        );

        if handle.is_null() {
            MessageBoxW(
                null_mut(),
                to_wstring("Window Creation Failed!").as_ptr(),
                to_wstring("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() = T_CONST.to_string();

    let hwnd = create_main_window("Main Window", "Main Window").expect("Window creation failed!");
    unsafe {
        ShowWindow(hwnd, SW_SHOW);
        UpdateWindow(hwnd);
        SetTimer(hwnd, IDT_TIMER1, 2000, None);
    }

    run_message_loop(hwnd);
}

static TEXT: Mutex<String> = Mutex::new(String::new());

const T_CONST: &str = "This is the first line of the text
and this is the third line
and now comes the last line
";
  • Related