I've been trying to use Ruby's FFI to make a project that needs Win32 APIs. I can't get to see the Window at all. I get MessageBox to run successfully but no luck making an empty Window. My guess is that the issue lies within wrong types some variables more specifically WindowProc callback.
Here's the code:
require 'ffi'module Win32 NULL = 0 NO_ERROR = 1 ERROR_SUCCESS = 0x00 ERROR_NOT_LOCKED = 0x9E ERROR_CLIPBOARD_NOT_OPEN = 0x58A extend FFI::Library class WNDCLASS < FFI::Struct layout :style, :uint, :lpfnWndProc, :pointer, :cbClsExtra, :int, :cbWndExtra, :int, :hInstance, :pointer, :hIcon, :pointer, :hCursor, :pointer, :hbrBackground, :pointer, :lpszMenuName, :pointer, :lpszClassName, :pointer end class WNDCLASSW < FFI::Struct layout :style, :uint, :lpfnWndProc, :pointer, :cbClsExtra, :int, :cbWndExtra, :int, :hInstance, :pointer, :hIcon, :pointer, :hCursor, :pointer, :hbrBackground, :pointer, :lpszMenuName, :pointer, :lpszClassName, :pointer end class WNDCLASSEX < FFI::Struct layout :cbSize, :uint, :style, :uint, :lpfnWndProc, :pointer, :cbClsExtra, :int, :cbWndExtra, :int, :hInstance, :pointer, :hIcon, :pointer, :hIconSm, :pointer, :hCursor, :pointer, :hbrBackground, :pointer, :lpszMenuName, :pointer, :lpszClassName, :pointer end class MSG < FFI::Struct layout :hwnd, :uint, :message, :uint, :wparam, :pointer, :lparam, :pointer, :time, :uint, :pt_x, :int, :pt_y, :int end module Kernel32 extend FFI::Library ffi_lib 'Kernel32' ffi_convention :stdcall attach_function :GetModuleHandleW, [ :int ], :pointer end module User32 extend FFI::Library ffi_lib 'User32' ffi_convention :stdcall # MessageBox(HWND, LPCTSTR, LPCTSTR, uint) attach_function :MessageBoxW, [:pointer, :buffer_in, :buffer_in, :uint], :int # HWND CreateWindowExW(DWORD, LPCTSTR, LPCTSTR, DWORD, int, int, int, int, HWND, HMENU, HINSTANCE, LPVOID); attach_function :CreateWindowExW, [:int, :buffer_in, :buffer_in, :int, :int, :int, :int, :int, :pointer, :pointer, :pointer, :pointer], :pointer # extern 'ATOM RegisterClass(const WINDCLASS*);' Not Done # extern 'ATOM RegisterClassW(const WINDCLASS*);' attach_function :RegisterClassW, [:pointer], :ushort #extern 'ATOM RegisterClassEx(const WINDCLASSEX*);' #bool UpdateWindow(HWND) attach_function :UpdateWindow, [:pointer], :bool #'bool ShowWindow(HWND, int);' attach_function :ShowWindow, [:pointer, :int], :bool # bool GetMessage(void*, HWND, uint, uint);' attach_function :GetMessageW, [:pointer, :pointer, :uint, :uint], :bool # bool TranslateMessage(const void*) attach_function :TranslateMessage, [:pointer], :bool # bool DispatchMessage(const void*) attach_function :DispatchMessageW, [:pointer], :long # bool PostQuitMessage(uint) attach_function :PostQuitMessage, [:uint], :bool #void* DefWindowProcW(HWND, void*, void*, void*) attach_function :DefWindowProcW, [:pointer, :pointer, :pointer, :pointer], :pointer # HCURSOR LoadCursor(HINSTANCE, LPCTSTR) attach_function :LoadCursorW, [:pointer, :int], :pointer attach_function :WindowProcedure, [:pointer, :uint, :pointer, :pointer], :long Callback = FFI::Function.new(:long, [:pointer, :uint, :uintptr_t, :uintptr_t]) do |hwnd, msg, w_param, l_param| case msg when 0x0001 p 'Window created' when 0x0002 Win32::User32::PostQuitMessage(0) else Win32::User32::DefWindowProcW(hwnd, msg, w_param, l_param) end endendwc = Win32::WNDCLASS.newwclass_name = 'WindowClass'.encode('UTF-8')wc[:lpszClassName] = FFI::Pointer.new(wclass_name.to_i)wc[:hCursor] = Win32::User32::LoadCursorW(nil, 32512)wc[:hbrBackground] = 5wc[:hIcon] = 0wc[:cbClsExtra] = 0wc[:cbWndExtra] = 0wc[:hInstance] = Win32::Kernel32::GetModuleHandleW(0)wc[:style] = 0wc[:lpfnWndProc] = Win32::User32::WindowProcedure(Win32::User32::Callback)p Win32::User32::RegisterClassW(wc.pointer)wname = 'VentanaName'.encode('UTF-8')hwMain = Win32::User32::CreateWindowExW(0, FFI::Pointer.new(wclass_name.to_i), FFI::Pointer.new(wname.to_i), 0x10CF0000, 0, 0, 500, 500, nil, nil, nil, nil)if hwMain.nil? raise StandardError.new 'Unable to create a Window'endmsg = Win32::MSG.newmsg_ptr = msg.to_ptrWin32::User32::ShowWindow(hwMain, 5)Win32::User32::UpdateWindow(hwMain)while true m = Win32::User32::GetMessageW(msg_ptr, hwMain, 0, 0) p m if m != 0 Win32::User32::TranslateMessageW(msg_ptr) Win32::User32::DispatchMessageW(msg_ptr) else break endend
I initially used Fiddle, but lack of docs made it hard to stop guessing and look for AI help. After I switched to FFI I still had the same issue of the Window not showing up. I've read MS WinAPI docs and FFI examples.