受欢迎的博客标签

c#模拟键盘按键,鼠标移动控制其他程序窗口

Published

本文旨在自己动手实现一个类似于“按键精灵”的桌面软件。第一部分介绍了简单的模拟方式,但是有些软件能够屏蔽掉这种简单模拟带来的效果,因此第二部分将介绍如何从驱动级层面进行模拟。

游戏外挂一般分为三个级别:

初级是鼠标、键盘模拟-鼠标键盘模拟的外挂,就是SendMessage或者Key_event

中级是Call游戏内部函数,读写内存-就是Hook进入程序内部操作

高级是抓包,封包的“脱机挂”(完全模拟客户端网络数据,不用运行游戏)。

用C#写外挂的不是很多,大部分是C++,主要原因是MS的C#目前不支持内联汇编功能。因此用C++写底层库,然后用C#调用成为DONET爱好者开发外挂的首选。

 

 

 

简单的模拟键盘鼠标方式(c#)

初级是鼠标、键盘模拟-鼠标键盘模拟的外挂,就是SendMessage或者Key_event

发送消息 模拟鼠标键盘定时 自动下载dzh 6.03 分笔数据(c#)- SendMessage

path

F:\stock\ReadDZHRealTime\src\F10\DownLoadF0FormsTimer\Form1.cs

 

/// <summary>
        /// 发送消息 模拟鼠标键盘定时 自动下载dzh 6.03 分笔数据
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_DownloadReportData_Click(object sender, EventArgs e)
        {
            Process[] Precess = Process.GetProcesses();

            foreach (Process p in Precess)
            {
                if (p.ProcessName == "dzh2")
                {

                    SetForegroundWindow(p.MainWindowHandle);


                    if (p.MainWindowTitle.ToUpper().Contains("大智慧") || p.MainWindowTitle.ToUpper().Contains("下载数据"))
                    {

                    }


                    IntPtr msgHandle = FindWindow(null, "下载数据");  //主窗口标题

                    //把下载窗口显示到前台 20190303
                    SetForegroundWindow(msgHandle);


                    if (msgHandle != IntPtr.Zero)
                    {
                        //找到Button
                        //  判断按钮是否可用
                        IntPtr btnHandle = FindWindowEx(msgHandle, 0, "Button", "开始");
                        IntPtr btnHandle2 = FindWindowEx(msgHandle, 0, "Button", "取消");

                        //  判断按钮是否可用
                        bool a = IsWindowEnabled(btnHandle);

                        int i = 0;
                        if (btnHandle != IntPtr.Zero)
                        {

                            i = SendMessage(btnHandle, BM_CLICK, 0, 0);  //const int MOUSEEVENTF_LEFTDOWN = 0x0002; //模拟鼠标左键按下 


                        }

                    }




                    //   等待下载完成 弹出窗口 出现

                    //  判断弹出下载成功窗口

                    int ii = 0;


                    while (true)
                    {

                        System.Threading.Thread.Sleep(1000);

                        //  判断弹出下载成功警告框
                        IntPtr msgHandleDownloadSuccessfulResult = FindWindow(null, "SuperStk");  //主窗口标题

                    

                        if (msgHandleDownloadSuccessfulResult != IntPtr.Zero)
                        {
                            

                            //找到Button
                            //  判断按钮是否可用
                            IntPtr btnHandleOK = FindWindowEx(msgHandleDownloadSuccessfulResult, 0, "Button", "确定");


                            //  判断按钮是否可用
                            bool a = IsWindowEnabled(btnHandleOK);

                            int i = 0;
                            if (btnHandleOK != IntPtr.Zero)
                            {
                                //把下载窗口显示到前台 20190303
                                SetForegroundWindow(msgHandleDownloadSuccessfulResult);


                                i = SendMessage(btnHandleOK, BM_CLICK, 0, 0);   //模拟鼠标左键按下 

                                break;

                                //   等待下载完成

                                //while (!IsWindowEnabled(btnHandleOK))
                                //{

                                //    int ddd = 0;
                                //    Application.DoEvents();


                                //}

                            }

                        }


                        ii++;
                        //  Console.WriteLine("第"+i.ToString()+"次检查"); 5秒都没显示出来就推出循环
                        if (ii > 15)
                        {
                            break;
                        }


                        int ddd = 0;
                        Application.DoEvents();


                    }




                    // step 3  下载完成,处理弹出下载成功窗口





                }

            }
        }

How to use

鼠标右键点击 DownLoadF0FormsTimer.exe,用Administrator 权限 运行程序,确保其他应用程序能收到 发送的消息

模拟按键股票代码下载大智慧6.03 F10 (c#)- Key_event SendKeys

path:

F:\stock\ReadDZHRealTime\src\F10\DownLoadF10FormsSimulateMouseKeyFromDzh603\DownLoadF10FormsSimulateMouseKeyFromDzh603.csproj

 

namespace DownLoadF10FormsSimulateMouseKeyFromDzh603
{
    public partial class Form1 : Form
    {
        //http://it.china-b.com/cxsj/cs/20090824/163177_1.html
        [DllImport("msvcrt.dll")]
        public static extern int _getch();

        [DllImport("msvcrt.dll")]
        public static extern int _kbhit();

        [DllImport("user32.dll")]
        private static extern bool
       SetForegroundWindow(IntPtr hWnd);

        //  [System.Runtime.InteropServices.DllImport("user32")]
        //   private static extern int mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo); 
        [DllImport("user32", EntryPoint = "mouse_event")]
        private static extern int mouse_event(
          int dwFlags,// 下表中标志之一或它们的组合 
          int dx,
          int dy, //指定x,y方向的绝对位置或相对位置 
          int cButtons,//没有使用 
          int dwExtraInfo//没有使用 
          );

        const int MOUSEEVENTF_MOVE = 0x0001;     // 移动鼠标 
        const int MOUSEEVENTF_LEFTDOWN = 0x0002; //模拟鼠标左键按下 
        const int MOUSEEVENTF_LEFTUP = 0x0004; //模拟鼠标左键抬起 
        const int MOUSEEVENTF_RIGHTDOWN = 0x0008; //模拟鼠标右键按下 
        const int MOUSEEVENTF_RIGHTUP = 0x0010; //模拟鼠标右键抬起 
        const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;// 模拟鼠标中键按下 
        const int MOUSEEVENTF_MIDDLEUP = 0x0040;// 模拟鼠标中键抬起 
        const int MOUSEEVENTF_ABSOLUTE = 0x8000; //标示是否采用绝对坐标

        [DllImport("user32.dll")]
        static extern bool SetCursorPos(int X, int Y);
        public Form1()
        {
            InitializeComponent();
        }

        private void button_DownLoadF10FormsSimulateMouseKeyFromDzh603_Click(object sender, EventArgs e)
        {
            //创建一个矩形对象 
            System.Drawing.Rectangle rect = new System.Drawing.Rectangle();
            //通过一个函数对这个矩形对象赋值,这些值就是屏幕的工作区域 
            rect = Screen.GetBounds(this);
            //  MessageBox.Show("本机器的分辨率是" + rect.Width.ToString() + "*" + rect.Height.ToString());


            int xstep = rect.Width / 12;
            int ystep = rect.Height / 35;

            int startx = 140;
            int starty = 80;

            bool a = false;

            ConsoleKeyInfo keyInfo;

            foreach (Process p in Process.GetProcesses())
            {

                if (p.ProcessName == "dzh2")
                {





                }
                if (p.MainWindowTitle.ToUpper().Contains("大智慧") || p.MainWindowTitle.ToUpper().Contains("模拟精灵"))
                {


                    SetForegroundWindow(p.MainWindowHandle);
                    System.Threading.Thread.Sleep(1000);
                    SendKeys.SendWait("SZ300009");
                    SendKeys.SendWait("{ENTER}");
                    SendKeys.SendWait("{F10}");
                    SendKeys.Flush();

                    mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, startx, starty, 0, 0);
                    System.Threading.Thread.Sleep(2000);

                    DzhDataProvider dzhDataProvider = new DzhDataProvider();

                    //从财务数据中读取股票代码表
                    var codeTableList = dzhDataProvider.GetCodetableFromFullFin();

                    foreach (string item in codeTableList)
                    {


                        //在while (true)循环中,按a键退出循环 http://it.china-b.com/cxsj/cs/20090824/163177_1.html
                        if (_kbhit() > 0)//如果有键按下

                        {



                            if ((char)_getch() == 0x1b)//判断按键ESc,ESC的键盘扫描码是0x1b

                                break;

                        }


                        //获取鼠标位置
                        int y1 = Control.MousePosition.Y;

                        //鼠标移到屏幕下方退出
                        if (y1 >= rect.Height / 2)
                            break;


                        //股票代码
                        string stockcode = item.ToString();

                        //非股票,下一个
                        if (Common.DzhData.Utility.StockCategory.GetStockCodeType(stockcode) != Common.DzhData.Model.StockCodeType.ISGP)
                            continue;



                        //模拟键盘输入股票代码,回车,按F10键
                        for (int i = 0; i < stockcode.Length; i++)//逐字节变为16进制字符
                        {
                            var code = stockcode[i];
                            SendKeys.SendWait($"{code}");
                            System.Threading.Thread.Sleep(500);
                        }


                        SendKeys.SendWait("{ENTER}");
                        System.Threading.Thread.Sleep(500);
                        SendKeys.SendWait("{F10}");

                        SendKeys.Flush();



                        string Market = stockcode.Substring(0, 2);
                        string filename = stockcode.Replace(Market, "") + ".F10";
                        string datafile = dzhDataProvider.InstalllPath + "\\data\\" + Market + "\\base\\" + filename;

                        //确保F0文件生成-判断文件.F10是否生成,如果按键后没生成文件,再按一次
                        int failcount;
                        failcount = 0;
                        while (!File.Exists(datafile) && failcount < 4)
                        {
                            SendKeys.SendWait($"{stockcode}");
                            SendKeys.SendWait("{ENTER}");
                            SendKeys.SendWait("{F10}");
                            SendKeys.Flush();
                            System.Threading.Thread.Sleep(1000);

                            failcount++;
                        }

                        if (File.Exists(datafile))
                        {
                            int i = 0;

                            DataFileStruct.F10File_Data_RecordStruct[] subitem = dzhDataProvider.GetF10SubItem(stockcode);




                            //菜单为2行,每行8个子栏目
                            int count = 1;
                            for (int row = 0; row < 2; row++)
                            {
                                //后面8个子栏目
                                for (int col = 1; col < 9; col++)
                                {
                                    if (count > subitem.Length) break;

                                    SetCursorPos(startx + col * xstep, starty + row * ystep);
                                    mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

                                    datafile = dzhDataProvider.InstalllPath + "\\data\\" + Market + "\\base\\" + subitem[count - 1].SubItemFileName;

                                    failcount = 0;
                                    while (!File.Exists(datafile) && failcount < 4)
                                    {
                                        SetCursorPos(startx + col * xstep, starty + row * ystep);
                                        mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
                                        System.Threading.Thread.Sleep(1000);
                                        failcount++;

                                    }

                                    //判断子目录文件是否生成,如未生成再按一次
                                    if (!File.Exists(datafile))
                                    {
                                        SendKeys.SendWait(stockcode);
                                        SendKeys.SendWait("{ENTER}");
                                        SendKeys.SendWait("{F10}");
                                        SendKeys.Flush();

                                        SetCursorPos(startx + col * xstep, starty + row * ystep);
                                        mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);



                                    }

                                    System.Threading.Thread.Sleep(100);

                                    count++;

                                }
                            }
                        }
                    }

                    mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);

                    mouse_event(MOUSEEVENTF_LEFTDOWN, 500, 400, 0, 0);
                    mouse_event(MOUSEEVENTF_LEFTUP, 500, 400, 0, 0);

                    //SendKeys.SendWait("SZ300006");
                    //SendKeys.SendWait("{ENTER}");

                    //SendKeys.SendWait("SZ300007");
                    //SendKeys.SendWait("{ENTER}");

                }
                else
                {

                }



            }
        }

中级是Call游戏内部函数,读写内存,Hook进入程序内部

 

1.使用Spy++工具,查找窗口,查看 主窗口及其下控件的句柄、控件类型、Caption。

2.对于给定的对话框窗口,用FindWindow函数。

对话框窗口其中的任何控件,如图标、文本、确定、取消按钮等都是它的子窗口,本质上还是窗口,查找子窗口时用FindWindowEx。

凡运行于Windows上的窗口,都具有句柄。窗口上的文本框,按钮之类的,也有其句柄(可看作子窗口句柄)。这些句柄的类型可以通过Spy++进行查询.

用EnumWindows,遍历所有的顶级父窗口,用EnumChildWindows遍历主窗口下的所有子窗口。

vs 2019  tool spy++  find 找到窗口名称
用findWindow 找到主窗口句柄、用findWindowEx 找到主窗口内控件的句柄
用SendMessage 向句柄发送 按键 或 鼠标点击消息

鼠标右键点击 .EXE,用Administrator 权限 运行程序,确保其他应用程序能收到 发送的消息

 

首先: 要确保 hWnd 是你希望操作的”日常工作软件“的主窗口句柄的ID号码 —— 这是很古老的Win32API的概念,每个桌面上的窗口(包括不可见的),甚至一个按钮,都对应一个唯一的hWnd句柄ID。。。。 如果不能获取或者获取的不对,你就趁早放弃吧。。。

/// <summary>
/// 找到窗口
/// </summary>
/// <param name="lpClassName">窗口类名(例:Button)</param>
/// <param name="lpWindowName">窗口标题</param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "FindWindow")]
private extern static IntPtr FindWindow(string lpClassName, string lpWindowName);
 
/// <summary>
/// 找到窗口
/// </summary>
/// <param name="hwndParent">父窗口句柄(如果为空,则为桌面窗口)</param>
/// <param name="hwndChildAfter">子窗口句柄(从该子窗口之后查找)</param>
/// <param name="lpszClass">窗口类名(例:Button</param>
/// <param name="lpszWindow">窗口标题</param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "FindWindowEx")]
private extern static IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
 
/// <summary>
/// 发送消息
/// </summary>
/// <param name="hwnd">消息接受窗口句柄</param>
/// <param name="wMsg">消息</param>
/// <param name="wParam">指定附加的消息特定信息</param>
/// <param name="lParam">指定附加的消息特定信息</param>
/// <returns></returns>
[DllImport("user32.dll", EntryPoint = "SendMessageA")]
private static extern int SendMessage(IntPtr hwnd, uint wMsg, int wParam, int lParam);
 
//窗口发送给按钮控件的消息,让按钮执行点击操作,可以模拟按钮点击
private const int BM_CLICK = 0xF5;

其次:右键点击 .EXE,用Administrator 权限 运行程序,确保其他应用程序能收到 发送的消息

在现在Windows的UAC的管制之下,以及各种安全机制、沙箱隔离的现代防恶意软件的保护机制的呵护下,你要确定你的程序有系统管理员级别的权限能够给其他”日常工作软件“的窗体发送消息才可以

最后:如其他楼层回复所述,如果是要发送键盘,那第二个参数应该是个类似于:WM_KEYDOWN 这样的值。

通过窗体标题,循环查找该窗体,然后找到确定按钮,通过句柄发送点击消息。

private void Form1_Load(object sender, EventArgs e)
       {
           Task task = new Task(() =>
           {
               while (true)
               {
                   //测试警告框
                   IntPtr maindHwnd = FindWindow(null, "提示");//主窗口标题
                   if (maindHwnd != IntPtr.Zero)
                   {
                       IntPtr childHwnd = FindWindowEx(maindHwnd, IntPtr.Zero, null, "确定");//按钮控件标题
                       if (childHwnd != IntPtr.Zero)
                       {
                           SendMessage(childHwnd, BM_CLICK, 0, 0);
                       }
                   }
               }
           });
 
           task.Start();
       }

 

Note:发送键盘(WM_KEYDOWN, WM_KEYUP)事件和发送鼠标事件(WM_MOUSEDOWN, WM_MOUSEUP)时候,最后两个参数的用途是不一样的

 

 

SysListView32

读取其他软件listview控件的内容 https://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;
        }

 
    }
}

 

驱动级模拟键盘鼠标(C#实现)- WINIO驱动

https://www.cnblogs.com/vitaminVIP/p/11733954.html

 

WINIO驱动

 

Useful links

Getting & Setting SysListView32 control items c#

https://www.codeproject.com/questions/238246/getting-setting-syslistview32-control-items

https://stackoverflow.com/questions/4857602/get-listview-items-from-other-windows

 

https://github.com/sahajkoka/ListOpenWindows/blob/master/ListOpenWindows/Program.cs(获取Windows窗口列表c#)

 

EnumChildWindows  find all child windows c#

https://www.codeproject.com/Questions/816920/EnumChildWindows-doesnt-find-all-child-windows

 

c#自己动手实现一个类似于“按键精灵”的桌面软件。第一部分介绍了简单的模拟方式,但是有些软件能够屏蔽掉这种简单模拟带来的效果,因此第二部分将介绍如何从驱动级层面进行模拟。

https://blog.csdn.net/ryuenkyo/article/details/106207047