3

这是我的代码:

   public static string ReadListViewItem(IntPtr lstview, int item)
    {
        const int dwBufferSize = 1024;

        int dwProcessID;
        LV_ITEM lvItem;
        string retval;
        bool bSuccess;
        IntPtr hProcess = IntPtr.Zero;
        IntPtr lpRemoteBuffer = IntPtr.Zero;
        IntPtr lpLocalBuffer = IntPtr.Zero;
        IntPtr threadId = IntPtr.Zero;

        try
        {
            lvItem = new LV_ITEM();
            lpLocalBuffer = Marshal.AllocHGlobal(dwBufferSize);
            // Get the process id owning the window
            threadId = GetWindowThreadProcessId(lstview, out dwProcessID);
            if ((threadId == IntPtr.Zero) || (dwProcessID == 0))
                throw new ArgumentException("hWnd");

            // Open the process with all access
            hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, dwProcessID);
            if (hProcess == IntPtr.Zero)
                throw new ApplicationException("Failed to access process");

            // Allocate a buffer in the remote process
            lpRemoteBuffer = VirtualAllocEx(hProcess, IntPtr.Zero, dwBufferSize, MEM_COMMIT,
              PAGE_READWRITE);
            if (lpRemoteBuffer == IntPtr.Zero)
                throw new SystemException("Failed to allocate memory in remote process");

            // Fill in the LVITEM struct, this is in your own process
            // Set the pszText member to somewhere in the remote buffer,
            // For the example I used the address imediately following the LVITEM stuct
            lvItem.mask = LVIF_TEXT;

            lvItem.iItem = item;
            lvItem.iSubItem = 2;
            lvItem.pszText = (IntPtr)(lpRemoteBuffer.ToInt32() + Marshal.SizeOf(typeof(LV_ITEM)));
            lvItem.cchTextMax = 50;

            // Copy the local LVITEM to the remote buffer
            bSuccess = WriteProcessMemory(hProcess, lpRemoteBuffer, ref lvItem,
              Marshal.SizeOf(typeof(LV_ITEM)), IntPtr.Zero);
            if (!bSuccess)
                throw new SystemException("Failed to write to process memory");

            // Send the message to the remote window with the address of the remote buffer
            SendMessage(lstview, LVM_GETITEMText, 0, lpRemoteBuffer);

            // Read the struct back from the remote process into local buffer
            bSuccess = ReadProcessMemory(hProcess, lpRemoteBuffer, lpLocalBuffer, dwBufferSize,IntPtr.Zero);
            if (!bSuccess)
                throw new SystemException("Failed to read from process memory");

            // At this point the lpLocalBuffer contains the returned LV_ITEM structure
            // the next line extracts the text from the buffer into a managed string
            retval = Marshal.PtrToStringAnsi((IntPtr)(lpLocalBuffer +
              Marshal.SizeOf(typeof(LV_ITEM))));
        }
        finally
        {
            if (lpLocalBuffer != IntPtr.Zero)
                Marshal.FreeHGlobal(lpLocalBuffer);
            if (lpRemoteBuffer != IntPtr.Zero)
                VirtualFreeEx(hProcess, lpRemoteBuffer, 0, MEM_RELEASE);
            if (hProcess != IntPtr.Zero)
                CloseHandle(hProcess);
        }
        return retval;
    }

无论我做什么 retval 都返回空,尽管 lpLocalBuffer 没有。

这是 ListItem 的定义:

   [StructLayout(LayoutKind.Sequential)]
    private struct LV_ITEM
    {
        public int mask;
        public int iItem;
        public int iSubItem;
        public int state;
        public int stateMask;
        public IntPtr pszText;
        public int cchTextMax;
        public int iImage;
        internal int lParam;
        internal int iIndent;
    }

我尝试为 86x、64bit、任何 cpu 编译,似乎根本没有任何效果!

知道为什么会发生这种情况吗?

C# + .net4 ,Windows 7 64 位。

4

5 回答 5

13

这是执行此操作的另一种方法 - 使用UI 自动化。它为您执行跨进程、跨位的工作,并且将针对列表视图、列表框或几乎任何其他标准 Windows UI 工作。这是一个示例应用程序,它将从鼠标指针下的列表视图中获取 HWND,并转储其中的项目。它只转储每个项目的名称;使用 Listviews,我认为您可以根据需要递归到每个项目中的字段。

// Compile using: csc ReadListView.cs /r:UIAutomationClient.dll

using System;
using System.Windows.Automation;
using System.Runtime.InteropServices;

class ReadListView
{
    public static void Main()
    {
        Console.WriteLine("Place pointer over listview and hit return...");
        Console.ReadLine();

        // Get cursor position, then the window handle at that point...
        POINT pt;
        GetCursorPos(out pt);
        IntPtr hwnd = WindowFromPoint(pt);

        // Get the AutomationElement that represents the window handle...
        AutomationElement el = AutomationElement.FromHandle(hwnd);

        // Walk the automation element tree using content view, so we only see
        // list items, not scrollbars and headers. (Use ControlViewWalker if you
        // want to traverse those also.)
        TreeWalker walker = TreeWalker.ContentViewWalker;
        int i = 0;
        for( AutomationElement child = walker.GetFirstChild(el) ;
            child != null; 
            child = walker.GetNextSibling(child) )
        {
            // Print out the type of the item and its name
            Console.WriteLine("item {0} is a \"{1}\" with name \"{2}\"", i++, child.Current.LocalizedControlType, child.Current.Name);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    };

    [DllImport("user32.dll")]
    private static extern IntPtr WindowFromPoint(POINT pt);

    [DllImport("user32.dll")]
    private static extern int GetCursorPos(out POINT pt);
}
于 2011-03-21T09:06:11.643 回答
4

我知道这很旧,但我在尝试解决我的问题时发现了它,希望这对其他人有帮助。

我在这个问题中使用了C++ 中的建议,并稍微修改了 LV_ITEM 结构以使其在 VB.NET 中使用 64 位(我没有在 C# 中测试过,但我想解决方案非常相似。)

Public Structure LV_ITEM64

    Public mask As Integer
    Public iItem As Integer
    Public iSubItem As Integer
    Public state As Integer
    Public stateMask As Integer
    Public placeholder1 As Integer
    Public pszText As Integer
    Public placeholder2 As Integer
    Public cchTextMax As Integer
    Public iImage As Integer

End Structure

然后,在声明结构的实例时,我使用以下代码在 64 位和 32 位结构之间进行选择:

Dim lvi As Object

If IntPtr.Size = 4 Then
    lvi = New LV_ITEM
Else
    lvi = New LV_ITEM64
End If
于 2012-08-28T22:43:18.037 回答
1

您已澄清您正在尝试将 32 位进程中的列表视图控件中的项目读取到不同的 64 位进程中。

我在各种论坛上看到了很多关于这个主题的问题,但似乎没有一个取得成功。

我认为您最好的选择是创建一个 32 位可执行文件,该可执行文件将能够读取其他程序的列表视图。

于 2011-03-20T15:05:35.160 回答
1

如果您的程序是 32 位而目标程序是 64 位,那么至少有一个障碍需要克服。或者反过来。LVITEM 声明将是错误的,IntPtr 的位数错误。这使得 Marshal.SizeOf() 返回错误的值。对齐是好的,我认为,偶然。将字段更改为 int 或 long 可以解决问题,具体取决于目标程序的位数。您可以通过查看 Taskmgr.exe 的“进程”选项卡来找到它。如果是 32 位进程,则进程名称后缀为“*32”。或者通过设置项目的目标平台设置以匹配目标进程(x86 或 AnyCPU)来避免麻烦。

使用 Debug + Windows + Memory + Memory1 对此进行调试。将“lpLocalBuffer”放在地址框中,观察您看到的内容与您的代码读取的内容。您绝对应该能够从十六进制视图中看出您正确获取了字符串。请注意,如果您在字符串字符之间看到零,则目标进程使用列表视图的 Unicode 版本。然后需要 Marshal.PtrToStringUnicode 来读取它。

于 2011-03-20T15:35:22.010 回答
0

抱歉,我的回复太晚了,但我刚刚遇到了同样的问题。这是我用于 VB.NET 的结构,它适用于 32 位和 64 位系统。

<StructLayout(LayoutKind.Sequential, Pack:=1)> _
Public Structure LV_ITEM
    Public Mask As UInteger
    Public Index As Integer
    Public SubIndex As Integer
    Public State As Integer
    Public StateMask As IntPtr
    Public Text As String
    Public TextLength As Integer
    Public ImageIndex As Integer
    Public LParam As IntPtr
End Structure
于 2013-12-12T12:20:44.527 回答