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.