利用windows消息操縱程序

今天遇到個小問題,如何在A程序中讀取和操縱B程序窗口。網上搜了一下,搜到以下兩篇文章,試了一下有效,在此作個記錄備忘(如下內容爲轉載):html

 

1. 操縱別的程序窗口中的按鈕數組

https://www.cnblogs.com/hehexiaoxia/p/4223927.html數據結構

首先介紹基本WindowsApi:函數

public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);工具

函數說明:在窗口列表中尋找與指定條件相符的第一個窗口ui

導入庫:user32.libspa

頭文件:winuser.h線程

命名空間 using System.Runtime.InteropServices;指針

參數說明 code

lpClassName String,窗口類名

lpWindowName String,窗口標題

返回值:窗口句柄

public static extern IntPtr FindWindowEx(IntPtr hwndParent, uint hwndChildAfter, string lpszClass, string lpszWindow);

函數說明:在窗口列表中尋找與指定條件相符的第一個子窗口

導入庫:user32.lib

頭文件:winuser.h

命名空間 using System.Runtime.InteropServices;

參數說明 

hwndParent IntPtr ,父窗口句柄,若是hwndParent爲 0 ,則函數以桌面窗口爲父窗口,查找桌面窗口的全部子窗口。

hwndChildAfter IntPtr ,子窗口句柄,查找從在Z序中的下一個子窗口開始。子窗口必須爲hwndParent窗口的直接子窗口而非後代窗口。若是HwndChildAfter爲NULL,查找從hwndParent的第一個子窗口開始。若是hwndParent 和 hwndChildAfter同時爲NULL,則函數查找全部的頂層窗口及消息窗口。

lpszClass string ,控件類名

lpszWindow string ,控件標題,若是該參數爲 NULL,則爲全部窗口全匹配。

返回值:控件句柄。

 

public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam);

函數功能:枚舉一個父窗口的全部子窗口。

導入庫:user32.lib

頭文件:winuser.h

命名空間 using System.Runtime.InteropServices;

參數說明

hWndParent IntPtr 父窗口句柄

lpfn CallBack 回調函數的地址

lParam int 自定義的參數

注意:回調函數的返回值將會影響到這個API函數的行爲。若是回調函數返回true,則枚舉繼續直到枚舉完成;若是返回false,則將會停止枚舉。

其中CallBack是這樣的一個委託:public delegate bool CallBack(IntPtr hwnd, int lParam); 若是 CallBack 返回的是true,則會繼續枚舉,不然就會終止枚舉。

 

應用實例:

用到的WindowApi類

static class WindowsApi
{
    [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

    [DllImport("user32.dll", EntryPoint = "FindWindowEx", SetLastError = true)]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, uint hwndChildAfter, string lpszClass, string lpszWindow);

    [DllImport("user32.dll", EntryPoint = "SendMessage", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern int SendMessage(IntPtr hwnd, uint wMsg, int wParam, int lParam);

    [DllImport("user32.dll", EntryPoint = "SetForegroundWindow", SetLastError = true)]
    public static extern void SetForegroundWindow(IntPtr hwnd);

    [DllImport("user32.dll")]
    public static extern int EnumChildWindows(IntPtr hWndParent, CallBack lpfn, int lParam);

    public delegate bool CallBack(IntPtr hwnd, int lParam);
}

class Program

{

    /// <summary>
    /// 查找窗體上控件句柄
    /// </summary>
    /// <param name="hwnd">父窗體句柄</param>
    /// <param name="lpszWindow">控件標題(Text)</param>
    /// <param name="bChild">設定是否在子窗體中查找</param>
    /// <returns>控件句柄,沒找到返回IntPtr.Zero</returns>
    private IntPtr FindWindowEx(IntPtr hwnd, string lpszWindow, bool bChild)
    {
        IntPtr iResult = IntPtr.Zero;
        // 首先在父窗體上查找控件
        iResult = WindowsApi.FindWindowEx(hwnd, 0, null, lpszWindow);
        // 若是找到直接返回控件句柄
        if (iResult != IntPtr.Zero) return iResult;

        // 若是設定了不在子窗體中查找
        if (!bChild) return iResult;

        // 枚舉子窗體,查找控件句柄
        int i = WindowsApi.EnumChildWindows(
            hwnd,
            (h, l) =>
            {
                IntPtr f1 = WindowsApi.FindWindowEx(h, 0, null, lpszWindow);
                if (f1 == IntPtr.Zero)
                    return true;
                else
                {
                    iResult = f1;
                    return false;
                }
            },
            0);
        // 返回查找結果
        return iResult;
    }

    private void OnRunClick(object sender, EventArgs e)
    {
        // 查找主界面句柄
        IntPtr mainHandle = WindowsApi.FindWindow(null, "主界面(Ver:3.1.3.47)");
        if (mainHandle != IntPtr.Zero)
        {
            // 查找按鈕句柄
            IntPtr iBt = FindWindowEx(mainHandle, "現(F1)", true);
            if (iBt != IntPtr.Zero)
            // 發送單擊消息
            WindowsApi.SendMessage(iBt, 0xF5, 0, 0);
        }
    }
}

應用工具:

這裏能夠應用spy工具來查看窗口的句柄、標題、類型等信息,很是方便。

 

2. 讀取別的程序窗口中listview的內容

http://www.cnblogs.com/szyicol/p/4872094.html

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獲取
            //hwnd = 0xC1CC0;
            headerhwnd = SendMessage(hwnd, LVM_GETHEADER, 0, 0);//listview的列頭句柄

            rows = ListView_GetItemRows(hwnd);//總行數,即進程的數量
            cols = ListView_GetItemCols(headerhwnd);//列表列數
            //cols = 2;
            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;
        }

 
    }
}
相關文章
相關標籤/搜索