Set Icon of InternetExplorer Application to ico file [Ruby 1.9.3]

-1

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.

ruby
winapi
asked on Stack Overflow Apr 28, 2017 by Sancarn

2 Answers

2

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.

answered on Stack Overflow Apr 29, 2017 by peter
0

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.

Full code for changing the icon of a iexplorer application:

#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
answered on Stack Overflow Apr 29, 2017 by Sancarn • edited Apr 29, 2017 by Sancarn

User contributions licensed under CC BY-SA 3.0