下面是我的代码:
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位。
5条答案
按热度按时间abithluo1#
这里有一个不同的方法来实现这一点-使用UI Automation。它可以跨进程、跨位地为你工作,并且可以针对列表视图、列表框或几乎任何其他标准的Windows UI工作。下面是一个示例应用程序,它将从鼠标指针下的列表视图中获取HWND,并将其中的项转储。它只转储每个项目的名称;如果你愿意的话,我想你可以使用Listviews递归到每一项中的字段。
eqqqjvef2#
我知道这是旧的,但我发现它,而试图解决我的问题,希望这将帮助别人。
我在this question中使用了C++中的建议,并稍微修改了LV_ITEM结构,使其在VB.NET中与64位一起工作(我没有在C#中测试过,但我想解决方案非常相似。
然后,在声明结构的示例时,我使用以下代码在64位和32位结构之间进行选择:
a14dhokn3#
您已经澄清了您正在尝试将32位进程中的列表视图控件中的项读取到另一个64位进程中。
我在各种论坛上看到过许多关于这一主题的问题,但似乎没有一个问题取得成功。
我认为你最好的选择是创建一个32位的可执行文件,它将能够读出其他程序的列表视图。
kd3sttzy4#
如果您的程序是32位的,而目标程序是64位的,那么至少有一个障碍需要克服。或者反过来。LVITEM声明将是错误的,IntPtr具有错误的位数。这使得Marshal.SizeOf()返回错误的值。我想,对齐是好的,偶然的。根据目标程序的位数,将该字段更改为int或long可以修复该问题。您可以通过查看Taskmgr.exe的“进程”选项卡来找到它。如果是32位进程,则进程名称后加“*32”。或者通过设置项目的目标平台设置来匹配目标进程(x86或AnyCPU),从而避免麻烦。
使用Debug + Windows + Memory + Memory1调试此程序。将“lpLocalBuffer”放在Address框中,观察您看到的内容与代码读取的内容。您应该能够从十六进制视图中判断出您正确地获得了字符串。请注意,如果在字符串字符之间看到零,则目标进程使用列表视图的Unicode版本。然后需要Marshal.PtrToStringUnicode来读取它。
yqlxgs2m5#
很抱歉我的回复这么晚,但我刚刚遇到了同样的问题。下面是我在VB.NET中使用的结构,它可以在32位和64位系统上工作。