Created
April 16, 2010 16:26
-
-
Save anonymous/368632 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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