rubywinapiruby-ffi

How do I change icon positions via FFI?


I'm trying to change the positions of icons in a Windows 7 Desktop using Ruby with the FFI gem.

So far "Managing Desktop Icons" has most of what I want but it's not working for me.

The article uses LVA_ALIGNLEFT at one point but I understand I need to use LVS_ALIGNLEFT according to "LVM_ARRANGE message".

require 'ffi'

module Win32
  extend FFI::Library
  ffi_lib 'user32'
  ffi_convention :stdcall

  enum :lvm, [:LVM_GETITEMCOUNT, 4100,
              :LVM_ARRANGE, 4118,
              :LVM_SETITEMPOSITION, 4111]

  enum :parameter, [:GW_CHILD, 5,
                    :LVS_ALIGNLEFT, 2048 ]

  # Uses C Function to find the window handle(HWND) of a window with the specified text, in this case 'ProgMan'
  attach_function :findWindow, 
    :FindWindowA,[ :string, :parameter ], :int

  # Gets passed in window's child window 
  attach_function :getChildWindow,
    :GetWindow, [ :int, :parameter], :int

  # Sends a message to the passed in window  
  attach_function :sendMessage,
    :SendMessageA, [ :int, :lvm, :parameter, :long], :int
end

# Finds Windows handle for FolderView, SysListView32 which is the icon list
DesktopHandle = Win32.findWindow('ProgMan', 0)
DesktopHandle = Win32.getChildWindow(DesktopHandle, :GW_CHILD)
DesktopHandle = Win32.getChildWindow(DesktopHandle, :GW_CHILD)

# Aligns icons left?
Win32.sendMessage(DesktopHandle, :LVM_ARRANGE, :LVS_ALIGNLEFT, 0)

Win32.sendMessage(DesktopHandle, :LVM_ARRANGE, :LVS_ALIGNLEFT, 0) should left-align the icons but does nothing.

I've used Win32.sendMessage(DesktopHandle, :LVM_GETITEMCOUNT, 0, 0) to make sure I have the proper handle and this does output the same number of icons I have.

I picked up most of my values from "LVM_* defs".

I'm just not sure why this isn't working.

EDIT: Looks like the the left-align is actually working I just didn't realize what it was doing, I thought that using this command should have moved all my icons to the left side of my screen, however this command only changes the alignment! I.E. in Microsoft word when you left-align something, or center something this changes how the text is.

Now I need to get the actual part that changes the icon's position to work:

for (int i=0; i<200; i++)
  SendMessage(DesktopHandle, LVM_SETITEMPOSITION, 0, MAKELPARAM(10, i));

The only part I'm still searching for is how to do the MAKELPARAM(10, I) part in ruby, I think I might want to use the Ruby's array#pack, but I'm not positive how to go about it.

Update: Turns out my source for the LVM values weren't what I needed, instead I used the values from "List-View Messages".(I updated my numbers so the above are good)

I also found "Ruby Class: Object" for the MAKELPARAM().

def MAKELPARAM(w1,w2)
  return (w2<<16) | w1
end

This moved one of my icons Win32.sendMessage(DesktopHandle, :LVM_SETITEMPOSITION, 0, makeLPARAM(10, 1))


Solution

  • Turns out I was sending the wrong values in my sendmessage.

    Code to count number of icons and then move one icon to a certain x,y position(This will move the first icon in the list to the top left corner position, in my case I have nothing in the spot.

    require 'ffi'
    
    module Win32
      extend FFI::Library
      ffi_lib 'user32'
      ffi_convention :stdcall
    
      enum :lvm, [:LVM_GETITEMCOUNT, 4100,
              :LVM_DELETEALLITEMS, 4105,
              :LVM_GETNEXTITEM, 4108,
              :LVM_SETITEMPOSITION, 4111,
              :LVM_GETITEMPOSITION, 4112,
              :LVM_ARRANGE, 4118,
              :LVM_UPDATE, 4138,
              :LVM_GETITEMTEXTA, 4141,
              :LVM_SETITEMTEXTA, 4142 ]
    
      enum :parameter, [:GW_CHILD, 5,
                        :LVS_ALIGNLEFT, 2048 ]
    
      # Uses C Function to find the window handle(HWND) of a window with the specified text, in this case 'ProgMan'
      attach_function :findWindow, 
        :FindWindowA,[ :string, :parameter ], :int
    
      # Gets passed in window handle's child window 
      attach_function :getChildWindow,
        :GetWindow, [ :ulong, :parameter], :int
    
      # Sends a message to the passed in window  
      attach_function :sendMessage,
        :SendMessageA, [ :ulong, :lvm, :parameter, :long], :int
    
    end # end of Win32
    
    def makeLPARAM(w1, w2)
      return (w2<<16) | w1
    end
    
    # Finds Windows handle for FolderView, SysListView32 which is the icon list
    DesktopHandle = Win32.findWindow('ProgMan', 0)
    DesktopHandle = Win32.getChildWindow(DesktopHandle, :GW_CHILD)
    DesktopHandle = Win32.getChildWindow(DesktopHandle, :GW_CHILD)
    
    # Gets count of icons on desktop
    Win32.sendMessage(DesktopHandle, :LVM_GETITEMCOUNT, 0, 0)
    
    # Moves the first icon in the list to the top leftmost position on the screen
    Win32.sendMessage(DesktopHandle, :LVM_SETITEMPOSITION, 0, makeLPARAM(10, 1))
    

    This was a lot more difficult then I imagined! Although a lot was not having the right values, can anyone point out a place that I should have looked for the values in the first place?