从64位的SysListView32获取文本

时间:2011-03-20 14:45:24

标签: c# winapi syslistview32

这是我的代码:

   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的def:

   [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位。

5 个答案:

答案 0 :(得分:12)

这是一种不同的方法 - 使用UI Automation。它为您进行跨进程,跨位工作,并且可以对列表视图,列表框或几乎任何其他标准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);
}

答案 1 :(得分:4)

我知道这已经过时了,但我在尝试解决问题的同时找到了它,并希望这会帮助其他人。

我使用了this question中的推荐,这是在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

答案 2 :(得分:1)

您已经澄清过,您正试图将32位进程中的列表视图控件中的项读入另一个64位进程。

我在各种论坛上都看到过很多关于这个主题的问题,而且似乎没有人能够取得成功。

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

答案 3 :(得分:1)

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

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

答案 4 :(得分: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