Assuming you want to build a Windows application without a console, this is one way to do it:
@[Link(ldflags: "/ENTRY:wWinMainCRTStartup")] # overrides previous `/ENTRY` setting
@[Link(ldflags: "/SUBSYSTEM:WINDOWS")]
lib LibCrystalMain
end
lib LibC
fun CommandLineToArgvW(lpCmdLine : LPWSTR, pNumArgs : Int*) : LPWSTR*
fun LocalFree(hMem : Void*) : Void*
end
@[CallConvention("X86_StdCall")]
fun wWinMain(hInstance : Void*, hPrevInstance : Void*, pCmdLine : UInt16*, nCmdShow : Int32) : Int32
argv = LibC.CommandLineToArgvW(pCmdLine, out argc)
status = wmain(argc, argv)
LibC.LocalFree(argv)
status
end
module Crystal::System::FileDescriptor
def self.from_stdio(fd)
console_handle = false
handle = LibC._get_osfhandle(fd)
if handle != -1 && handle != -2 # standard streams are unavailable
handle = LibC::HANDLE.new(handle)
old_mode = uninitialized LibC::DWORD
if LibC.GetConsoleMode(handle, pointerof(old_mode)) != 0
console_handle = true
if fd == 1 || fd == 2 # STDOUT or STDERR
if LibC.SetConsoleMode(handle, old_mode | LibC::ENABLE_VIRTUAL_TERMINAL_PROCESSING) != 0
at_exit { LibC.SetConsoleMode(handle, old_mode) }
end
end
end
end
# don't use `#system_info` to determine whether the descriptor should block,
# as that raises because `fd` is invalid (non-blocking IO isn't supported yet anyway)
io = IO::FileDescriptor.new(fd, blocking: true)
if console_handle
io.sync = true
else
io.flush_on_newline = true
end
io
end
end
Note that the entry point for the linker is not wmain
nor wWinMain
, but rather wWinMainCRTStartup
, which calls wWinMain
eventually. There is no need to remove the existing main
or wmain
, they are simply not called directly by the real entry point. This real entry point never accepts arguments, so there is no general mechanism to pass the parameters from a user entry point like wWinMain
to Crystal.main
. In this case all the parameters can be retrieved anywhere as below:
lib LibC
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2
fun GetCommandLineW : LPWSTR
fun GetStartupInfoW(lpStartupInfo : STARTUPINFOW*)
end
hInstance = begin
result = LibC.GetModuleHandleExW(LibC::GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, nil, out hmodule)
result != 0 ? hmodule : Pointer(Void).null
end
hPrevInstance = Pointer(Void).null
pCmdLine = LibC.GetCommandLineW
nCmdShow = begin
LibC.GetStartupInfoW(out info)
info.wShowWindow
end
For GCC / Clang on Linux the entry point is specified similarly:
@[Link(ldflags: "--entry=foo")]
lib LibCrystalMain
end
fun foo
LibC.printf("abc\n")
LibC._exit(0)
end
For macOS apparently you need an extra link flag.