This article will be added to over time.
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-2008 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 thing to do (outside the loop, preferably) is obtain an IShellFolder 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 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;