How to reimplement Win32 MAKEINTRESOURCE macro in Zig

Hi! I am currently making a simple windowing library, and am working on the win32 part of it now. I have run into an issue that I have struggles with overcoming.
I am writing my own user32.zig implementation, and with that I am trying to convert the original MAKEINTRESOURCE macro from MINGW winuser.h, which you can see here:


My attempt to make something similar in Zig looks like this:
Note: LPCSTR is equivalent to [*:0]const u8 and LPCWSTR is equivalent to [*:0]const u16.

pub fn makeIntResourceA(i: comptime_int) LPCSTR {
    return @as(windows.LPCSTR, @ptrFromInt(@as(usize, @intCast(i))));

pub fn makeIntResourceW(i: comptime_int) LPCWSTR {
    const address: usize = @intCast(i);
    const ptr: LPCWSTR = @ptrFromInt(address);
    return ptr;

This works fine for the makeIntResourceA function, since the LPCSTR just has a 1 byte alignment, the issues is when I am trying to use the makeIntResourceW function.
The makeIntResourceW works fine with values such as IDC_ARROW = 32512 because that has a 2 byte alignment, but with icons such as IDC_BEAM=32513 I get the following error, understandably.

└─ install opengl-example
   └─ zig build-exe opengl-example Debug native 1 errors
src\user32.zig:1480:38: error: pointer type '[*:0]const u16' requires aligned address
    const ptr: LPCWSTR = @ptrFromInt(address);

This code is then later used from my window implementation and is passed to the win32 LoadCursorW function. My question then becomes how can I even do something like this in Zig, or do I have to write my own winuser.h file that only includes the constants and the macro and use that instead?


Hello @Skarsh, welcome to the forum.

While LPCWSTR declaration remains the same, zig type system is not going to let you have a misaligned value.
You can use MAKEINTRESOURCEA with LoadCursorA, and use LoadCursorW when you have a string name.

Please note that there are windows bindings automatically generated from windows metadata

There was a similar issue with HWND_TOPMOST passed as a pointer with value -1:


Thanks for the reply @dimdin!

Yes, that’s a good point, and I think it should work fine for my usecase at least.

1 Like

My initial thought would be something like this:

const std = @import("std");
const windows =;

// LPCWSTR but with align(1)
const ResourceNamePtrW = [*:0]align(1) const windows.WCHAR;

extern "user32" fn LoadIconW(
    hInstance: ?windows.HINSTANCE,
    lpIconName: ResourceNamePtrW,
) callconv(windows.WINAPI) ?windows.HICON;

extern "user32" fn LoadCursorW(
    hInstance: ?windows.HINSTANCE,
    lpCursorName: ResourceNamePtrW,
) callconv(windows.WINAPI) ?windows.HCURSOR;

// Resource ordinals are limited to u16
fn makeIntResourceW(id: u16) ResourceNamePtrW {
    return @ptrFromInt(@as(usize, id));

const IDC_BEAM = 32513;
const beam_resource_name = makeIntResource(IDC_BEAM);

pub fn main() !void {
    // Load the beam cursor using the integer ID
    const beam_cursor = LoadCursorW(null, beam_resource_name);
    std.debug.print("cursor={*}\n", .{beam_cursor});

    // Load an icon from the .exe just to show that string names also work
    const icon_name = std.unicode.utf8ToUtf16LeStringLiteral("foo");
    const instance: windows.HINSTANCE = @ptrCast(windows.kernel32.GetModuleHandleW(null));
    const icon = LoadIconW(instance, icon_name);
    std.debug.print("icon={*}\n", .{icon});

with an .rc file like:

FOO ICON "test.ico"

Built via (on master version of Zig for .rc compilation support):

zig build-exe test.zig test.rc

and running it results in:


Another option is to make ResourceNamePtr an *opaque {} which means that you’d have to @ptrCast string names when calling the Load functions, but would make it so that you wouldn’t need separate makeIntResourceA/makeIntResourceW functions.