.net 从64位SysListView32获取文本

ht4b089n  于 2023-06-07  发布在  .NET
关注(0)|答案(5)|浏览(323)

下面是我的代码:

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没有。
下面是LV_ITEM的定义:

[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;
}

我试过编译x86,x64,任何CPU,似乎没有工作在所有!
知道为什么会这样吗
C# + .NET 4,Windows 7 64位。

abithluo

abithluo1#

这里有一个不同的方法来实现这一点-使用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);
}
eqqqjvef

eqqqjvef2#

我知道这是旧的,但我发现它,而试图解决我的问题,希望这将帮助别人。
我在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
a14dhokn

a14dhokn3#

您已经澄清了您正在尝试将32位进程中的列表视图控件中的项读取到另一个64位进程中。
我在各种论坛上看到过许多关于这一主题的问题,但似乎没有一个问题取得成功。
我认为你最好的选择是创建一个32位的可执行文件,它将能够读出其他程序的列表视图。

kd3sttzy

kd3sttzy4#

如果您的程序是32位的,而目标程序是64位的,那么至少有一个障碍需要克服。或者反过来。LVITEM声明将是错误的,IntPtr具有错误的位数。这使得Marshal.SizeOf()返回错误的值。我想,对齐是好的,偶然的。根据目标程序的位数,将该字段更改为int或long可以修复该问题。您可以通过查看Taskmgr.exe的“进程”选项卡来找到它。如果是32位进程,则进程名称后加“*32”。或者通过设置项目的目标平台设置来匹配目标进程(x86或AnyCPU),从而避免麻烦。
使用Debug + Windows + Memory + Memory1调试此程序。将“lpLocalBuffer”放在Address框中,观察您看到的内容与代码读取的内容。您应该能够从十六进制视图中判断出您正确地获得了字符串。请注意,如果在字符串字符之间看到零,则目标进程使用列表视图的Unicode版本。然后需要Marshal.PtrToStringUnicode来读取它。

yqlxgs2m

yqlxgs2m5#

很抱歉我的回复这么晚,但我刚刚遇到了同样的问题。下面是我在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

相关问题