Getting text from SysListView32 in 64 bit

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

no matter what i do, retval returns empty, although lpLocalBuffer does not.

here is the def from 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 processor, nothing works at all!

any idea why this could be happening?

C # + .net4, windows 7 64bit.

+3
source share
5 answers

Here's another approach to this - use UI automation . It does cross-process, cross-bit works for you, and will work against listviews, listboxes, or just about any other standard Windows user interface. Here is an example of an application that will get HWND from the list under the mouse pointer and dump elements in it. It discards only the name of each element; with Listviews, I think you can rewrite fields in each element 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); } 
+11
source

I know this is old, but I found it trying to solve my problem and hopefully it helps someone else.

I used the recommendation in this question , which was in C ++, and slightly changed the LV_ITEM structure to make it work with 64-bit in VB.NET (I haven’t tested it in C #, but I think the solution is very 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, declaring an 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 
+4
source

You have found that you are trying to read items from a list view control in a 32-bit process to another 64-bit process.

I have seen many questions on this topic in various forums, and none of them have ever achieved a successful result.

I think your best option is to create a 32-bit executable that can read from another list of program list.

+1
source

There is at least one hurdle to overcome if your program is 32-bit and the target program is 64-bit. Or vice versa. The LVITEM declaration will be incorrect, IntPtr has the wrong number of bits. What makes Marshal.SizeOf () return the wrong value. Agree, I think, by chance. Changing the field to int or long can fix the problem, depending on the bit size of the target program. You can find this out by looking at the Processes tab of Taskmgr.exe. The process name is fixed with "* 32" if it is a 32-bit process. Or just stay away from problems by setting your target platform target parameter according to the target process (x86 or AnyCPU).

Debug this using Debug + Windows + Memory + Memory1. Put "lpLocalBuffer" in the "Address" field and watch what you see and what your code is reading. You should definitely determine from the hexadecimal representation that you received the string correctly. Please note: if you see zeros between string characters, then the target process uses the Unicode version of the list. Marshal.PtrToStringUnicode then needs to be read.

+1
source

Sorry, my answer is so late, but I just ran into the same problem. 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 
0
source

All Articles