How to make an opaque struct static

I'm developing a Windows program that repeatedly calls popup(). Every time the function is called, some operations defining the properties of the popup window are repeated. The code could be made more efficient if the operations that have the same effect every time were extracted and executed only once. To make it possible, some variables must be maintained between the calls to popup(). The code below exemplifies the problem.

// [dependencies]
// winapi = { version = "0.3.9", features = ["wingdi", "winuser"] }

use winapi::um::winuser::*;
use winapi::shared::windef::*;

static mut HANDLE: HWND = 0 as HWND;
static mut HDEV: HDC = 0 as HDC;

pub fn popup( text: &str ) {
    unsafe {
        // many code lines
        let mut ps: PAINTSTRUCT = core::mem::zeroed();
        HDEV = BeginPaint(HANDLE, &mut ps);
        // many code lines
    }
}

In the above code, the variable ps: PAINTSTRUCT must be made static. The problem is complicated because all involved winapi functions are unsafe and the struct PAINTSTRUCT has unknown contents. I tried different approaches but did not succeed. Help is appreciated.

If hdc is dependent on the handle variable, how could it be safely cached across multiple calls to popup?

1 Like

Due to a comment from lachlansneff I've modified the problem statement, see above.
All winapi handles with type names starting with "H" are pointers and can easily be made static. In fact, these pointers may be assigned once and never change.
I'm having trouble making the struct ps: PAINTSTRUCT static. This struct collects information about the text and background colors and font, which require several calls to winapi functions but never change.

Weird that you need a &mut if it doesn't change.

You could use a OnceCell and Mutex.

If you want to fetch a handle a single time, the standard way to do so would be to put it behind a LazyLock or perhaps a LazyCell. The stable crate versions of which are sync::Lazy and unsync::Lazy.

You could perhaps do very marginally better with AtomicPtr (since you can use INVALID_HANDLE as a sentinel rather than a separate initialization flag), but I'm not aware of any crate which wraps such up in a nice API.

Also,

  • Check if getting a fresh handle actually costs anything worth optimizing for. If you're just doing caching because "obviously I should" but it turns out it's not actually beneficial, don't! It'll simplify things significantly.
  • Consider if you can use some PopupHandler struct which stores the state and has an associated method rather than using static caching within a free function. Even if the handler ends up in a lazily initialized static anyway, having the state object visible is generally considered better design than presenting as a stateless free function.
1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.