now it just partially worked, at least error message is shown,now the focus is somewhat about windows api functions. the same window class runs normally in c,but fails in crystal.
lib LibC
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT = 2
fun GetCommandLineW : LPWSTR
fun GetStartupInfoW(lpStartupInfo : STARTUPINFOW*)
alias WPARAM = UInt32*
alias LPARAM = Void*
alias LRESULT = Void*
@[CallConvention("X86_StdCall")]
type WNDPROC = (Void*, UInt32, WPARAM, LPARAM) -> LRESULT
# type WNDPROC = ( WNDCLASSW*,UInt32,WPARAM,LPARAM ) -> LRESULT
alias LPCWSTR = UInt16*
struct WNDCLASSW
style : UInt32
lpfnWndProc : WNDPROC
cbClsExtra : Int32
cbWndExtra : Int32
hInstance : Void* # HINSTANCE
hIcon : Void* # HICON
hCursor : Void* # HCURSOR
hbrBackground : Void* # HBRUSH
lpszMenuName : LPCWSTR
lpszClassName : LPCWSTR
end
alias HWND = WNDCLASSW*
alias WNDCLASS = WNDCLASSW
alias ATOM = UInt16
type WINBOOL = Int32
struct POINT
x, y : Int32
end
struct MSG
hwnd : HWND
message : UInt32
wParam : WPARAM
lParam : LPARAM
time : Int32
pt : POINT
end
alias LPMSG = MSG*
@[CallConvention("X86_StdCall")]
fun RegisterClassW(window_class : WNDCLASS*) : ATOM
type HMENU = Void*
type HINSTANCE = Void*
type LPVOID = Void*
@[CallConvention("X86_StdCall")]
fun CreateWindowExW(
dwExStyle : DWORD,
lpClassName : LPCWSTR,
lpWindowName : LPCWSTR,
dwStyle : DWORD,
x : Int32, y : Int32, nWidth : Int32, nHeight : Int32,
hWndParent : HWND, hMenu : HMENU, hInstance : HINSTANCE, lpParam : LPVOID
) : HWND
@[CallConvention("X86_StdCall")]
fun GetMessageW(lpMsg : LPMSG, hWnd : HWND, wMsgFilterMin : UInt32, wMsgFilterMax : UInt32) : Int32
@[CallConvention("X86_StdCall")]
fun TranslateMessage(lpMsg : MSG*) : Int32
@[CallConvention("X86_StdCall")]
fun DispatchMessageW(lpMsg : MSG*) : Void*
@[CallConvention("X86_StdCall")]
fun ShowWindow(hWnd : HWND, nCmdShow : Int32) : Int32
@[CallConvention("X86_StdCall")]
struct RECT
left : Int32
top : Int32
right : Int32
bottom : Int32
end
type HDC = Void*
struct PAINTSTRUCT
hdc : HDC
fErase : WINBOOL
rcPaint : RECT
fRestore : WINBOOL
fIncUpdate : WINBOOL
rgbReserved : BYTE[32]
end
alias LPPAINTSTRUCT = PAINTSTRUCT*
@[CallConvention("X86_StdCall")]
fun BeginPaint(hWnd : HWND, lpPaint : LPPAINTSTRUCT) : HDC
type HBRUSH = Void*
@[CallConvention("X86_StdCall")]
fun FillRect(hDC : HDC, lprc : RECT*, hbr : HBRUSH) : Int32
@[CallConvention("X86_StdCall")]
fun EndPaint(hWnd : HWND, lpPaint : PAINTSTRUCT*) : WINBOOL
@[CallConvention("X86_StdCall")]
fun DefWindowProcW(hWnd : HWND, msg : UInt32, wParam : WPARAM, lParam : LPARAM) : LRESULT
@[CallConvention("X86_StdCall")]
fun PostQuitMessage(nExitCode : Int32) : Void
end
@[Link(ldflags: "/ENTRY:wWinMainCRTStartup")] # overrides previous `/ENTRY` setting
@[Link(ldflags: "/SUBSYSTEM:WINDOWS")]
lib LibCrystalMain
end
@[Link("gdi32")]
@[Link("kernel32")]
@[Link("User32")]
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)
return status
LibC.LocalFree(argv)
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
@[Link("User32")]
lib LibC
fun MessageBoxW(hWnd : Void*, lpText : LPWSTR, lpCaption : LPWSTR, uType : UInt) : Int
end
module Crystal
def self.exit(status : Int32, exception : Exception?) : Int32
status = Crystal::AtExitHandlers.run status, exception
if exception
LibC.MessageBoxW(nil, exception.inspect_with_backtrace.to_utf16, "Unhandled exception".to_utf16, 0x10)
end
status
end
end
hInstance = begin
result = LibC.GetModuleHandleExW(LibC::GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, nil, out hmodule)
result != 0 ? hmodule : Pointer(Void).null
end.not_nil!
hPrevInstance = Pointer(Void).null
pCmdLine = LibC.GetCommandLineW
nCmdShow = begin
LibC.GetStartupInfoW(out info)
info.wShowWindow
end
CW_USEDEFAULT = Int32::MIN
WS_OVERLAPPEDWINDOW = 13565952
CLASS_NAME = "Sample Window Class".to_utf16
WINDOW_TITLE = "Learn to Program Windows".to_utf16
make_log = ->(text : String) do
LibC.MessageBoxW(nil, text.to_utf16, "winapp log".to_utf16, 0x10)
end
make_log.call "begin to create window class"
CS_HREDRAW = 1
CS_VREDRAW = 2
wc = LibC::WNDCLASSW.new
wc.lpfnWndProc = ->windowProc(LibC::HWND, UInt32, LibC::WPARAM, LibC::LPARAM).pointer.unsafe_as(LibC::WNDPROC).not_nil!
wc.hInstance = hInstance
wc.lpszClassName = CLASS_NAME
wc.style = CS_HREDRAW | CS_VREDRAW
if LibC.RegisterClassW(pointerof(wc)) == 0
err = LibC.GetLastError
raise "window class register failed because #{err}"
end
make_log.call "wc registerclass"
# Create the window.
hwnd = LibC.CreateWindowExW(
0, # Optional window styles
CLASS_NAME, # Window class
WINDOW_TITLE,
WS_OVERLAPPEDWINDOW, # Window style
# Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
nil, # Parent window
nil, # Menu
hInstance.unsafe_as(LibC::HINSTANCE), # Instance handle
nil # Additional application data
)
make_log.call "hwnd created"
unless hwnd
err = LibC.GetLastError
raise "hwnd isn't valid because #{err}"
end
LibC.ShowWindow(hwnd, nCmdShow)
make_log.call "window shown"
# Run the message loop.
msg = LibC::MSG.new
while (LibC.GetMessageW(pointerof(msg), nil, 0, 0) > 0)
make_log.call "get msg"
LibC.TranslateMessage(pointerof(msg))
LibC.DispatchMessageW(pointerof(msg))
end
make_log.call "return"
COLOR_WINDOW = 5
WM_DESTROY = 2
WM_PAINT = 0xF
@[CallConvention("X86_StdCall")]
def windowProc(hwnd : LibC::HWND, uMsg : UInt32, wParam : LibC::WPARAM, lParam : LibC::LPARAM) : LibC::LRESULT
case (uMsg)
when WM_DESTROY
LibC.PostQuitMessage(0)
return nil.as(Void*)
when WM_PAINT
ps = LibC::PAINTSTRUCT.new
hdc = LibC.BeginPaint(hwnd, pointerof(ps))
# All painting occurs here, between BeginPaint and EndPaint.
psrcPaint = ps.rcPaint
LibC.FillRect(hdc, pointerof(psrcPaint), (COLOR_WINDOW + 1).unsafe_as(LibC::HBRUSH))
ps.rcPaint = psrcPaint
LibC.EndPaint(hwnd, pointerof(ps))
return nil.as(Void*)
end
return LibC.DefWindowProcW(hwnd, uMsg, wParam, lParam)
end
