I am trying to write a python routine to save and restore desktop icon positions. I am using 32-bit python 2.7 on Windows 7 x64. Using information from here (stack exchange), I am able to read the icon names and positions from the foreign process list view that Windows uses to store this info, but I fail when using LVM_SETITEMPOSITION to set new positions (or restore the positions). All of the icons end up in the same exact spot on the desktop. 'Auto arrange' and 'align to grid' are off. The relevant code is towards the bottom. WARNING: if you run this code all of your icons will be in a pile :(
import ctypes
class LVITEMW(ctypes.Structure):
_fields_ = [
('mask', ctypes.c_uint32),
('iItem', ctypes.c_int32),
('iSubItem', ctypes.c_int32),
('state', ctypes.c_uint32),
('stateMask', ctypes.c_uint32),
('pszText', ctypes.c_uint64),
('cchTextMax', ctypes.c_int32),
('iImage', ctypes.c_int32),
('lParam', ctypes.c_uint64), # On 32 bit should be c_long
('iIndent',ctypes.c_int32),
('iGroupId', ctypes.c_int32),
('cColumns', ctypes.c_uint32),
('puColumns', ctypes.c_uint64),
('piColFmt', ctypes.c_int64),
('iGroup', ctypes.c_int32),
]
class POINT(ctypes.Structure):
_fields_ = [('x', ctypes.c_int), ('y', ctypes.c_int)]
def icon_save_restore(savedicons=None, restore=False):
import struct, commctrl, win32gui, win32con, win32api
dthwnd = win32gui.FindWindow(None, 'Program Manager')
ukhwnd = win32gui.GetWindow(dthwnd, win32con.GW_CHILD)
slvhwnd = win32gui.GetWindow(ukhwnd, win32con.GW_CHILD)
pid = ctypes.create_string_buffer(4)
p_pid = ctypes.addressof(pid)
ctypes.windll.user32.GetWindowThreadProcessId(slvhwnd, p_pid)
hProcHnd = ctypes.windll.kernel32.OpenProcess(win32con.PROCESS_ALL_ACCESS, False, struct.unpack("i",pid)[0])
pBuffertxt = ctypes.windll.kernel32.VirtualAllocEx(hProcHnd, 0, 4096, win32con.MEM_RESERVE|win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
copied = ctypes.create_string_buffer(4)
p_copied = ctypes.addressof(copied)
lvitem = LVITEMW()
lvitem.mask = ctypes.c_uint32(commctrl.LVIF_TEXT)
lvitem.pszText = ctypes.c_uint64(pBuffertxt)
lvitem.cchTextMax = ctypes.c_int32(4096)
lvitem.iSubItem = ctypes.c_int32(0)
pLVI = ctypes.windll.kernel32.VirtualAllocEx(hProcHnd, 0, 4096, win32con.MEM_RESERVE| win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
win32api.SetLastError(0)
ctypes.windll.kernel32.WriteProcessMemory(hProcHnd, pLVI, ctypes.addressof(lvitem), ctypes.sizeof(lvitem), p_copied)
num_items = win32gui.SendMessage(slvhwnd, commctrl.LVM_GETITEMCOUNT)
if restore is False:
p = POINT()
pBufferpnt = ctypes.windll.kernel32.VirtualAllocEx(hProcHnd, 0, ctypes.sizeof(p), win32con.MEM_RESERVE|win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
icons = {}
for i in xrange(num_items):
# Get icon text
win32gui.SendMessage(slvhwnd, commctrl.LVM_GETITEMTEXT, i, pLVI)
target_bufftxt = ctypes.create_string_buffer(4096)
ctypes.windll.kernel32.ReadProcessMemory(hProcHnd, pBuffertxt, ctypes.addressof(target_bufftxt), 4096, p_copied)
key = target_bufftxt.value
# Get icon position
win32api.SendMessage(slvhwnd, commctrl.LVM_GETITEMPOSITION, i, pBufferpnt)
p = POINT()
ctypes.windll.kernel32.ReadProcessMemory(hProcHnd, pBufferpnt, ctypes.addressof(p), ctypes.sizeof(p), p_copied)
icons[key] = (i,p)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pLVI, 0, win32con.MEM_RELEASE)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pBuffertxt, 0, win32con.MEM_RELEASE)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pBufferpnt, 0, win32con.MEM_RELEASE)
win32api.CloseHandle(hProcHnd)
return icons
else: # RESTORE ICON POSITIONS PROBLEM IS HERE SOMEWHERE!!!
win32gui.SendMessage(slvhwnd, win32con.WM_SETREDRAW, 0, 0)
for i in xrange(num_items):
# Get icon text
win32gui.SendMessage(slvhwnd, commctrl.LVM_GETITEMTEXT, i, pLVI)
target_bufftxt = ctypes.create_string_buffer(4096)
ctypes.windll.kernel32.ReadProcessMemory(hProcHnd, pBuffertxt, ctypes.addressof(target_bufftxt), 4096, p_copied)
key = target_bufftxt.value
if key in savedicons.keys():
# Set icon position
p = savedicons[key][1] # p is ctypes POINT
p_lng = point_to_long(p) # explicitly convert to HIWORD/LOWORD and c_long
# Reserve space for input variable in foreign process and get pointer to it the that memory space
pBufferpnt = ctypes.windll.kernel32.VirtualAllocEx(hProcHnd, 0, ctypes.sizeof(p_lng), win32con.MEM_RESERVE|win32con.MEM_COMMIT, win32con.PAGE_READWRITE)
# Write the desired coordinates in to the space just created
ret = ctypes.windll.kernel32.WriteProcessMemory(hProcHnd, pBufferpnt, ctypes.addressof(p_lng), ctypes.sizeof(p_lng), p_copied)
if ret == 0:
raise WindowsError
# Send the message to change the position for that item's index and the pointer to the new position
ret = win32gui.SendMessage(slvhwnd, commctrl.LVM_SETITEMPOSITION, i, pBufferpnt)
if ret == 0:
raise WindowsError
# Release the reserved memory for the variable (I recognize that I probably don't need to aLloc/free this within the loop)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pBufferpnt, 0, win32con.MEM_RELEASE)
win32gui.SendMessage(slvhwnd, win32con.WM_SETREDRAW, 1, 0)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pLVI, 0, win32con.MEM_RELEASE)
ctypes.windll.kernel32.VirtualFreeEx(hProcHnd, pBuffertxt, 0, win32con.MEM_RELEASE)
win32api.CloseHandle(hProcHnd)
return None
def point_to_long(p):
ret = (p.y * 0x10000) + (p.x & 0xFFFF)
return ctypes.c_long(ret)
if __name__ == '__main__':
mysavedicons = icon_save_restore(restore=False)
icon_save_restore(mysavedicons, restore=True)
I think there may be a problem with either 1) something to do with 32 bit and 64 bit memory address space, but the other components where I write the LVITEM structure or read the icon text work ok or 2) there is some issue in the way I am converting the coordinate information or calling SendMessage for GETITEMPOSITION. Any insight or help would be greatly appreciated.
Turns out that there is version that uses 32-bit addresses (LVM_SETITEMPOSITION32) that I wish MSDN would have cross-referenced in their documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/bb761194(v=vs.85).aspx
This accepts the POINT structure directly so there is no need to try to convert to HIWORD/LOWORD. Before posting, I did indeed try using a 32 bit shift and longlong (64 bit version of long) and that didn't work either with LVM_SETITEMPOSITION. In any case, with the change, everything works as expected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With