I have created a relatively simple Ruby script which allows the user to select an item from a drop down box. I do this by creating an instance of internet explorer and then automating it from ruby.
Preferably I would want to take the UI further and change the ICON and possibly even the window caption.
Note: I do realise this is 're-inventing the wheel' as it were, as there are many Gems available that aid in the creation of GUIs. However, unfortunately I need a solution in pure standard Ruby.
Here is my code currently:
# To learn how to use win32 libraries see here:
# http://phrogz.net/programmingruby/lib_windows.html
# https://ruby-doc.org/stdlib-2.2.0/libdoc/win32ole/rdoc/WIN32OLE.html
require 'win32ole'
require 'Win32API'
def combobox(sTitle, aOptions)
ie = WIN32OLE.new('InternetExplorer.Application')
#set various options
ie.resizable = false
ie.toolbar = false
ie.registerAsDropTarget = false
ie.statusBar = false
ie.navigate("about:blank")
sleep(0.1) until !ie.busy
ie.width=450
ie.height=190
ie.left = ie.document.parentWindow.screen.width / 2 - 200
ie.top = ie.document.parentWindow.screen.height / 2 - 75
#Set window icon (C++)
# HANDLE icon = LoadImage(fgDisplay.Instance, "c:\\icon.ico", IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
# SendMessage(instance, (UINT)WM_SETICON, ICON_BIG, (LPARAM)icon);
#icon = LoadImage(null,"my/file/name/as/string.ico",IMAGE_ICON=1,0 or 32,0 or 32,LR_DEFAULTSIZE||LR_LOADFROMFILE) #LR_DEFAULTSIZE = 0x00000040; LR_LOADFROMFILE = 0x00000010;
#SendMessage(HWND, WM_SETICON, ICON_BIG, icon)
#User32 LoadImage: https://msdn.microsoft.com/en-us/library/windows/desktop/ms648045(v=vs.85).aspx
#User32 SendMessageA: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644950(v=vs.85).aspx
#define WM_SETICON 0x0080
#ICON_BIG 1
#ICON_SMALL 0
#Define constants:
_IMAGE_ICON = 1
_LR_LOADFROMFILE = 0x00000040
_LR_DEFAULTSIZE = 0x00000010
_WM_SETICON = 0x0080
_ICON_BIG = 1
_ICON_SMALL = 0
#Set icon
icoPath = "C:\Users\jwa\Desktop\Icons\toxic.ico" #"l" --> "v"
icon = Win32API.new("user32","LoadImage",["v","p","i","i","i","i"],"p").call(0,icoPath, _IMAGE_ICON, 0,0, _LR_DEFAULTSIZE || _LR_LOADFROMFILE)
puts icon
Win32API.new("user32","SendMessageA",["l","i","i","p"],"i").call(ie.hwnd,_WM_SETICON,_ICON_SMALL,icon)
#Set window content
s = "
<html>
<head>
<title>#{sTitle}</title>
</head>
<script type=\"text/javascript\">window.bWait=true</script>
<script>
submitForm = function(){
document.url = document.getElementById(\"entries\").selectedIndex
}
</script>
<body bgColor=Silver>
<center>
<b>#{sTitle}<b>
<p>
<select id=entries size=1 style='width:250px'>
<option selected></option>"
#Add options
aOptions.each do |item|
s += "<option>#{item}</option>"
end
s += " </select>
<p>
<button id=but0 onclick='submitForm()'>OK</button>
</center>
</body>
</html>"
ie.document.writeLn(s)
ie.document.body.scroll = "no"
ie.document.body.style.borderStyle = "outset"
ie.document.body.style.borderWidth = "3px"
ie.document.all.entries.Focus
ie.visible = true
#Bring window to front
Win32API.new("user32","BringWindowToTop",["l"],"i").call(ie.hwnd)
#wait till url not about:blank
begin
until (ie.document.url!="about:blank") do
sleep(0.1)
end
rescue
return -1
end
#Get id and hide ie
id=ie.document.url
id=id.to_i
ie.visible = false
#Return id-1
return id-1
end
In general the script works however I am yet to get the custom icon working.
#Set icon
icoPath = "C:\Users\jwa\Desktop\Icons\toxic.ico" #"l" --> "v"
icon = Win32API.new("user32","LoadImage",["v","p","i","i","i","i"],"p").call(0,icoPath, _IMAGE_ICON, 0,0, _LR_DEFAULTSIZE || _LR_LOADFROMFILE)
puts icon ### ==> 0
Win32API.new("user32","SendMessageA",["l","i","i","p"],"i").call(ie.hwnd,_WM_SETICON,_ICON_SMALL,icon) ### ==> Doesn't do anything (likely due to icon==0)
Is there anything obvious which is wrong with this code?
Note: Again, I would love to use a Gem like ffi for this, however that is unfortunately not possible.
Nice solution, I'm late with this but would like to show you what I use for such tasks. I used to use green_shoes for such tasks but got tired of the not native look of the controls and by other limitations (eg no grid's).
On the other hand there is autohotkey which is powerfull and has a very good GUI but is difficult to code in (surely compared with Ruby).
So I tried autohotkey.dll, it lets you use autohotkey code and so also GUI from Ruby. Works very well. You need to download and register the dll. https://autohotkey.com/board/topic/39588-autohotkeydll/
Here an example of a dropdown selection (there is a real combo also).
require 'win32ole'
def choose_from_dropdown *list
selections = list.join('|')
code = %Q{
Gui, Add, DropDownList, r50 w200 vChoice, #{selections}
Gui, Show, x131 y91 h67 w227, Make your choice
Hotkey,Enter,a1,ON
Return
GuiClose:
Gui, Submit, nohide
return
a1:
Gui, Submit, nohide
return
}
ahk = WIN32OLE.new("AutoHotkey.Script")
ahk.AhkTextDll(code)
choice = nil
while ahk.ahkReady == 1 do
sleep 1
choice = ahk.ahkgetvar('Choice')
if choice && choice != ""
ahk.ahkTerminate
return choice
end
end
rescue => e
puts e.message
puts e.backtrace
end
puts choose_from_dropdown %w{one two three}
In such lists I like to scroll with the up and down keys and confirm with the Enter key, so that is what I implemented, other implementations with Ok button etc are also possible.
Well... This is embarrassing. It was a really nooby error... I keep forgetting that in Ruby \
escapes characters in a string so:
#Set icon
icoPath = "C:\Users\jwa\Desktop\Icons\toxic.ico"
actually returned "C:UsersjwaDesktopIconstoxic.ico"
which of course is not the file path... I needed to do:
#Set icon
icoPath = "C:\\Users\\jwa\\Desktop\\Icons\\toxic.ico"
Oops. Also, it appears that win32api uses "p" for pointers within ruby. So when it comes to using the handle returned by win32api in the seticon statement, i.e.
Win32API.new("user32","SendMessageA",[args*],"i")
the final parameter of args* should be a long "l"
instead of a pointer. Like this:
Win32API.new("user32","SendMessageA",["l","i","i","l"],"i").call(ie.hwnd,_WM_SETICON,_ICON_SMALL,icon)
also it is important to note that you should call both _ICON_SMALL
and _ICON_BIG
. _ICON_SMALL
is the icon in the window itself and _ICON_BIG
is the icon in the alt-tab menu.
#Define constants:
_IMAGE_ICON = 1
_LR_LOADFROMFILE = 0x00000040
_LR_DEFAULTSIZE = 0x00000010
_WM_SETICON = 0x0080
_ICON_BIG = 1
_ICON_SMALL = 0
#Set icon
icoPath = "C:\\Users\\jwa\\Desktop\\Icons\\toxic.ico" #"l" --> "v"
icon = Win32API.new("user32","LoadImageA",["l","p","l","l","l","l"],"p").call(0,icoPath, _IMAGE_ICON, 0,0, _LR_DEFAULTSIZE | _LR_LOADFROMFILE | 0)
Win32API.new("user32","SendMessageA",["l","i","i","l"],"i").call(ie.hwnd,_WM_SETICON,_ICON_SMALL,icon) # affects title bar
Win32API.new("user32","SendMessageA",["l","i","i","l"],"i").call(ie.hwnd,_WM_SETICON,_ICON_BIG,icon) # affects alt-tab
User contributions licensed under CC BY-SA 3.0