How to reimplement Win32 MAKEINTRESOURCE macro in Zig

My initial thought would be something like this:

const std = @import("std");
const windows = std.os.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:

cursor=*os.windows.HCURSOR__opaque_2421@10007
icon=*os.windows.HICON__opaque_2812@d40c63

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.

2 Likes