Reputation: 31
I'm trying to make a program which can remember the location of desktop icons and restore them to the right location.
Now I'm having some real problems with this piece of code (I got this code here: https://social.msdn.microsoft.com/Forums/windows/en-US/d7df8a4d-fc0f-4b62-80c9-7768756456e6/how-can-i-get-desktops-icons-information-):
private void button1_Click(object sender, EventArgs e)
{
// get the handle of the desktop listview
IntPtr vHandle = FindWindow("Progman", "Program Manager");
vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SysListView32", "FolderView");
// get total count of the icons on the desktop
int vItemCount = SendMessage(vHandle, LVM_GETITEMCOUNT, 0, 0);
this.label1.Text = vItemCount.ToString();
uint vProcessId;
GetWindowThreadProcessId(vHandle, out vProcessId);
IntPtr vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, vProcessId);
IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, 4096,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
try
{
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(vHandle, LVM_GETITEMW, j, vPointer.ToInt32());
ReadProcessMemory(vProcess,
(IntPtr)((int)vPointer + Marshal.SizeOf(typeof(LVITEM))),
Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0),
vBuffer.Length, ref vNumberOfBytesRead);
string vText = Encoding.Unicode.GetString(vBuffer, 0, (int)vNumberOfBytesRead);
string IconName = vText;
// get icon location
SendMessage(vHandle, LVM_GETITEMPOSITION, j, vPointer.ToInt32());
Point[] vPoint = new Point[1];
ReadProcessMemory(vProcess, vPointer, Marshal.UnsafeAddrOfPinnedArrayElement(vPoint, 0),
Marshal.SizeOf(typeof(Point)), ref vNumberOfBytesRead);
string IconLocation = vPoint[0].ToString();
// insert an item into the ListView
this.listView1.Items.Add(new ListViewItem(new
string[]{IconName,IconLocation}));
}
}
finally
{
VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
CloseHandle(vProcess);
}
this.listView1.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnContent);
}
This piece of C# code should be able to get the text and potitions of all dekstop icons. The problem is that I can't get the correct text with this code. The text of a desktop icon is retrieved here:
string vText = Encoding.Unicode.GetString(vBuffer, 0, (int)vNumberOfBytesRead);
string IconName = vText;
Somehow the text is not correctly retrieved here. vBuffer has no value in all of it's 256 bytes.
So now I came up with a solution with the use of pointers and rewrote the code above like this:
private void button1_Click(object sender, EventArgs e)
{
// get the handle of the desktop listview
IntPtr vHandle = FindWindow("Progman", "Program Manager");
vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SysListView32", "FolderView");
// get total count of the icons on the desktop
int vItemCount = SendMessage(vHandle, LVM_GETITEMCOUNT, 0, 0);
this.label1.Text = vItemCount.ToString();
uint vProcessId;
GetWindowThreadProcessId(vHandle, out vProcessId);
IntPtr vProcess = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false, vProcessId);
IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
try
{
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(vHandle, LVM_GETITEMW, j, vPointer.ToInt32());
unsafe
{
// IntPtr baseaddress = n;
int nsize = (int)((LVITEM*)vPointer)->cchTextMax;
IntPtr baseaddress = (IntPtr)((LVITEM*)vPointer)->pszText;
ReadProcessMemory(vProcess, baseaddress, Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0), nsize, ref vNumberOfBytesRead);
}
string vText = Encoding.Unicode.GetString(vBuffer, 0, (int)vNumberOfBytesRead);
string IconName = vText;
// get icon location
SendMessage(vHandle, LVM_GETITEMPOSITION, j, vPointer.ToInt32());
Point[] vPoint = new Point[1];
ReadProcessMemory(vProcess, vPointer,
Marshal.UnsafeAddrOfPinnedArrayElement(vPoint, 0),
Marshal.SizeOf(typeof(Point)), ref vNumberOfBytesRead);
string IconLocation = vPoint[0].ToString();
// insert an item into the ListView
this.listView1.Items.Add(new ListViewItem(new string[]{IconName,IconLocation}));
}
}
finally
{
VirtualFreeEx(vProcess, vPointer, 0, MEM_RELEASE);
CloseHandle(vProcess);
}
}
The problem is now that when I run my code I get the following error:
" Attempted to read or write protected memory. This is often an indication that other memory is corrupt"
So this does not seem to work either. Does somebody know how I succesfully can get the text of each desktop item? And I never used pointers before so i think i might have done something wrong. Does somebody see what goes wrong in the code above?
Upvotes: 2
Views: 1502
Reputation: 33
I know this is an old question, but since people will still find this question and I could find little to none conclusive information elsewhere I would like to provide the answer I came up with.
The missing element in the original code is that the LVITEM.pszText
is a pointer to text, but the text at the pointer's address isn't fetched from the virtual buffer.
In my code below I tried to use no unsafe code. You will find all the methods I encapsulated in the NativeMethods class as DLLImports via the search engine of your choice. The used LVITEMA
structure can be found at the Win32 api documentation and implemented below.
public void GetDesktopIcons()
{
// This buffer size will be enough.
const int BUFFER_SIZE = 0x110;
// Find window handles via process names.
IntPtr vHandle = FindWindow("Progman", "Program Manager");
vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SHELLDLL_DefView", null);
vHandle = FindWindowEx(vHandle, IntPtr.Zero, "SysListView32", "FolderView");
// TODO: Do error handling.
if (vHandle == IntPtr.Zero) {
return;
}
// Count subwindows of desktop => count of icons.
int vIconCount = (int) NativeMethods.SendMessage(desktopWindowHandle, NativeMethods.LVM.GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
// Get the desktop window's process to enumerate child windows.
NativeMethods.GetWindowThreadProcessId(desktopWindowHandle, out uint vProcessId);
IntPtr vProcess = NativeMethods.OpenProcess(NativeMethods.PROCESS_VM.OPERATION | NativeMethods.PROCESS_VM.READ | NativeMethods.PROCESS_VM.WRITE, false, vProcessId);
// Allocate memory in the desktop process.
IntPtr vPointer = VirtualAllocEx(vProcess, IntPtr.Zero, new UintPtr(BUFFER_SIZE), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// Initialize loop variables.
NativeMethods.LVITEMA currentDesktopIcon = new NativeMethods.LVITEMA();
byte[] vBuffer = new byte[BUFFER_SIZE];
uint bytesRead = 0;
// Instantiate an item to write to the remote buffer and be filled out there.
NativeMethods.LVITEMA[] remoteBufferDesktopIcon = new NativeMethods.LVITEMA[1];
// Initialize basic structure.
// We want to get the icon's text, so set the mask accordingly.
remoteBufferDesktopIcon[0].mask = NativeMethods.LVIF.TEXT;
// Set maximum text length to buffer length minus offset used in pszText.
remoteBufferDesktopIcon[0].cchTextMax = vBuffer.Length - Marshal.SizeOf(typeof(NativeMethods.LVITEMA));
// Set pszText at point after this structure in the remote process's buffer.
remoteBufferDesktopIcon[0].pszText = (IntPtr) ((int) bufferPointer + Marshal.SizeOf(typeof(NativeMethods.LVITEMA)));
try
{
// Loop through available desktop icons.
for (int i = 0; i < iconCount; i++)
{
remoteBufferDesktopIcon[0].iItem = i;
// Write to desktop process the structure we want to get.
NativeMethods.WriteProcessMemory(desktopProcessHandle, bufferPointer, Marshal.UnsafeAddrOfPinnedArrayElement(remoteBufferDesktopIcon, 0), new UIntPtr((uint) Marshal.SizeOf(typeof(NativeMethods.LVITEMA))), ref bytesRead);
// Get i-th item of desktop and read its memory.
NativeMethods.SendMessage(desktopWindowHandle, NativeMethods.LVM.GETITEMW, new IntPtr(i), bufferPointer);
NativeMethods.ReadProcessMemory(desktopProcessHandle, bufferPointer, Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0), new UIntPtr((uint) Marshal.SizeOf(currentDesktopIcon)), ref bytesRead);
// TODO: Error handling. This error is really unlikely.
if (bytesRead != Marshal.SizeOf(currentDesktopIcon))
{
throw new Exception("Read false amount of bytes.");
}
// Get actual struct filled from buffer.
currentDesktopIcon = Marshal.PtrToStructure<NativeMethods.LVITEMA>(Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0));
// Use the now set pszText pointer to read the icon text into the buffer. Maximum length is 260, more characters won't be displayed.
NativeMethods.ReadProcessMemory(desktopProcessHandle, currentDesktopIcon.pszText, Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer, 0), new UIntPtr(260), ref bytesRead);
// Read from buffer into string with unicode encoding, then trim string.
currentIconTitle = Encoding.Unicode.GetString(vBuffer, 0, (int) bytesRead);
currentIconTitle = currentIconTitle.Substring(0, currentIconTitle.IndexOf('\0'));
// TODO: Do something with the icon title.
}
}
finally
{
// Clean up unmanaged memory.
NativeMethods.VirtualFreeEx(vProcess, bufferPointer, UIntPtr.Zero, NativeMethods.MEM.RELEASE);
NativeMethods.CloseHandle(vProcess);
}
Here is the LVITEMA
struct I used in NativeMethods:
[StructLayout(LayoutKind.Sequential)]
internal struct LVITEMA
{
public int mask;
public int iItem;
public int iSubItem;
public int state;
public int stateMask;
public IntPtr pszText;
public int cchTextMax;
public int iImage;
public IntPtr lParam;
public int iIndent;
public uint iGroupId;
public UIntPtr puColumns;
}
Upvotes: 2