2011-03-20 277 views
3

這裏是我的代碼:64位中獲取文本從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犯規。

這裏是列表項的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,64位,任何CPU,似乎沒有在所有的工作!

任何想法,爲什麼這可能會發生?

C#+ .net4,windows 7 64bit。

+0

'LV_ITEM'的定義是什麼?你在哪個系統上運行?你能給我們一個我們可以運行的程序嗎? – 2011-03-20 14:48:26

+0

我編輯我的答案與lv_ITEM的定義 – Stacker 2011-03-20 14:54:21

+0

SysListView32是在資源管理器中的權利?它是64位窗口和64位瀏覽器? – 2011-03-20 14:55:58

回答

11

這是另一種做法 - 使用UI Automation。它爲您完成跨進程,跨位的工作,並且可以針對列表視圖,列表框或幾乎任何其他標準Windows用戶界面進行工作。下面是一個示例應用程序,它將從鼠標指針下的列表視圖中獲取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); 
} 
+0

我會嘗試你的建議一旦我用盡希望使用WINAPI – Stacker 2011-03-21 16:03:20

1

您已經闡明,您正嘗試將32位過程中的列表視圖控件中的項目讀取到不同的64位過程中。

我在各種論壇上看到過很多關於這個話題的問題,而且似乎沒有人能夠取得成功的結果。

我認爲你最好的選擇是創建一個32位的可執行文件,它將能夠讀出其他程序的列表視圖。

+0

我試着用32位編譯器編譯軟件,仍然我不能得到預期的結果,有趣的事情是用於與32位Vista的工作相同的代碼。 – Stacker 2011-03-20 15:06:56

1

如果你的程序是32位,目標程序是64位的,至少有一個障礙需要克服。或者相反。 LVITEM聲明將是錯誤的,IntPtr具有錯誤的位數。這使得Marshal.SizeOf()返回錯誤的值。我認爲,無意中調整是可以的。將字段更改爲int或long可以解決問題,具體取決於目標程序的位數。你可以通過查看Taskmgr.exe,Processes選項卡找到它。如果進程名是32位進程,則後綴名爲「* 32」。或者通過設置項目的目標平臺設置來匹配目標進程(x86或AnyCPU),避免麻煩。

使用Debug + Windows + Memory + Memory1進行調試。將「lpLocalBuffer」放在地址框中,觀察你看到的與你的代碼讀取的內容。你一定能夠從十六進制視圖中得知你得到了正確的字符串。請注意,如果您在字符串字符之間看到零,那麼目標進程將使用Unicode版本的列表視圖。 Marshal.PtrToStringUnicode然後需要閱讀它。

+0

即使你的指針寬度正確,我從來沒有見過任何人報告實現這個成功。你有嗎? – 2011-03-20 15:56:47

+0

@大衛 - 從來沒有試圖混合自己,也沒有看到徹底的失敗宣佈。我不知道WOW層試圖處理VirtualAllocEx和Read/WriteProcessMemory的指針值的程度。 SDK文檔不禁止它,並且它們沒有Wow64Xxx版本。如果OP實際上混合了Bit,那麼OP應該首先進入該牆。 – 2011-03-20 16:08:59

+0

我覺得內存方面應該可以讓Windows處理,但是在試圖幫助解決其他SO問題時,我從來沒有能夠實現它。由於OP的目標應用程序是32位,所以在他的應用程序中使用32位是最容易的。 – 2011-03-20 16:12:59

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 
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