我正在尝试制作一个程序,它可以记住桌面图标的位置并将它们恢复到正确的位置。
现在我对这段代码遇到了一些真正的问题(我在这里得到了这段代码: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);
}
这段C#代码应该能够获取所有桌面图标的文本和位置。问题是我无法使用此代码获得正确的文本。桌面图标的文本在此处检索:
string vText = Encoding.Unicode.GetString(vBuffer, 0, (int)vNumberOfBytesRead);
string IconName = vText;
不知何故,此处未正确检索文本。 vBuffer 的 256 字节没有任何值(value)。
所以现在我想出了一个使用指针的解决方案,并重写了上面的代码,如下所示:
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);
}
}
现在的问题是,当我运行代码时,出现以下错误:
“尝试读取或写入 protected 内存。这通常表明其他内存已损坏”
所以这似乎也不起作用。有人知道我如何成功获取每个桌面项目的文本吗? 而且我以前从未使用过指针,所以我想我可能做错了什么。有人看出上面的代码出了什么问题吗?
最佳答案
我知道这是一个老问题,但由于人们仍然会找到这个问题,而我在其他地方几乎找不到结论性信息,所以我想提供我想出的答案。
原始代码中缺少的元素是LVITEM.pszText
是一个指向文本的指针,但指针地址处的文本不是从虚拟缓冲区中获取的。
在下面的代码中,我尝试不使用不安全的代码。您将通过您选择的搜索引擎找到我封装在 NativeMethods 类中作为 DLLImports 的所有方法。使用的LVITEMA
结构可以在 at the Win32 api documentation 找到并在下面实现。
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);
}
这是我在 NativeMethods 中使用的 LVITEMA
结构:
[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;
}
关于c# - 获取所有Windows桌面快捷方式的文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30369537/