开发者

Desktop Icon manipulation - how to get a handle to SysListView32 when themes with picture rotation are enabled

I'm trying to move icons around the desktop, everything works well until a theme that has picture rotation is picked. With a basic Windows 7 theme, the SysListView32 is child of SHELLDLL_DefView which is in turn child of Progman.

But when a picture rotation desktop theme is picked, SysListView32 becomes child of SHELLDLL_DefView which in turn becomes child of WorkerW. There are more than 1. How should I go about finding the right HWND pointing to the right WorkerW. Enumerate all desktop windows and check each one with a classname WorkerW?

[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr GetWindow(IntPtr hWnd, GetWindow_Cmd uCmd);
[DllImport("user32.DLL")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent,IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[Dll开发者_JS百科Import("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

enum GetWindow_Cmd : uint
{
    GW_HWNDFIRST = 0,
    GW_HWNDLAST = 1,
    GW_HWNDNEXT = 2,
    GW_HWNDPREV = 3,
    GW_OWNER = 4,
    GW_CHILD = 5,
    GW_ENABLEDPOPUP = 6
}

In my main() for example, I make the following calls:

IntPtr HWND = FindWindow("Progman",null);
HWND = GetWindow(HWND, GetWindow_Cmd.GW_CHILD);
HWND = GetWindow(HWND, GetWindow_Cmd.GW_CHILD);


Big thanks to Hans for trying this on his machine, and for Sertac for clueing me that SysListView32 changes parents from "Progman" to the "WorkerW" classname. My solution was to first try to find the SysListView32 inside Progman's children:

       hwndIcon = NativeMethods.FindWindow("Progman", null);
       hwndIcon = NativeMethods.FindWindowEx(hwndIcon, IntPtr.Zero, "SHELLDLL_DefView", null);
       hwndIcon = NativeMethods.FindWindowEx(hwndIcon, IntPtr.Zero, "SysListView32", "FolderView");

if hwndIcon returns IntPtr.Zero, I try enumerating all windows under the desktop, then find those whose classname is "WorkerW" (I do this in the delegate GetSysListViewContainer(...)) Amongst the latter, I find "The One And Only One," ie. the one that has a child. That is the one that contains SHELLDLL_DefView, which itself contains SysListView32, which itself contains the handle of each Icon on the desktop:

       hwndIcon = NativeMethods.FindWindow("Progman", null);
       hwndIcon = NativeMethods.FindWindowEx(hwndIcon, IntPtr.Zero, "SHELLDLL_DefView", null);
       hwndIcon = NativeMethods.FindWindowEx(hwndIcon, IntPtr.Zero, "SysListView32", "FolderView");

       if (hwndIcon == IntPtr.Zero)
        {
            IntPtr hDesktop = NativeMethods.GetDesktopWindow();
            IntPtr hwnd = IntPtr.Zero;
            EnumWindowsProc ewp = new EnumWindowsProc(GetSysListViewContainer);
            EnumWindows(ewp, 0);
            hwndIcon = NativeMethods.FindWindowEx(hwndIcon, IntPtr.Zero, "SHELLDLL_DefView", null);
            hwndIcon = NativeMethods.FindWindowEx(hwndIcon, IntPtr.Zero, "SysListView32", "FolderView");
        }

With the following I get a desktop icon count:

       int vItemCount = NativeMethods.SendMessage(hwndIcon, LVM_GETITEMCOUNT, 0, 0);
       string vText;
       int vProcessId = 0;

And with this I loop through all icons:

        NativeMethods.GetWindowThreadProcessId(hwndIcon, ref vProcessId);
        IntPtr vProcess = NativeMethods.OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, vProcessId);
        IntPtr foo = IntPtr.Zero;
        IntPtr vPointer = NativeMethods.VirtualAllocEx(vProcess, IntPtr.Zero, sizeof(uint), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);

            for (int j = 0; j < vItemCount; j++)
            {
                byte[] vBuffer = new byte[256];
                LVITEM[] vItem = new LVITEM[1];
                vItem[0].mask = LVIF_TEXT;
                vItem[0].iItem = j;
                vItem[0].iSubItem = 0;
                vItem[0].cchTextMax = vBuffer.Length;
                vItem[0].pszText = (IntPtr)((int)vPointer + Marshal.SizeOf(typeof(LVITEM)));
                uint vNumberOfBytesRead = 0;
                WriteProcessMemory(vProcess, vPointer, Marshal.UnsafeAddrOfPinnedArrayElement(vItem, 0), Marshal.SizeOf(typeof(LVITEM)), ref vNumberOfBytesRead);
                SendMessage(hwndIcon, LVM_GETITEMW, j, vPointer.ToInt32());
                ReadProcessMemory(vProcess, (IntPtr)((int)vPointer + Marshal.SizeOf(typeof(LVITEM))), Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0), vBuffer.Length, out vNumberOfBytesRead);

                // Get the name of the Icon
                vText = Encoding.Unicode.GetString(vBuffer, 0, (int)vNumberOfBytesRead);

                // Get  Icon location
                SendMessage(hwndIcon, LVM_GETITEMPOSITION, j, vPointer.ToInt32());
                Point[] vPoint = new Point[1];
                foo = Marshal.UnsafeAddrOfPinnedArrayElement(vPoint, 0);
                ReadProcessMemory(vProcess, vPointer, Marshal.UnsafeAddrOfPinnedArrayElement(vPoint, 0), Marshal.SizeOf(typeof(Point)), out vNumberOfBytesRead);

               //and ultimaely move icon.
               SendMessage(hwndIcon, LVM_SETITEMPOSITION, j, lParam[0]);

So to recap, I needed to figure out why I couldn't get a handle for the listview container where all desktop icons are stored in Windows. The original code I had worked well when there was no background rotation, but failed to get the handle of ListSysView32 when there was.

Is there any better way to do this from .Net? kj


This is the ugly and simple way to get a handler to a SysListView32 (C++ code)

HWND hWndLV = ::GetShellWindow();
hWndLV = ::GetNextWindow( ::GetNextWindow(hWndLV, GW_HWNDPREV), GW_HWNDPREV);
hWndLV = ::GetFirstChild(hWndLV);
hWndLV = ::GetNextWindow(hWndLV, GW_HWNDNEXT);
hWndLV = ::GetFirstChild(hWndLV);
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜