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.