Getting Text from SysListView32 in 64bit
here is my code :
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.开发者_如何学JAVAPtrToStringAnsi((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;
}
no matter what i do retval returns empty, although lpLocalBuffer doesnt .
here is the def of 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;
}
i tried compiling for 86x , 64bit, any cpu , nothing seems to work at all !
any idea why this might be happening ?
C# + .net4 , windows 7 64bit.
Here's a different approach to doing this - use UI Automation. It does the cross-process, cross-bitness work for you, and will work against listviews, listboxes, or pretty much any other standard Windows UI. Here's a sample app that will get the HWND from the listview under the mouse pointer, and dump the items in it. It dumps just the name of each item; with Listviews, I think you can recurse into the fields in each item if you want.
// 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);
}
I know this is old, but I found it while trying to solve my problem and hopefully this will help someone else.
I used the recommendation in this question, that was in C++, and slightly modified the LV_ITEM structure to make it work with 64bit in VB.NET (I haven't tested in C# but I imagine the solution is quite similar.)
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
Then, when declaring the instance of the structure, I used the following code to choose between 64 bit and 32 bit structures:
Dim lvi As Object
If IntPtr.Size = 4 Then
lvi = New LV_ITEM
Else
lvi = New LV_ITEM64
End If
You have clarified that you are trying to read items from a list view control in a 32 bit process into a different 64 bit process.
I have seen many questions on this topic in various forums and not one ever seemed to achieve a successful outcome.
I think your best option is to create a 32 bit executable which will be able to read out of the other program's list view.
There is at least one obstacle to overcome if your program is 32-bit and the target program is 64-bit. Or the other way around. The LVITEM declaration will be wrong, IntPtr has the wrong number of bits. Which makes Marshal.SizeOf() return the wrong value. Alignment is okay, I think, by accident. Changing the field to either int or long can fix the problem, depending on the bitness of the target program. Which you can find out by looking at the Taskmgr.exe, Processes tab. The process name is post-fixed with "*32" if it is a 32-bit process. Or simply stay out of trouble by setting your project's Target platform setting to match the target process (x86 or AnyCPU).
Debug this by using Debug + Windows + Memory + Memory1. Put "lpLocalBuffer" in the Address box and observe what you see vs what your code reads. You should definitely be able to tell from the hex view that you got the string properly. Note that if you see zeros between the string characters then the target process uses the Unicode version of the list view. Marshal.PtrToStringUnicode is then required to read it.
Sorry my response is so late but I just came across the same issue. Here is the structure I used for VB.NET which works on both 32 and 64 bit systems.
<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
精彩评论