游戲程序的操作不外乎兩種——鍵盤輸入控制和鼠標(biāo)輸入控制,幾乎所有游戲中都使用鼠標(biāo)來改變角色的位置和方向,本文主要是講述如何使用C#調(diào)用Windows API函數(shù)實現(xiàn)鼠標(biāo)模擬操作的功能。首先通過結(jié)合FindWindow和FindWindowEx尋找到窗體的按鈕,在通過SetCursorPos或mouse_event函數(shù)操作鼠標(biāo),同時涉及到通過spy++工具獲取窗體消息的信息。
一。 Windows API函數(shù)介紹
.NET沒有提供改變鼠標(biāo)指針位置、模擬單機操作的函數(shù),但是可以通過調(diào)用Windows API函數(shù)實現(xiàn)。
[DllImport(“user32.dll”)]
static extern bool SetCursorPos(int X,int Y);
該函數(shù)用于設(shè)置鼠標(biāo)的位置,其中X和Y是相對于屏幕左上角的絕對位置。
[DllImport(“user32.dll”)]
static extern void mouse_event(MouseEventFlag flags,int dx,int dy,uint data,UIntPtr extraInfo);
該函數(shù)不僅可以設(shè)置鼠標(biāo)指針絕對位置,而且可以以相對坐標(biāo)來設(shè)置位置。
其中flags標(biāo)志位集,指定點擊按鈕和鼠標(biāo)動作的多種情況.dx指鼠標(biāo)沿x軸絕對位置或上次鼠標(biāo)事件位置產(chǎn)生以來移動的數(shù)量.dy指沿y軸的絕對位置或從上次鼠標(biāo)事件以來移動的數(shù)量.data如果flags為MOUSE_WHEEL則該值指鼠標(biāo)輪移動的數(shù)量(否則為0),正值向前轉(zhuǎn)動.extraInfo指定與鼠標(biāo)事件相關(guān)的附加32位值。
?。跠llImport(“user32.dll”)]
static extern IntPtr FindWindow(string strClass, string strWindow);
該函數(shù)根據(jù)類名和窗口名來得到窗口句柄,但是這個函數(shù)不能查找子窗口,也不區(qū)分大小寫。如果要從一個窗口的子窗口查找需要使用FIndWindowEX函數(shù)。
?。跠llImport(“user32.dll”)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
string strClass, string strWindow);
該函數(shù)獲取一個窗口的句柄,該窗口的類名和窗口名與給定的字符串相匹配,該函數(shù)查找子窗口時從排在給定的子窗口后面的下一個子窗口開始。其中參數(shù)
hwnParent為要查找子窗口的父窗口句柄,若該值為NULL則函數(shù)以桌面窗口為父窗口,查找桌面窗口的所有子窗口。
hwndChildAfter子窗口句柄,查找從在Z序中的下一個子窗口開始,子窗口必須為hwnParent直接子窗口而非后代窗口,若hwnChildAfter為NULL,查找從父窗口的第一個子窗口開始。
strClass指向一個指定類名的空結(jié)束字符串或一個標(biāo)識類名字符串的成員的指針。
strWindow指向一個指定窗口名(窗口標(biāo)題)的空結(jié)束字符串。若為NULL則所有窗體全匹配。
返回值:如果函數(shù)成功,返回值為具有指定類名和窗口名的窗口句柄,如果函數(shù)失敗,返回值為NULL.
二。 鼠標(biāo)自動點擊按鈕和查看鼠標(biāo)運行軌跡
首先創(chuàng)建一個C#工程,設(shè)計的窗體如下圖所示,同時添加Timer時間器控件:
然后添加的如下代碼,即可實現(xiàn)鼠標(biāo)模擬技術(shù)及自動操作鼠標(biāo):
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; //StructLayout
namespace MouseAction
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
//結(jié)構(gòu)體布局 本機位置
[StructLayout(LayoutKind.Sequential)]
struct NativeRECT
{
public int left;
public int top;
public int right;
public int bottom;
}
//將枚舉作為位域處理
?。跢lags]
enum MouseEventFlag : uint //設(shè)置鼠標(biāo)動作的鍵值
{
Move = 0x0001, //發(fā)生移動
LeftDown = 0x0002, //鼠標(biāo)按下左鍵
LeftUp = 0x0004, //鼠標(biāo)松開左鍵
RightDown = 0x0008, //鼠標(biāo)按下右鍵
RightUp = 0x0010, //鼠標(biāo)松開右鍵
MiddleDown = 0x0020, //鼠標(biāo)按下中鍵
MiddleUp = 0x0040, //鼠標(biāo)松開中鍵
XDown = 0x0080,
XUp = 0x0100,
Wheel = 0x0800, //鼠標(biāo)輪被移動
VirtualDesk = 0x4000, //虛擬桌面
Absolute = 0x8000
}
//設(shè)置鼠標(biāo)位置
[DllImport(“user32.dll”)]
static extern bool SetCursorPos(int X, int Y);
//設(shè)置鼠標(biāo)按鍵和動作
?。跠llImport(“user32.dll”)]
static extern void mouse_event(MouseEventFlag flags, int dx, int dy,
uint data, UIntPtr extraInfo); //UIntPtr指針多句柄類型
[DllImport(“user32.dll”)]
static extern IntPtr FindWindow(string strClass, string strWindow);
//該函數(shù)獲取一個窗口句柄,該窗口雷鳴和窗口名與給定字符串匹配 hwnParent=Null從桌面窗口查找
?。跠llImport(“user32.dll”)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,
string strClass, string strWindow);
[DllImport(“user32.dll”)]
static extern bool GetWindowRect(HandleRef hwnd, out NativeRECT rect);
//定義變量
const int AnimationCount = 80;
private Point endPosition;
private int count;
private void button1_Click(object sender, EventArgs e)
{
NativeRECT rect;
//獲取主窗體句柄
IntPtr ptrTaskbar = FindWindow(“WindowsForms10.Window.8.app.0.2bf8098_r11_ad1”, null);
if (ptrTaskbar == IntPtr.Zero)
{
MessageBox.Show(“No windows found!”);
return;
}
//獲取窗體中“button1”按鈕
IntPtr ptrStartBtn = FindWindowEx(ptrTaskbar, IntPtr.Zero, null, “button1”);
if (ptrStartBtn == IntPtr.Zero)
{
MessageBox.Show(“No button found!”);
return;
}
//獲取窗體大小
GetWindowRect(new HandleRef(this, ptrStartBtn), out rect);
endPosition.X = (rect.left + rect.right) / 2;
endPosition.Y = (rect.top + rect.bottom) / 2;
//判斷點擊按鈕
if (checkBox1.Checked)
{
//選擇“查看鼠標(biāo)運行的軌跡”
this.count = AnimationCount;
movementTimer.Start();
}
else
{
SetCursorPos(endPosition.X, endPosition.Y);
mouse_event(MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero);
mouse_event(MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero);
textBox1.Text = String.Format(“{0},{1}”, MousePosition.X, MousePosition.Y);
}
}
//Tick:定時器,每當(dāng)經(jīng)過多少時間發(fā)生函數(shù)
private void movementTimer_Tick(object sender, EventArgs e)
{
int stepx = (endPosition.X - MousePosition.X) / count;
int stepy = (endPosition.Y - MousePosition.Y) / count;
count--;
if (count == 0)
{
movementTimer.Stop();
mouse_event(MouseEventFlag.LeftDown, 0, 0, 0, UIntPtr.Zero);
mouse_event(MouseEventFlag.LeftUp, 0, 0, 0, UIntPtr.Zero);
}
textBox1.Text = String.Format(“{0},{1}”, MousePosition.X, MousePosition.Y);
mouse_event(MouseEventFlag.Move, stepx, stepy, 0, UIntPtr.Zero);
}
}
}
同時自定義一個對話框,增加一個button按鈕,其運行結(jié)果如下圖所示:
可以看到當(dāng)運行程序勾選“查看鼠標(biāo)運行的軌跡”并點擊“開始”按鈕后,會通過FindWindow和FindWindowEx函數(shù)查找窗體“Form1”的“button1”按鈕,并通過mouse_event移動鼠標(biāo)和點擊鼠標(biāo)。其中函數(shù)原型為:
IntPtr FindWindowEx(
IntPtr hwndParent, // handle to parent window [父窗體句柄]
IntPtr hwndChildAfter, // handle to child window [子窗體句柄]
string strClass, // class name [窗體類名]
string strWindow // window name [窗體名]
);
但是怎樣找到窗體類名和按鈕的類名呢?由于初學(xué),很多窗體我都沒有實現(xiàn)如QQ,它需要用到一個叫spy++的工具。
PS:第一次制作gif格式動態(tài)圖片,參照博客 http://blog.csdn.net/tangcheng_ok/article/details/8246792
三。 使用SPY++工具獲取窗體信息
如果修改代碼為:
//獲取任務(wù)欄句柄
IntPtr ptrTaskbar = FindWindow(“Shell_TrayWnd”,null);
//托盤通知句柄
IntPtr ptrStartBtn = FindWindowEx(ptrTaskbar, IntPtr.Zero, “TrayNotifyWnd”, null);
可以獲取電腦底部任務(wù)欄的托盤通知句柄,其中通過Spy++工具(VS中“工具”中自帶)查找如下圖所示:
同樣,我通過spy++工具獲取txt句柄,首先打開spy++工具,同時點擊“查找窗口”按鈕(望遠(yuǎn)鏡),再點擊“查找程序工具”中按鈕拖拽至要查看的窗體中,點擊“確定”按鈕
這樣就會顯示這個txt的信息,同時可以右擊“屬性”顯示窗體的類名、窗體題目、句柄等信息。
最后通過下面代碼可以獲取hello.txt的句柄:
//獲取記事本句柄
IntPtr ptrTaskbar = FindWindow(“Notepad”, null);
IntPtr ptrStartBtn = FindWindowEx(ptrTaskbar, IntPtr.Zero, “Edit”, null);
再通過mouse_event操作鼠標(biāo),同時可以通過SendMessage將指定的消息發(fā)送到一個或多個窗口,PostMessage將一個消息寄送到一個線程的消息隊列后就立即返回。實現(xiàn)消息傳遞等功能,學(xué)習(xí)ing~
四。 總結(jié)
該篇文章主要講述C#如何操作鼠標(biāo)的事件,在制作游戲外掛或自動運行程序時非常實用,但遺憾的是在上面通過窗體名稱“Form1”獲取窗體時總是失敗,需要通過spy++獲取它的類名來實現(xiàn).Why?同時如果想學(xué)習(xí)鍵盤模擬技術(shù)的可以研究SetWindowsHookEx(安裝鉤子)、CallNextHookEx(下一個鉤子)、UnhookWindowsHookEx(卸載鉤子)和鼠標(biāo)Hook實現(xiàn)很多技術(shù)。
希望文章對大家有所幫助,如果有錯誤或不足之處,請見諒~
(By:Eastmount 2014年10月13日 晚上8點 http://blog.csdn.net/eastmount/)
參考資料-在線筆記:
本文主要參考書籍《C#網(wǎng)絡(luò)變成高級篇之網(wǎng)頁游戲輔助程序設(shè)計》張慧斌 王小峰著
1.C#獲取QQ聊天輸入框中內(nèi)容 http://www.csharpwin.com/csharpspace/9133r5654.shtml
2.C#查找窗口,F(xiàn)indWindow用法(By-LYBwwp)http://blog.csdn.net/lybwwp/article/details/8168553
3.FindWindowEx用法(By-coolszy) http://blog.csdn.net/coolszy/article/details/5523784
4.C# 隱藏任務(wù)欄開始按鈕關(guān)閉shell(By-sshhbb)http://blog.csdn.net/sshhbb/article/details/6605976
5.任務(wù)欄句柄 http://blog.csdn.net/wangjieest/article/details/6943241
6.C#如何在外部程序的密碼框內(nèi)自動輸入密碼 http://biancheng.dnbcw.info/c/117849.html
7.C#實現(xiàn)對外部程序的調(diào)用操作 http://www.blue1000.com/bkhtml/c17/2012-11/70993.htm
8.百度知道 C# API函數(shù)FindWindowEx返回子窗體的值為零
9.百度知道 用C#操作API實現(xiàn)填寫桌面窗體內(nèi)的textbox并點擊窗體按鈕
評論
查看更多