See also the excellent article from Raymond Chen: Manipulating the positions of desktop icons
In the Desktop Restore Shell Extension DLL, dkticnsr.dll, this access is trivial because dkticnsr.dll runs in the context of Windows Explorer. This is because it is loaded as a COM object as Explorer is loaded, and runs in response to messages from the Explorer context menu on the Desktop.
The CommandLine version is actually much more complicated than the shell extension, internally, as it is running in its own process. In order to reliably read and write memory in the Desktop Explorer process, shared memory access must be employed. DesktopCmd.exe accomplishes this by way of calls to VirtualAllocEx(), ReadProcessMemory(), and WriteProcessMemory(). For now we'll discuss the techniques used by the Shell Extension, dkticnsr.dll.
The Window names and hierarchy were determined by using the VC++ tool, Windows Spy++:
HWND GetDesktopListView() { HWND hWndPM = FindWindowEx( NULL, NULL, _T("Progman"), NULL ); if (!hWndPM) return NULL; HWND hWnd = FindWindowEx( hWndPM, NULL, _T("SHELLDLL_DefView"), NULL ); if (!hWnd) { // Under Win2K this seems to move around, and there can be many WorkerW windows EnumWindows( FindDLV, LPARAM((HWND*)&hWnd) ); if (!hWnd) return NULL; } HWND hWndLV = FindWindowEx( hWnd, NULL, _T("SysListView32"), NULL ); return hWndLV; }
//////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK FindDLV( HWND hWndPM, LPARAM lParam ) {
HWND hWnd = FindWindowEx( hWndPM, NULL, _T("SHELLDLL_DefView"), NULL ); if (hWnd) { // This is our window! HWND* phWnd = (HWND*)lParam; *phWnd = hWnd;
return FALSE; // all done } return TRUE; // keep looking }
//////////////////////////////////////////////////////////////////////////////////////
LVITEM lvi; POINT pt; _TCHAR szBuf[ _MAX_PATH ]; for (int ix = ListView_GetNextItem( hWndLV, -1, LVNI_ALL ); ix != -1; ix = ListView_GetNextItem( hWndLV, ix, LVNI_ALL )) { ZeroMemory( &lvi, sizeof( LVITEM ) ); szBuf[ 0 ] = _T('\0'); lvi.iItem = ix; lvi.mask = LVIF_TEXT | LVIF_PARAM; lvi.pszText = szBuf; lvi.cchTextMax = MAX_PATH; if (!ListView_GetItem( hWndLV, &lvi )) continue; ZeroMemory( &pt, sizeof( POINT ) ); if (!ListView_GetItemPosition( hWndLV, ix, &pt )) continue; // ... }
Copyright © 1995-2020 by Jamie O'Connell. All rights reserved. |
email: webmaster@_REMOVE_midiox.com |
This page was last modified on 01-11-08 |
Following is some code I use to determine this and obtain the Shell Item ID (SHITEMID) for the item. The first thi COM pointer to the Desktop folder:
// ... in loop // See if it's a normal file or dir LPCITEMIDLIST pidl = LPCITEMIDLIST(lvi.lParam); if (pidl == NULL) // then this is Vista or above { // In this case, save in the registry the old way; Name - Value: encoded DWORD // Code ommitted... continue; // after saving just loop to the next icon } CString strValName; WIN32_FIND_DATA fd = { 0 }; HRESULT hr = SHGetDataFromIDList( pSHDesk, pidl, SHGDFIL_FINDDATA, &fd, sizeof( WIN32_FIND_DATA ) ); if (SUCCEEDED( hr )) { strValName = fd.cFileName; } else { // No, see if it's a Registered desktop item with a Guid SHDESCRIPTIONID dd = { 0 }; hr = SHGetDataFromIDList( pSHDesk, pidl, SHGDFIL_DESCRIPTIONID, &dd, sizeof( SHDESCRIPTIONID ) ); if (SUCCEEDED( hr )) { strValName.FromGUID( dd.clsid ); } } if (!SUCCEEDED( hr ) || strValName.IsEmpty()) continue; // Failure // ... Now you can just store the obtained information: Item ID, Name, Position
void CString::FromGUID( const GUID& guid ) { Format( _T("{%-08.8X-%-04.4X-%-04.4X-%-02.2X%-02.2X-%02.2X%-02.2X%-02.2X%-02.2X%-02.2X%-02.2X}"), guid.Data1, guid.Data2, guid.Data3, guid.Data4[ 0 ], guid.Data4[ 1 ], guid.Data4[ 2 ], guid.Data4[ 3 ], guid.Data4[ 4 ], guid.Data4[ 5 ], guid.Data4[ 6 ], guid.Data4[ 7 ] ); }
Then, inside the loop, after retrieving the Item and Name, you can query the shell for the object. Note that the and Name, you can query the shell for the object. Note that the CString class in the code is not the MFC CString (this applet does not use MFC), but a custom class I have crafted for my needs. In particular, the FromGUID() member is custom and will be listed below:
CComPtr<IShellFolder> pSHDesk; if (!SUCCEEDED( SHGetDesktopFolder( &pSHDesk ) )) return;