如何使用python 2.7移动Windows桌面图标?

时间:2015-02-13 17:58:03

标签: windows python-2.7 icons ctypes listviewitem

我正在尝试编写一个python例程来保存和恢复桌面图标位置。我在Windows 7 x64上使用32位python 2.7。使用此处的信息(堆栈交换),我能够从外部进程列表视图中读取Windows用于存储此信息的图标名称和位置,但是在使用LVM_SETITEMPOSITION设置新位置(或恢复位置)时失败。所有图标最终都在桌面上的相同位置。 '自动安排'和'对齐网格'关了。相关代码是最底层的。警告:如果您运行此代码,您的所有图标都将堆叠在一起:(

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)

我认为可能存在以下问题:1)与32位和64位内存地址空间有关,但是我编写LVITEM结构或读取图标文本的其他组件工作正常或2)有一些我转换坐标信息或调用SendMessage for GETITEMPOSITION的方式问题。任何见解或帮助将不胜感激。

1 个答案:

答案 0 :(得分:1)

事实证明,有一个版本使用32位地址(LVM_SETITEMPOSITION32),我希望MSDN在其文档中交叉引用:https://msdn.microsoft.com/en-us/library/windows/desktop/bb761194(v=vs.85).aspx

它直接接受POINT结构,因此无需尝试转换为HIWORD / LOWORD。在发布之前,我确实尝试使用32位移位和longlong(64位版本的long),并且使用LVM_SETITEMPOSITION不起作用。无论如何,随着变化,一切都按预期工作。