A substantial milestone for translate-c is the successful c-importing of Windows.h.
I've given this another go with 0.5.0+c4770e7aa.
const windows = @cImport({
@cDefine("WIN32_LEAN_AND_MEAN", "1");
@cInclude("Windows.h");
});
usingnamespace windows;
extern "user32" fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCSTSTR, uType: UINT) callconv(.Stdcall) c_int;
export fn WinMain(hInstance: HINSTANCE, hPrevInstace: HINSTANCE, lpCmdLine: PWSTR, nCmdShow: INT) callconv(.Stdcall) INT {
MessageBoxA(null, "Hello", "title", 0);
}
PS C:\dev\src> zig build-exe test.zig
.\test.zig:1:17: error: C import failed
const windows = @cImport({
^
.\test.zig:1:17: note: libc headers not available; compilation does not link against libc
const windows = @cImport({
^
.\zig-cache\o\X31yopM03uxmIKZiqh1rP0rnHeAJdDfiWqbdFMfmHxY6oIsokRfiAiUhWLfJ4T-0\cimport.h:2:10: note: 'Windows.h' file not found
#include <Windows.h>
^
.\test.zig:6:16: note: referenced here
usingnamespace windows;
^
C:\dev\Zig\lib\zig\std\builtin.zig:443:73: note: referenced here
pub const panic: PanicFn = if (@hasDecl(root, "panic")) root.panic else default_panic;
^
I'm not sure what libc headers have to do with finding and importing Windows.h.
did you try zig build-exe -lc test.zig ?
I have not, but why should that be required to include Windows.h?
I'm guessing windows.h uses some things from libc. I tried cIncluding a minimal .h file with no #includes and was able to build-exe without -lc
I'm sure Windows.h does use CRT stuff, but could you explain why that matters for _importing_?
Maybe I should re-name the title of this issue as I'm only really concerned about the Win32 ramifications of this problem. And unfortunately Windows.h is probably the most insane C header in existence.
@cImport from: https://ziglang.org/documentation/master/#cImport :
This function parses C code and imports the functions, types, variables, and compatible macro definitions into a new empty struct type, and then returns that type.
So when you use @cImport / @cInclude("Windows.h"), zig will parse Windows.h and everything it includes. One thing that may be informative is to run zig translate-c /path/to/Windows.h -lc > Windows.h.zig. This would produce the same thing as @cImport ing the file. Note that zig has lazy analysis so if there symbols that resulted in translation errors which aren't used, they will not be analyzed.
Thanks. I do know the general mechanism, although I don't see how that has to do with the linker / libc, especially if lazy analysis is involved. But this is all quite complex so that's why I ask.
I returned to this issue after seeing the flurry of translate-c work done recently. I'll see what translate-c does in hopes that illuminates the core of the issue after work.
Windows.h immediately includes winapifamily.h, which Zig translate cannot find (even though it's in the /share folder just above). Even tried running Zig translate inside the Developer Command Prompt for VS 2019.
C:\dev>zig translate-c "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\Windows.h" -ls > Windows.h.zig
C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\Windows.h:1:10: error: 'winapifamily.h' file not found
#include <winapifamily.h>
You'll need to -I..\share. Check the output of zig --help:
-I[dir] add directory to include search path
zig translate-c "C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\Windows.h" -ls -I..\share
also -ls ? did you mean -lc ?
Oops. Thanks for that. I'll see how far that gets me. The Windows.h is going to pull in files from who knows where in general. Is it expected that all Win32 dev's provide the full include list or is @cImport() eventually going to handle that itself?
Oops. Thanks for that. I'll see how far that gets me. The Windows.h is going to pull in files from who knows where in general. Is it expected that all Win32 dev's provide the full include list or is @cImport() eventually going to handle that itself?
I don't think the list of includes is actually that long. I think you just have to include the include paths of the windows SDK - the path where you found Windows.h.
Might want to try getting an example C program compiling from the command line using cl.exe (Microsoft/visual studio compiler), then port that. I'm about 80% sure you have to manually add the windows SDK when compiling from the command line with cl.exe, too
Might want to try getting an example C program compiling from the command line using cl.exe (Microsoft/visual studio compiler), then port that.
That's what I'm doing. =) But with Win32 dev, you plunge immediately into the deep end of the pool by basically requiring Windows.h right off the bat except for the most basic programs. A simple MessageBoxA call is easy to port over, but when you want a proper window responding to keyboard / mouse events, you have to dig into Win32 structures instead of just type aliases.
I'm about 80% sure you have to manually add the windows SDK when compiling from the command line with cl.exe
That's taken care of by executing cl.exe inside the Developer Command Prompt, but it's not necessary for Clang. Since Clang can do it without fuss, I would hope that Zig can.
Fortunately the -lc and -I flags got translate-c to output something seemingly useful, but I've hit another snag.
Consider that the first 30 some lines of Windows.zig look reasonable.
pub const va_list = [*c]u8;
pub extern fn __va_start([*c][*c]u8, ...) void;
pub const ptrdiff_t = c_longlong;
pub const __vcrt_bool = bool;
pub const wchar_t = c_ushort;
pub extern fn __security_init_cookie() void;
pub extern fn __security_check_cookie(_StackCookie: usize) void;
pub extern fn __report_gsfailure(_StackCookie: usize) noreturn;
pub extern var __security_cookie: usize;
pub const ExceptionContinueExecution = @enumToInt(enum__EXCEPTION_DISPOSITION.ExceptionContinueExecution);
pub const ExceptionContinueSearch = @enumToInt(enum__EXCEPTION_DISPOSITION.ExceptionContinueSearch);
pub const ExceptionNestedException = @enumToInt(enum__EXCEPTION_DISPOSITION.ExceptionNestedException);
pub const ExceptionCollidedUnwind = @enumToInt(enum__EXCEPTION_DISPOSITION.ExceptionCollidedUnwind);
pub const enum__EXCEPTION_DISPOSITION = extern enum {
ExceptionContinueExecution,
ExceptionContinueSearch,
ExceptionNestedException,
ExceptionCollidedUnwind,
};
pub const EXCEPTION_DISPOSITION = enum__EXCEPTION_DISPOSITION;
pub const struct__EXCEPTION_RECORD = extern struct {
ExceptionCode: DWORD,
ExceptionFlags: DWORD,
ExceptionRecord: [*c]struct__EXCEPTION_RECORD,
ExceptionAddress: PVOID,
NumberParameters: DWORD,
ExceptionInformation: [15]ULONG_PTR,
};
And my test program (using the full Windows.zig where WNDCLASS is defined):
usingnamespace @import("Windows.zig");
export fn WinMain(hInstance: HINSTANCE, hPrevInstace: HINSTANCE, lpCmdLine: PWSTR, nCmdShow: INT) callconv(.Stdcall) INT {
var testing = WNDCLASS{};
}
When I zig build-exe test.zig, the output is:
.\Windows.zig:1:1: error: invalid character: '\xff'
聽鈻爌
^
I think .\ means 'The current working directory' expressed as a relative path. If so, this is correct. I have Windows.zig next to test.zig in the same directory.
When Windows.zig is viewed as hex values, you can see
So there seems to be two padding bytes that is confusing @import?
Your file contents appear to be in utf16? How did you create that file?
Perhaps zig should emit a warning/note if file contents looks like a utf16 BOM
Ah, I bet that's it. I just piped the output of zig translate > Windows.zig, like that. I'll save as UTF-8 tomorrow morning to see if that fixes it.
Yes that'll be it - done it myself
That fixed it.
Also, after the usual win32 shenanigans, I have a window properly registered and displayed, all through Zig!
This doesn't close the issue though. There's quite a few declarations in Windows.zig (when created via the manner discussed in this thread) that are not properly handled. Not sure how to enumerate the issues that are useful for others that care about this, as they are very obvious to anyone who will attempt to register and display a basic window like I did here.
So I've just been selectively moving what bits and pieces I need over, which isn't much.
Nice job. For the unhandled declarations I would suggest starting with the first error you see. See if you can create a new issue with a minimal reproduction of c code which results in the mis-translation.
Just translated the header to Zig, and I get conversion mistakes like these:
pub const LCS_sRGB = 'sRGB';
These come from
typedef enum {
LCS_CALIBRATED_RGB = 0x00000000,
LCS_sRGB = 0x73524742,
LCS_WINDOWS_COLOR_SPACE = 0x57696E20
} LogicalColorSpace;
Arguably a very ugly way to encode a small string, and although it does correctly convert as if it were a kind of string, it should be written as ['s', 'R', 'G', 'B'], correct?
These come from
No, that comes from wingdi.h where it's defined as a macro
#define LCS_sRGB 'sRGB'
The macro-handling part in translate-c is na茂ve and assumes everything between two ' is a character literal.
Actually the multiple characters in a character literal is a Microsoft extension:
To create a value from a narrow multicharacter literal, the compiler converts the character or character sequence between single quotes into 8-bit values within a 32-bit integer. Multiple characters in the literal fill corresponding bytes as needed from high-order to low-order.
From: https://docs.microsoft.com/en-us/cpp/cpp/string-and-character-literals-cpp?view=vs-2019 "
So in a way it is a character literal. ;)
it should be written as
['s', 'R', 'G', 'B'], correct?
No, it should be an integer value. Maybe @bitCast(i32, [_]['s', 'R', 'G', 'B']) or @bitCast(i32, [_]['B', 'G', 'R', 's']) depending on the endianness?
For anyone who ended up in this issue because they were interested in using Win32, there is https://github.com/GoNZooo/zig-win32
I've had no massive issues creating Win32 apps with this, including registering window classes, handling events, etc.
Hello, I'm including libusb and vJoy to write a vJoy feeder on x86_64-windows-msvc. Both libusb and vJoy need to include windows.h so I can't get use a windows.h wrapper. Then when it gets translated, GetCurrentFiber is defined twice.
pub const GetCurrentFiber = @compileError("unable to translate function"); // C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\..\um\winnt.h:22637:1
// C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\..\um\winnt.h:22637:1
pub const NtCurrentTeb = @compileError("unable to translate function"); // C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\..\um\winnt.h:22627:1
// C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\..\um\winnt.h:22643:19: warning: TODO implement translation of CastKind BuiltinFnToFnPtr
pub const GetCurrentFiber = @compileError("unable to translate function"); // C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\..\um\winnt.h:22637:1
pub fn GetFiberData() callconv(.C) PVOID {
return @ptrCast([*c]PVOID, @alignCast(@alignOf(PVOID), GetCurrentFiber())).?.*;
}
This causes problems with zig build.
.\zig-cache\o\JdUprE-lTL_c-YYISTsev1ZOiL_ppA5aV3VREsDOl-YulgIH7xVaMkJxcdNDm6wz\cimport.zig:7490:5: error: redefinition of 'GetCurrentFiber'
pub const GetCurrentFiber = @compileError("unable to translate function"); // C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\..\um\winnt.h:22637:1
^
.\zig-cache\o\JdUprE-lTL_c-YYISTsev1ZOiL_ppA5aV3VREsDOl-YulgIH7xVaMkJxcdNDm6wz\cimport.zig:7486:5: note: previous definition is here
pub const GetCurrentFiber = @compileError("unable to translate function"); // C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\ucrt\..\um\winnt.h:22637:1
Any way to work around this?
EDIT: It seems this is the only remaining issue importing windows.h at least for me. If I use zig translate-c on windows.h and remove the second definition of GetCurrentFiber, the import works. But that's not going to work when I need to import more than one C library that includes windows.h.
I'm now having a problem with the semicolon after GetCurrentFiber getting deleted for some reason, along with the associated comment.
Most helpful comment
That fixed it.
Also, after the usual win32 shenanigans, I have a window properly registered and displayed, all through Zig!
This doesn't close the issue though. There's quite a few declarations in Windows.zig (when created via the manner discussed in this thread) that are not properly handled. Not sure how to enumerate the issues that are useful for others that care about this, as they are very obvious to anyone who will attempt to register and display a basic window like I did here.
So I've just been selectively moving what bits and pieces I need over, which isn't much.