需求:獲取其他程式中的ListView控制元件的文字內容

努力的藍精靈發表於2013-10-17


原理程式之間是相互隔離的,資料是不能共享的(有些特例)

   LVM_GETTITEMTEXT:將一個資料緩衝區提供給listview32控制元件,你不能把你的程式的資料緩衝提供給另外的程式,所以要用openprocess 開啟“工作管理員”程式, 然後   在“工作管理員程式”中申請足夠長度的記憶體(VirualAllocEx),將這個記憶體地址提供給listview32,使用 sendmessage 傳送LVM_GETTITEMTEXT訊息,待sendmessage返    回後,用readprocessmemory 讀取這段記憶體的資料,即listview控制元件的文字內容

API函式

 

  • FindWindow   //查詢視窗
  • FindWindowEx ////在視窗列表中尋找與指定條件相符的第一個子視窗
  • SendMessage
  • GetWindowThreadProcessId//找出某個視窗的建立者(執行緒或程式),返回建立者的標誌符
  • OpenProcess         //開啟一個已存在的程式物件,並返回程式的控制程式碼
  • VirtualAllocEx //為指定的程式分配記憶體地址:成功則返回分配記憶體的首地址
  • ReadProcessMemory //從指定記憶體中讀取位元組集資料
  • WriteProcessMemory //將資料寫入記憶體中
  • CloseHandle
  • VirtualFreeEx //在其它程式中釋放申請的虛擬記憶體空間

 

程式演示

程式碼

 

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Runtime.InteropServices; namespace 讀取其他軟體listview控制元件的內容 { public partial class Form1 : Form { int hwnd; //視窗控制程式碼 int process;//程式控制程式碼 int pointer; private const uint LVM_FIRST = 0x1000; private const uint LVM_GETHEADER = LVM_FIRST + 31; private const uint LVM_GETITEMCOUNT = LVM_FIRST + 4;//獲取列表行數 private const uint LVM_GETITEMTEXT = LVM_FIRST + 45;//獲取列表內的內容 private const uint LVM_GETITEMW = LVM_FIRST + 75; private const uint HDM_GETITEMCOUNT = 0x1200;//獲取列表列數 private const uint PROCESS_VM_OPERATION = 0x0008;//允許函式VirtualProtectEx使用此控制程式碼修改程式的虛擬記憶體 private const uint PROCESS_VM_READ = 0x0010;//允許函式訪問許可權 private const uint PROCESS_VM_WRITE = 0x0020;//允許函式寫入許可權 private const uint MEM_COMMIT = 0x1000;//為特定的頁面區域分配記憶體中或磁碟的頁面檔案中的物理儲存 private const uint MEM_RELEASE = 0x8000; private const uint MEM_RESERVE = 0x2000;//保留程式的虛擬地址空間,而不分配任何物理儲存 private const uint PAGE_READWRITE = 4; private int LVIF_TEXT = 0x0001; [DllImport("user32.dll")]//查詢視窗 private static extern int FindWindow( string strClassName, //視窗類名 string strWindowName //視窗標題 ); [DllImport("user32.dll")]//在視窗列表中尋找與指定條件相符的第一個子視窗 private static extern int FindWindowEx( int hwndParent, // handle to parent window    int hwndChildAfter, // handle to child window string className, //視窗類名 string windowName // 視窗標題 ); [DllImport("user32.DLL")] private static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam); [DllImport("user32.dll")]//找出某個視窗的建立者(執行緒或程式),返回建立者的標誌符 private static extern int GetWindowThreadProcessId(int hwnd,out int processId); [DllImport("kernel32.dll")]//開啟一個已存在的程式物件,並返回程式的控制程式碼 private static extern int OpenProcess(uint dwDesiredAccess, bool bInheritHandle,int processId); [DllImport("kernel32.dll")]//為指定的程式分配記憶體地址:成功則返回分配記憶體的首地址 private static extern int VirtualAllocEx(int hProcess, IntPtr lpAddress, uint dwSize, uint flAllocationType, uint flProtect); [DllImport("kernel32.dll")]//從指定記憶體中讀取位元組集資料 private static extern bool ReadProcessMemory( int hProcess, //被讀取者的程式控制程式碼 int lpBaseAddress,//開始讀取的記憶體地址 IntPtr lpBuffer, //資料儲存變數 int nSize, //要寫入多少位元組 ref uint vNumberOfBytesRead//讀取長度 ); [DllImport("kernel32.dll")]//將資料寫入記憶體中 private static extern bool WriteProcessMemory( int hProcess,//由OpenProcess返回的程式控制程式碼 int lpBaseAddress, //要寫的記憶體首地址,再寫入之前,此函式將先檢查目標地址是否可用,並能容納待寫入的資料 IntPtr lpBuffer, //指向要寫的資料的指標 int nSize, //要寫入的位元組數 ref uint vNumberOfBytesRead ); [DllImport("kernel32.dll")] private static extern bool CloseHandle(int handle); [DllImport("kernel32.dll")]//在其它程式中釋放申請的虛擬記憶體空間 private static extern bool VirtualFreeEx( int hProcess,//目標程式的控制程式碼,該控制程式碼必須擁有PROCESS_VM_OPERATION的許可權 int lpAddress,//指向要釋放的虛擬記憶體空間首地址的指標 uint dwSize, uint dwFreeType//釋放型別 ); /// <summary> /// LVITEM結構體,是列表檢視控制元件的一個重要的資料結構 /// 佔空間:4(int)x7=28個byte /// </summary> private struct LVITEM //結構體 { public int mask;//說明此結構中哪些成員是有效的 public int iItem;//專案的索引值(可以視為行號)從0開始 public int iSubItem; //子項的索引值(可以視為列號)從0開始 public int state;//子項的狀態 public int stateMask; //狀態有效的遮蔽位 public IntPtr pszText; //主項或子項的名稱 public int cchTextMax;//pszText所指向的緩衝區大小 } public Form1() { InitializeComponent(); } /// <summary> /// LV列表總行數 /// </summary> private int ListView_GetItemRows(int handle) { return SendMessage(handle,LVM_GETITEMCOUNT,0,0); } /// <summary> /// LV列表總列數 /// </summary> private int ListView_GetItemCols(int handle) { return SendMessage(handle, HDM_GETITEMCOUNT, 0, 0); } private void button1_Click(object sender, EventArgs e) { int headerhwnd; //listview控制元件的列頭控制程式碼 int rows,cols; //listview控制元件中的行列數 int processId; //程式pid hwnd = FindWindow("#32770", "Windows 工作管理員"); hwnd = FindWindowEx(hwnd, 0, "#32770",null); hwnd = FindWindowEx(hwnd, 0, "SysListView32",null);//程式介面視窗的控制程式碼,通過SPY獲取 headerhwnd = SendMessage(hwnd, LVM_GETHEADER, 0, 0);//listview的列頭控制程式碼 rows=ListView_GetItemRows(hwnd);//總行數,即程式的數量 cols = ListView_GetItemCols(headerhwnd);//列表列數 GetWindowThreadProcessId(hwnd, out processId); //開啟並插入程式 process = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE, false,processId); //申請程式碼的記憶體區,返回申請到的虛擬記憶體首地址 pointer = VirtualAllocEx(process, IntPtr.Zero, 4096, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); string[,] tempStr;//二維陣列 string[] temp = new string[cols]; tempStr=GetListViewItmeValue(rows,cols);//將要讀取的其他程式中的ListView控制元件中的文字內容儲存到二維陣列中 listView1.Items.Clear();//清空LV控制元件資訊 //輸出陣列中儲存的其他程式的LV控制元件資訊 for (int i = 0; i < rows; i++) { for (int j = 0; j < cols;j++ ) { temp[j]=tempStr[i,j]; } ListViewItem lvi = new ListViewItem(temp); listView1.Items.Add(lvi); } } /// <summary> /// 從記憶體中讀取指定的LV控制元件的文字內容 /// </summary> /// <param name="rows">要讀取的LV控制元件的行數</param> /// <param name="cols">要讀取的LV控制元件的列數</param> /// <returns>取得的LV控制元件資訊</returns> private string[,] GetListViewItmeValue(int rows,int cols) { string[,] tempStr = new string[rows,cols];//二維陣列:儲存LV控制元件的文字資訊 for (int i = 0; i < rows;i++ ) { for (int j = 0; j < cols;j++ ) { byte[] vBuffer = new byte[256];//定義一個臨時緩衝區 LVITEM[] vItem = new LVITEM[1]; vItem[0].mask = LVIF_TEXT;//說明pszText是有效的 vItem[0].iItem = i; //行號 vItem[0].iSubItem = j; //列號 vItem[0].cchTextMax = vBuffer.Length;//所能儲存的最大的文字為256位元組 vItem[0].pszText = (IntPtr)((int)pointer + Marshal.SizeOf(typeof(LVITEM))); uint vNumberOfBytesRead = 0; //把資料寫到vItem中 //pointer為申請到的記憶體的首地址 //UnsafeAddrOfPinnedArrayElement:獲取指定陣列中指定索引處的元素的地址 WriteProcessMemory(process,pointer,Marshal.UnsafeAddrOfPinnedArrayElement(vItem,0),Marshal.SizeOf(typeof(LVITEM)), ref vNumberOfBytesRead); //傳送LVM_GETITEMW訊息給hwnd,將返回的結果寫入pointer指向的記憶體空間 SendMessage(hwnd, LVM_GETITEMW,i,pointer); //從pointer指向的記憶體地址開始讀取資料,寫入緩衝區vBuffer中 ReadProcessMemory(process,((int)pointer + Marshal.SizeOf(typeof(LVITEM))),Marshal.UnsafeAddrOfPinnedArrayElement(vBuffer,0),vBuffer.Length, ref vNumberOfBytesRead); string vText = Encoding.Unicode.GetString(vBuffer, 0, (int)vNumberOfBytesRead); ; tempStr[i,j] = vText; } } VirtualFreeEx(process, pointer, 0, MEM_RELEASE);//在其它程式中釋放申請的虛擬記憶體空間,MEM_RELEASE方式很徹底,完全回收 CloseHandle(process);//關閉開啟的程式物件 return tempStr; } } }

相關文章