Skip to content

Instantly share code, notes, and snippets.

Created April 16, 2010 16:26
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/368632 to your computer and use it in GitHub Desktop.
Save anonymous/368632 to your computer and use it in GitHub Desktop.
# Contents
#
# module MsgBox # message Box constants
#
# module WinAPI ## only a few rudimentary Windows functions are in this
#
# AutoIt = WIN32OLE.new( "AutoItX3.Control" ) ## not for JRuby
# # AutoIt.MsgBox( 0, "title", "text" ) # this doesn't work
# pop-up a message box directly from Ruby;
# User32_MsgBox = User32[ 'MessageBoxA', 'ILSSI' ]
# # works in JRuby 1.8 and MRI Ruby 1.8.6, but not in MRI Ruby 1.9.1;
#
# Windows API functions
# GetCursorPos = Win32API.new( 'user32', 'GetCursorPos', 'P', 'V' )
# SetCursorPos = Win32API.new( 'user32', 'SetCursorPos', 'II', 'I' )
# SendInput = Win32API.new( 'user32', 'SendInput', 'IPI', 'I' )
# GetActiveWindow = Win32API.new( "user32", "GetActiveWindow", [], 'L' )
# GetDesktopWindow = Win32API.new( "user32", "GetDesktopWindow", [], 'L' )
# MsgBox # AutoIt and non AutoIt versions
#
# InputBox # uses AutoIt via C:/Program Files/AutoIt3/AutoIt3.exe
#
# # mouse stuff not using AutoIt
# def right_click
# def left_click
# def mouse_pos() # [x = from screen left, y = from screen top]
# def move_mouse( x, y )
#
# send_keys( *args ) # wraps AutoIt Send; args can include numerics
# # which trigger "sleep" for that number of seconds;
# # send_keys( "c", 1.5, "k" ) sends "c", sleeps 1.5 seconds, sends "k"
#
# # Daniel Berger & Heesob Park's MS Windows modules have clipboard functions
# # which don't use AutoIt, but I have not tried them.
# def clipboard() ; AutoIt.ClipGet() ; end
# def clipboard=( text ) ; AutoIt.ClipPut( text ) ; end
#
# class Browser # uses the above to control browsers
## AutoIt -> DllCall - looks maybe useful for Ruby?
# MSDN library site http://www.msdn.microsoft.com/ and select library
# ShowCursor - show or hide the cursor (aka mouse pointer)
# SetCursor and SetSystemCursor - set the cursor shape
# SetCursorPos - moves the cursor to the specified position
# mouse_event or (for Windows NT/2000/XP) SendInput - can
# synthesise mouse events such as movement and button clicks.
# As to MS Windows mouse messages these include:
# WM_LBUTTON_DOWN, WM_LBUTTON_UP, WM_LBUTTON_CLK, WM_LBUTTONDBLCLK
# WM_RBUTTON_DOWN, WM_RBUTTON_UP, WM_RBUTTON_CLK, WM_LBUTTONDBLCLK
# WM_MOUSEMOVE.
## $ai = WIN32OLE.new( "AutoItX3.Control" )
## # $ai.Send( "Salome" )
## # q = $ai.ClipGet # this works
## # $ai.MsgBox( 0, "title", "text" ) # this doesn't work
## # $ai.Send( "!{TAB}" )
## # $ai.MouseMove( 200, 300, 0 )
puts "\n*#{RUBY_VERSION}"
# RUBY_VERSION
if RUBY_PLATFORM != "java" then
require "win32ole"
end
# note that Win32API is deprecated after Ruby 1.9.1 - use dl directly instead
require "Win32API"
module MsgBox # message Box constants
# If 7 to 15 used as a button, no MsgBox is displayed and return value is 0.
# Icons
NoIcon = 0
Critical = 16
Question = 32
Exclamation = 48
Information = 64
# Buttons
OKOnly = 0
OKCancel = 1
AbortRetryIgnore = 2
YesNoCancel = 3
YesNo = 4
RetryCancel = 5
CancelTryAgainContinue = 6 # only valid on Windows 2000/XP and above
# Default Buttons
DefaultButton1 = 0
DefaultButton2 = 256
DefaultButton3 = 512
DefaultButton4 = 768
# Modality Constants
ApplicationModal = 0
SystemModal = 4096
# Return Values
OK = 1
Cancel = 2
Abort = 3
Retry = 4
Ignore = 5
Yes = 6
No = 7
TryAgain = 10 # only valid on Windows 2000/XP and above
Continue = 11 # only valid on Windows 2000/XP and above
end
module WinAPI ## only a few rudimentary Windows functions are in this
# AutoIt to be able to send key presses easily
# because for now I can't work out how to use SendInput to do this
if RUBY_PLATFORM != "java" then
AutoIt = WIN32OLE.new( "AutoItX3.Control" )
# AutoIt.MsgBox( 0, "title", "text" ) # this doesn't work
end
# note that Win32API is deprecated after Ruby 1.9.1 - use dl directly instead
require 'dl'
User32 = DL.dlopen( 'user32' )
if RUBY_VERSION < "1.9" then
# to be able to pop-up a message box directly from Ruby;
# works in JRuby 1.8 and MRI Ruby 1.8.6, but not in 1.9.1;
User32_MsgBox = User32[ 'MessageBoxA', 'ILSSI' ]
end
# Windows API functions
GetCursorPos = Win32API.new( 'user32', 'GetCursorPos', 'P', 'V' )
SetCursorPos = Win32API.new( 'user32', 'SetCursorPos', 'II', 'I' )
SendInput = Win32API.new( 'user32', 'SendInput', 'IPI', 'I' )
GetActiveWindow = Win32API.new( "user32", "GetActiveWindow", [], 'L' )
GetDesktopWindow = Win32API.new( "user32", "GetDesktopWindow", [], 'L' )
# Windows API constants
INPUT_MOUSE = 0x0
MOUSEEVENTF_LEFTDOWN = 0x0002
MOUSEEVENTF_LEFTUP = 0x0004
MOUSEEVENTF_RIGHTDOWN = 0x0008
MOUSEEVENTF_RIGHTUP = 0x0010
module_function
# has MsgBox title and text in a more "expected" order
def MsgBox!( title, text, buttons = 0, icon = 0, modality = 0,
timeout = 0, handle = -1 )
MsgBox( text, title, buttons, icon, modality, timeout, handle )
end
def MsgBox( text, title = "", buttons = 0, icon = 0, modality = 0,
timeout = 0, handle = -1 )
if const_defined?( :User32_MsgBox ) && timeout <= 0 && (handle == -1) then
# User32_MsgBox.call returns an array:
# [return_value, [0, text, title, buttons + icon + modality]]
# we only want the return value, hence "result, ="
result, = User32_MsgBox.call(0, text, title, buttons + icon + modality)
else
result = ai_InputBox_MsgBox( "MsgBox", buttons + icon + modality,
title, text, timeout, handle )
end
return result
end
def InputBox( title, text, default_return = "", pw_char = "",
width = -1, height = -1,
left = -1, top = -1,
timeout = 0, handle = nil )
result = ai_InputBox_MsgBox( "InputBox", title, text,
default_return, pw_char,
width, height, left, top,
timeout, handle )
return result
end
# Uses AutoIt (run using system "command") to show a MsgBox or InputBox
# and for InputBox returns the input using the clipboard.
def ai_InputBox_MsgBox( typ, *args )
q_return_via_clipboard = true
case typ
when "MsgBox" then
q_return_via_clipboard = false
# find last non default argument
last_arg_index = args.size - 1
last_non_default_arg_index = nil
last_arg_index.downto(0) do |n|
if ! (case n
when 0, 3 then args[n] == 0 # buttons+icon+etc, timout
when 1, 2 then args[n] == "" # title, text
when 4 then args[n] == -1 # handle
else false
end) then
last_non_default_arg_index = n ; break
end
end
last_non_default_arg_index ||= -1 # note -1, not 1
when "InputBox" then
# find last non default argument
last_arg_index = args.size - 1
last_non_default_arg_index = nil
last_arg_index.downto(2) do |n|
if ! (case n
when 2, 3 then args[n] == ""
when 4 then args[n] == -1 && args[n + 1] == -1 # width
when 5 then args[n - 1] == -1 && args[n] == -1 # height
when 6 then args[n] == -1 && args[n + 1] == -1 # position left
when 7 then args[n - 1] == -1 && args[n] == -1 # position top
when 8 then args[n] == 0 # timeout
when 9 then args[n] == -1 # handle
else false
end) then
last_non_default_arg_index = n ; break
end
end
last_non_default_arg_index ||= 1 # note 1, not -1
else return -9999 # invalid typ
end
cmd = "C:/Program Files/AutoIt3/AutoIt3.exe"
cmd << ' "' + "C:/!-Progs/!-AutoIt/ai_InputBox_MsgBox.au3" + '"'
cmd << ' ' + typ
(0 .. last_non_default_arg_index).each do |n|
case ( arg = args[n] )
when String then arg = '"' + arg + '"'
when nil then arg = "nil"
else arg = arg.to_s
end
cmd << ' ' + arg
end
if q_return_via_clipboard then
clipboard_saved = WinAPI.clipboard()
end
result = system cmd
if (exit_code = $? && $?.exited? && $?.exitstatus) then
if exit_code >= 128 then
exit_code -= 256
end
end
result = exit_code || -999
if q_return_via_clipboard then
if result == 0 then
result = WinAPI.clipboard()
end
WinAPI.clipboard = clipboard_saved
end
result
end
# borrowed from a library written by who??
def send_input( inputs )
n = inputs.size
ptr = inputs.collect { |i| i.to_s }.join # flatten arrays into single string
SendInput.call( n, ptr, inputs[0].size )
# # a later (?) version
# ptr = inputs.collect { |i| i.to_ptr.to_s}.join()
# SendInput( n, ptr, inputs[0].class.size)
end
def create_mouse_input( mouse_flag )
mi = Array.new(7, 0)
mi[ 0 ] = INPUT_MOUSE
mi[ 4 ] = mouse_flag
mi.pack( 'LLLLLLL' ) # Pack array into a binary sequence usable to SendInput
end
def right_click
down = create_mouse_input( MOUSEEVENTF_RIGHTDOWN )
up = create_mouse_input( MOUSEEVENTF_RIGHTUP )
send_input( [ down, up ] )
end
def left_click
down = create_mouse_input( MOUSEEVENTF_LEFTDOWN )
up = create_mouse_input( MOUSEEVENTF_LEFTUP )
send_input( [ down, up ] )
end
def mouse_pos() # [x = from screen left, y = from screen top]
lpPoint = ' ' * 8
GetCursorPos.Call( lpPoint )
lpPoint.unpack( 'LL' )
end
def move_mouse( x, y )
SetCursorPos.call( x, y )
end
# wraps AutoIt Send;
# args can include numerics to trigger "sleep" for that number of seconds;
# send_keys( "c", 1.5, "k" ) sends "c", sleeps 1.5 seconds, sends "k"
def send_keys( *args )
args = args[ 0 ] if args.size == 1 && args[ 0 ].kind_of?( Array )
args.each do | arg |
case arg
when String then AutoIt.Send( arg )
when Numeric then
if ! ( defined?( Complex ) && arg.kind_of?( Complex ) ) then
sleep arg
end
end
end
end
# I seem to recall that Daniel Berger & Heesob Park's MS Windows modules
# have versions of these which don't use AutoIt.
def clipboard() ; AutoIt.ClipGet() ; end
def clipboard=( text ) ; AutoIt.ClipPut( text ) ; end
def get_active_window() ; GetActiveWindow.Call() ; end
def get_desktop_window() ; GetDesktopWindow.Call() ; end
end
class Browser
attr_accessor :type
def initialize( browser_name )
browser_name = browser_name.to_s if browser_name.kind_of?( Symbol )
@type = case browser_name.downcase
when "opera", "opera_10_51" then :Opera_10_51
when "firefox", "firefox_3_5_5" then :Firefox_3_5_5
when "ie", "ie8", "ie_8",
"internetexplorer", "internetexplorer8",
"internetexplorer_8" then :InternetExplorer_8
else
raise "Browser: type=#{bowser_name.inspect}; not supported"
end
end
def get_url()
case @type
when :Opera_10_51
WinAPI.send_keys( "{F8}", 0.0625, "^{INSERT}", 0.125, "{ESC}" )
when :Firefox_3_5_5
WinAPI.send_keys( "!d", 0.0625, "^{INSERT}", 0.375 )
when :InternetExplorer_8
WinAPI.send_keys( "!d", 0.0625, "^{INSERT}", 0.125 )
else
raise "Browser: type=#{@type.inspect}; not supported"
end
WinAPI.clipboard()
end
def get_save_title_leave_save_input_box_open()
case @type
when :Opera_10_51
WinAPI.send_keys( "^s", 0.75, "^{INSERT}", 0.5 ) # needs longish delay??
when :Firefox_3_5_5
WinAPI.send_keys( "^s", 0.375, "^{INSERT}", 0.125 )
when :InternetExplorer_8
WinAPI.send_keys( "!f", 0.125, "a", 0.125, "^{INSERT}", 0.125 )
else
raise "Browser: type=#{@type.inspect}; not supported"
end
WinAPI.clipboard()
end
def save_from_open_save_input_box( save_type, path )
WinAPI.clipboard= path
WinAPI.send_keys( 0.0625, "+{INSERT}", 0.125 )
if ! ( ext = path.rindex( "." ) ) then
raise "Browser#save_from_open_save_input_box:" \
" save path should have a .extension"
end
ext_save_type = case ( ext = path[ ext .. -1 ] ).downcase
when ".mht" then :mht
when ".htm", ".html" then :htm
when ".txt" then :txt
else
raise "Browser#save_from_open_save_input_box:" \
" invalid extension = #{ext.inspect}"
end
q_ext_matches_save_type = case save_type
when :txt, :mht, :htm then
ext_save_type == save_type
when :htmi then ext_save_type == :htm
else false
end
if ! q_ext_matches_save_type then
raise "Browser#save_from_open_save_input_box" \
" - non-matching or invalid: save_type= #{save_type.inspect};" \
" path_extension= #{ext.inspect}"
end
case @type
when :Opera_10_51
save_seq = case save_type
when :txt then "t"
when :htm then "th"
when :htmi then "thh"
when :mht then "w"
else
raise "Browser#save_from_open_save_input_box:" \
" invalid save_type= #{save_type.inspect}"
end
WinAPI.send_keys( "{TAB}", 0.0675, save_seq, 0.0675, "{ENTER}" )
when :Firefox_3_5_5
save_seq = case save_type
when :txt then "t"
when :htmi then "tw"
when :htm then "tww"
else
raise "Browser#save_from_open_save_input_box:" \
" invalid save_type= #{save_type.inspect}"
end
WinAPI.send_keys( "{TAB}", 0.0675, save_seq, 0.0675, "{ENTER}" )
when :InternetExplorer_8
save_seq = case save_type
when :txt then "t"
when :htmi then "tw"
when :mht then "tww"
when :htm then "twww"
else
raise "Browser#save_from_open_save_input_box:" \
" invalid save_type= #{save_type.inspect}"
end
WinAPI.send_keys( "{TAB}", 0.0675, save_seq, 0.0675, "{ENTER}" )
else
raise "Browser: type=#{@type.inspect}; not supported"
end
nil
end
end
__END__
### BEGIN{
### } ##### BEGIN{
### The following code works (that is does not raise any exceptions)
### in Ruby 1.9, but I have not yet found out how to use it
### to send keystrokes.
require 'dl/import'
require 'dl/types'
module Input
extend DL::Importer ###??? does not work in Ruby 1.8.6
dlload "user32.dll"
include DL::Win32Types
typealias "LPINPUT", "void*"
# See also cygwin's winuser.h, if you use cygwin.
INPUT_MOUSE = 0x00000000
INPUT_KEYBOARD = 0x00000001
INPUT_HARDWARE = 0x00000002
VK_LBUTTON = 1
VK_RBUTTON = 2
VK_CANCEL = 3
VK_MBUTTON = 4
VK_BACK = 8
VK_TAB = 9
VK_RETURN = 13
VK_SHIFT = 16
VK_CONTROL = 17
VK_SNAPSHOT = 44
VK_NUMLOCK = 0x90
VK_LMENU = 0xA4
VK_RMENU = 0xA5
KEYEVENTF_EXTENDEDKEY = 0x00000001
KEYEVENTF_KEYUP = 0x00000002
KEYEVENTF_UNICODE = 0x00000004
KEYEVENTF_SCANCODE = 0x00000008
MouseInput = struct [
"DWORD type",
"long dx",
"long dy",
"DWORD mouseData",
"DWORD dwFlags",
"DWORD time",
"ULONG *dwExtraInfo",
]
KeyboardInput = struct [
"DWORD type",
"WORD wVk",
"WORD wScan",
"DWORD dwFlags",
"DWORD time",
"ULONG *dwExtraInfo",
"DWORD dummy1",
"DWORD dummy2",
]
HardwareInput = struct [
"DWORD type",
"DWORD uMsg",
"WORD wParamL",
"WORD wParamH",
"ULONG *dwExtraInfo",
"DWORD dummy1",
"DWORD dummy2",
"DWORD dummy3",
]
extern "UINT SendInput(UINT, LPINPUT, int)", :stdcall
def send_input(inputs)
n = inputs.size()
ptr = inputs.collect{|i| i.to_ptr.to_s}.join()
SendInput(n, ptr, inputs[0].class.size())
end
module_function :send_input
end
def create_keyboard_input()
ki = Input::KeyboardInput.malloc()
ki.type = Input::INPUT_KEYBOARD
ki.wVk = 0
ki.wScan = 0
ki.dwFlags = 0
ki.time = 0
ki.dwExtraInfo = 0
ki.dummy1 = 0
ki.dummy2 = 0
ki
end
ki0 = create_keyboard_input()
ki0.wVk = Input::VK_SNAPSHOT # Input::VK_SNAPSHOT
ki1 = create_keyboard_input()
ki1.wVk = Input::VK_SNAPSHOT # Input::VK_SNAPSHOT
ki1.dwFlags = Input::KEYEVENTF_KEYUP
p Input.send_input([ki0, ki1])
DL.free(ki0.to_i)
DL.free(ki1.to_i)
__END__
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment