What are these extra parameters when calling SendInput via win32API
The function call takes a union
of those structs, I would wager it has something to do with that.
Looks like MOUSEINPUT
is larger than KEYBOARDINPUT
:
typedef struct tagMOUSEINPUT {
LONG dx;
LONG dy;
DWORD mouseData;
DWORD dwFlags;
DWORD time;
ULONG_PTR dwExtraInfo;
} MOUSEINPUT, *PMOUSEINPUT;
Dunno for sure though, that seems like a pretty terrifying way to bridge those calls!
edit: Yeah, looks like using that MOUSEINPUT
size, you end up w/28 bytes of data sent to the function, which is what pack('ISSIIIII')
would net you.
How to call CreateWindowEx from Ruby?
Solution: Use ffi
. For whatever reason, it just doesn't work in DL
(Win32API
uses DL
under the hood)
Full credit goes here, wherever this is (I can't read Japanese): http://www19.atwiki.jp/tmtbnc/m/pages/56.html
My guess is it's because DL
doesn't seem to support stdcall
, but I honestly don't know enough about it to know.
The FFI solution as used by me is below:
class Windows
def initialize
hInstance = Win32::GetModuleHandle(nil)
@window_class = Win32::WNDCLASSEX.new
@window_class[:style] = Win32::CS_HREDRAW | Win32::CS_VREDRAW
@window_class[:lpfnWndProc] = method(:message_pump)
@window_class[:hInstance] = hInstance
@window_class[:hbrBackground] = Win32::COLOR_WINDOWFRAME
@window_class[:lpszClassName] = FFI::MemoryPointer.from_string 'ruby-skype'
@window = Win32::CreateWindowEx(Win32::WS_EX_LEFT, ::FFI::Pointer.new(@window_class.atom), 'ruby-skype', Win32::WS_OVERLAPPEDWINDOW,
0, 0, 0, 0, Win32::NULL, Win32::NULL, hInstance, nil)
end
def message_pump(window_handle, message_id, wParam, lParam)
puts "WM: #{message_id}"
Win32::DefWindowProc(window_handle, message_id, wParam, lParam)
end
module Win32
extend FFI::Library
ffi_lib('user32', 'kernel32')
ffi_convention(:stdcall)
private
def self._func(*args)
attach_function *args
case args.size
when 3
module_function args[0]
when 4
module_function args[0]
alias_method(args[1], args[0])
module_function args[1]
end
end
public
ULONG_PTR = FFI::TypeDefs[:ulong]
LONG_PTR = FFI::TypeDefs[:long]
ULONG = FFI::TypeDefs[:ulong]
LONG = FFI::TypeDefs[:long]
LPVOID = FFI::TypeDefs[:pointer]
INT = FFI::TypeDefs[:int]
BYTE = FFI::TypeDefs[:uint16]
DWORD = FFI::TypeDefs[:ulong]
BOOL = FFI::TypeDefs[:int]
UINT = FFI::TypeDefs[:uint]
POINTER = FFI::TypeDefs[:pointer]
VOID = FFI::TypeDefs[:void]
HWND = HICON = HCURSOR = HBRUSH = HINSTANCE = HGDIOBJ =
HMENU = HMODULE = HANDLE = ULONG_PTR
LPARAM = LONG_PTR
WPARAM = ULONG_PTR
LPCTSTR = LPMSG = LPVOID
LRESULT = LONG_PTR
ATOM = BYTE
NULL = 0
WNDPROC = callback(:WindowProc, [HWND, UINT, WPARAM, LPARAM], LRESULT)
class WNDCLASSEX < FFI::Struct
layout :cbSize, UINT,
:style, UINT,
:lpfnWndProc, WNDPROC,
:cbClsExtra, INT,
:cbWndExtra, INT,
:hInstance, HANDLE,
:hIcon, HICON,
:hCursor, HCURSOR,
:hbrBackground, HBRUSH,
:lpszMenuName, LPCTSTR,
:lpszClassName, LPCTSTR,
:hIconSm, HICON
def initialize(*args)
super
self[:cbSize] = self.size
@atom = 0
end
def register_class_ex
(@atom = Win32::RegisterClassEx(self)) != 0 ? @atom : raise("RegisterClassEx Error")
end
def atom
@atom != 0 ? @atom : register_class_ex
end
end # WNDCLASSEX
class POINT < FFI::Struct
layout :x, LONG,
:y, LONG
end
class MSG < FFI::Struct
layout :hwnd, HWND,
:message, UINT,
:wParam, WPARAM,
:lParam, LPARAM,
:time, DWORD,
:pt, POINT
end
_func(:RegisterWindowMessage, :RegisterWindowMessageA, [LPCTSTR], UINT)
_func(:GetModuleHandle, :GetModuleHandleA, [LPCTSTR], HMODULE)
_func(:RegisterClassEx, :RegisterClassExA, [LPVOID], ATOM)
_func(:CreateWindowEx, :CreateWindowExA, [DWORD, LPCTSTR, LPCTSTR, DWORD, INT, INT, INT, INT, HWND, HMENU, HINSTANCE, LPVOID], HWND)
_func(:GetMessage, :GetMessageA, [LPMSG, HWND, UINT, UINT], BOOL)
_func(:TranslateMessage, [LPVOID], BOOL)
_func(:DispatchMessage, :DispatchMessageA, [LPVOID], BOOL)
_func(:DefWindowProc, :DefWindowProcA, [HWND, UINT, WPARAM, LPARAM], LRESULT)
# @!group Predefined WindowHandle's
#
# These are WindowHandle's provided by the Win32 API for special purposes.
# Target for SendMessage(). Broadcast to all windows.
HWND_BROADCAST = 0xffff
# Used as a parent in CreateWindow(). Signifies that this should be a message-only window.
HWND_MESSAGE = -3
# @!endgroup
# CreateWindow Use Default Value
CW_USEDEFAULT = 0x80000000
COLOR_WINDOW = 5
COLOR_WINDOWFRAME = 6
# @!group Class Style contants.
CS_VREDRAW = 0x0001
CS_HREDRAW = 0x0002
# @!group Window Style constants
#
# This is only a subset.
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms632600.aspx
WS_BORDER = 0x00800000
WS_CAPTION = 0x00C00000
WS_DISABLED = 0x08000000
WS_OVERLAPPED = 0x00000000
WS_POPUP = 0x80000000
WS_SIZEBOX = 0x00040000
WS_SYSMENU = 0x00080000
WS_THICKFRAME = 0x00040000
WS_MAXIMIZEBOX = 0x00010000
WS_MINIMIZEBOX = 0x00020000
WS_OVERLAPPEDWINDOW = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX
WS_POPUPWINDOW = WS_POPUP | WS_BORDER | WS_SYSMENU
# @!group Window Extended Style constants
#
# This is only a subset.
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ff700543.aspx
WS_EX_LEFT = 0
# @!endgroup
end
end
How to call a Windows DLL from within Ruby?
Try Ruby's FFI
module.
Two examples to get you started:
Calling a C function from a DLL that returns a pointer to a string
Calling a Win32 function from within Ruby
The older DL
module seems to be less used and not as easy.
Ruby's *args causing seg fault when no args supplied
The likely reason it's seg faulting is because somewhere underneath Win32API
the lack of arguments is translating into one or more NULL pointer dereferences.
If you are going to create functions like this then you should consider adding a per-api-call, boolean-returning argument validation block (or lambda) as an argument to def_api
which you would then invoke as the first part of your derived function. Something like this:
def self.def_api(function, parameters, return_value, &valid_args?)
api = Win32API.new 'user32', function, parameters, return_value
define_method(function.snake_case) do |*args|
if valid_args? and valid_args?.call(*args)
api.call *args
else
// raise argument error, etc
end
end
end
Then
def_api 'FindWindow', ['P', 'P'], 'L', { |args| return true if // API specific checks succeed }
UPDATE: Adding more color at questioner's request
The &valid_args?
is the name of a block parameter. The ampersand prefix (&) is how you tell ruby that you passing a block. You can only pass one block to a methodand it must be the last parameter in the argument list. The question mark suffix (?) is just a convention in Ruby programming for naming functions which return boolean values.
If a block is passed you invoke it with &block.call(args)
To call a method with a block argument you do something like this:
method { |args| // block implementation }
or
method do |args|
// block implementation
end
The args
are passed to the block through the call method
. Hope this helps.
How can I write a generic C function for calling a Win32 function?
No, I don't think its possible to do with without writing some assembly. The reason is you need precise control over what is on the stack before you call the target function, and there's no real way to do that in pure C. It is, of course, simple to do in Assembly though.
Also, you're using PCSTR for all of these arguments, which is really just const char *
. But since all of these args aren't strings, what you actually want to use for return value and for Arguments[] is void *
or LPVOID
. This is the type you should use when you don't know the true type of the arguments, rather than casting them to char *
.
How to pass by reference in Ruby?
Ruby is strictly pass-by-value, which means references in the caller's scope are immutable. Obviously they are mutable within the scope, since you can assign to them after all, but they don't mutate the caller's scope.
a = 'foo'
def bar(b)
b = 'bar'
end
bar(a)
a
# => 'foo'
Note, however, that the object can be mutated even if the reference cannot:
a = 'foo'
def bar(b)
b << 'bar' # the only change: mutate the object instead of the reference
b = 'baz'
end
bar(a)
a
# => 'foobar'
If the object you get passed is immutable, too, then there is nothing you can do. The only possibilities for mutation in Ruby are mutating the reference (by assignment) or asking an object to mutate itself (by calling methods on it).
You can return an updated value from your method and have the caller assign that to the reference:
a = :foo
def bar(b)
:"#{a}bar"
end
c = bar(a)
c
# => :foobar
Or you can wrap the immutable object in a mutable one and mutate that mutable wrapper:
a = [:foo]
def bar(b)
b[0] = :bar
end
bar(a)
a
# => [:bar]
[This is really the same thing as the second example above.]
But if you can't change anything, then you are stuck.
Related Topics
How to Disable Wait Time for Watir
Ruby 'Pass by Value' Clarification
Call Ruby Script from Powershell
How to Mask All But Last Four Characters in a String
Escaping Apostrophes Using Gsub
Rvm and Osx Lion - Rvm 'Forgets' Gemsets on System Restart
Error Loading Media: File Could Not Be Played Error in Jw_Player Rails
Cast Between String and Classname
Controller Can Not Detect Ajax Requests
How to Get Out of a "Hung" State in Irb
How to Save Heroku Logs to Text File
Redirect_Uri_Mismatch. Login with Google Using Ruby on Rails
"Encoding::Invalidbytesequenceerror" Error Occurs in Rails 3.1.0