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);
[DllImport("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