c# - 获取所有Windows桌面快捷方式的文本

标签 c# windows desktop shortcut

我正在尝试制作一个程序,它可以记住桌面图标的位置并将它们恢复到正确的位置。

现在我对这段代码遇到了一些真正的问题(我在这里得到了这段代码: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/

相关文章:

c# - 如何在 .NET 中创建词典列表?

c# - 在 C# 中,许多 namespace (如类的嵌套)对性能有影响吗?

c# - 使用 MVC 设计模式编写点网桌面应用程序

c++ - 从 C++ 应用程序显示/隐藏桌面图标

windows - 哪个是最快的TAR应用程序?

JAR 中的 Java Desktop.open(File f) 引用文件?

c# - 在 XAML 中访问类变量

c# - 如何在内存中创建一个文件,然后通过asp.net mvc + jquery将该文件发送给用户?

c++ - 无框架 Qt 窗口 (win32) 中错误的窗口绘制行为

node.js - 如何通过终端(ubuntu 或 windows)查找 google chrome 或 firefox tab url?