Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Successfully @cImport("Windows.h") #3453

Closed
JesseRMeyer opened this issue Oct 14, 2019 · 28 comments
Closed

Successfully @cImport("Windows.h") #3453

JesseRMeyer opened this issue Oct 14, 2019 · 28 comments
Labels
contributor friendly This issue is limited in scope and/or knowledge of Zig internals. enhancement Solving this issue will likely involve adding new logic or components to the codebase. os-windows stage1 The process of building from source via WebAssembly and the C backend. translate-c C to Zig source translation feature (@cImport)
Milestone

Comments

@JesseRMeyer
Copy link

JesseRMeyer commented Oct 14, 2019

A substantial milestone for translate-c is the successful c-importing of Windows.h.

@andrewrk andrewrk added this to the 0.6.0 milestone Oct 14, 2019
@andrewrk andrewrk added bug Observed behavior contradicts documented or intended behavior stage1 The process of building from source via WebAssembly and the C backend. labels Oct 14, 2019
@JesseRMeyer
Copy link
Author

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.

@travisstaloch
Copy link
Contributor

did you try zig build-exe -lc test.zig ?

@JesseRMeyer
Copy link
Author

I have not, but why should that be required to include Windows.h?

@travisstaloch
Copy link
Contributor

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

@JesseRMeyer
Copy link
Author

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.

@JesseRMeyer JesseRMeyer changed the title @cImport() Compiler Bug: Hello World MessageBox assertion failed. @cImport("Windows.h") Compiler Bug: libc headers not available; compilation does not link against libc Jan 13, 2020
@travisstaloch
Copy link
Contributor

@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.

@JesseRMeyer
Copy link
Author

JesseRMeyer commented Jan 13, 2020

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.

@JesseRMeyer
Copy link
Author

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>

@JesseRMeyer JesseRMeyer changed the title @cImport("Windows.h") Compiler Bug: libc headers not available; compilation does not link against libc Successfully @cImport("Windows.h") Jan 13, 2020
@travisstaloch
Copy link
Contributor

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 ?

@JesseRMeyer
Copy link
Author

JesseRMeyer commented Jan 14, 2020

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?

@kavika13
Copy link
Contributor

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

@JesseRMeyer
Copy link
Author

JesseRMeyer commented Jan 14, 2020

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.

@JesseRMeyer
Copy link
Author

JesseRMeyer commented Jan 19, 2020

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'
 ■p
^

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
whex

So there seems to be two padding bytes that is confusing @import?

@daurnimator
Copy link
Contributor

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

@JesseRMeyer
Copy link
Author

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.

@Sobeston
Copy link
Contributor

Yes that'll be it - done it myself

@JesseRMeyer
Copy link
Author

JesseRMeyer commented Jan 20, 2020

That fixed it.

Also, after the usual win32 shenanigans, I have a window properly registered and displayed, all through Zig!

success

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.

@travisstaloch
Copy link
Contributor

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.

@ficion
Copy link

ficion commented Jan 24, 2020

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?

@LemonBoy
Copy link
Contributor

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.

@BarabasGitHub
Copy link
Contributor

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?

@andrewrk andrewrk added translate-c C to Zig source translation feature (@cImport) enhancement Solving this issue will likely involve adding new logic or components to the codebase. and removed bug Observed behavior contradicts documented or intended behavior labels Feb 18, 2020
@GoNZooo
Copy link

GoNZooo commented Apr 28, 2020

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.

@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Aug 13, 2020
@Sirius902
Copy link

Sirius902 commented Sep 9, 2020

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.

@qgcarver
Copy link

I'm now having a problem with the semicolon after GetCurrentFiber getting deleted for some reason, along with the associated comment.

@Sirius902
Copy link

Sirius902 commented Jan 14, 2021

Update: I've tried again after updating to master and the issue I mentioned earlier seems to be fixed after some recent commits!

@xackus
Copy link
Contributor

xackus commented Feb 5, 2021

When translating INVALID_HANDLE_VALUE translate-c messes up the order:

std\meta.zig:956:26: error: expected type '*c_void', found 'type'
    return @as(DestType, target);
                         ^
cimport.zig:49779:59: note: called from here
pub const INVALID_HANDLE_VALUE = (@import("std").meta.cast(HANDLE, LONG_PTR)) - 1;
                                                          ^

GENERIC_READ doesn't fit in a c_long:

cimport.zig:47185:38: error: integer value 2147483648 cannot be coerced to type 'c_long'
pub const GENERIC_READ = @as(c_long, 0x80000000);
                                     ^

@Vexu
Copy link
Member

Vexu commented Feb 25, 2021

There should now be no rendering issues so if someone on windows can confirm it then this issue should be closed and any remaining problems should be made into their own issues.

When translating INVALID_HANDLE_VALUE translate-c messes up the order:

This is #4071.

GENERIC_READ doesn't fit in a c_long

This is #4479.

@FireFox317
Copy link
Contributor

main.zig

const c = @cImport(@cInclude("windows.h"));

pub fn main() void {
    _ = c;
}

Compiles succesfully with zig build-exe main.zig -lc on Windows 10.

@Vexu Vexu closed this as completed Feb 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
contributor friendly This issue is limited in scope and/or knowledge of Zig internals. enhancement Solving this issue will likely involve adding new logic or components to the codebase. os-windows stage1 The process of building from source via WebAssembly and the C backend. translate-c C to Zig source translation feature (@cImport)
Projects
None yet
Development

No branches or pull requests