关于C# windows发送消息
2007年11月19日 星期一 下午 04:15
今天终于又有一点心得啦..这一切都源于我喜欢的一个游戏.
由于在玩游戏,所以用按键精灵做了个外挂,使用后发现游戏中是针对按键精灵处理过的,当使用它一定的时间后,后隐去人物的红和蓝,让自己不能加血,够狠的!!
于是今天上午想能不能用C#来做一个类似的东西呢,过程如下:
一、获取指定坐标的颜色
在游戏中,怪物的血是显示在一个固定的地方的,所以,只要这个点没有血,那么表示当前没有怪可攻击,意思就是可以找怪了,但是这个坐标是多少我都不知道。于是,我在游戏中截了下屏,并保存了图片,把它设为桌面背景色,哈哈,我真是太聪明啦~~在程序中BUTTON下写一行:lbpoint.Text = Cursor.Position.X.ToString() + "," + Cursor.Position.Y.ToString();把鼠标放到血是最左边,也就是怪物血最少的那里,按了下空格,嘿嘿,坐标出来了:293, 35,当然是不是一次完成的,多几下就差不多了。
现在坐标出来了,怎么取它的色呢?经过一阵“摆渡”,原来windows的一个API可以实现这样的功能,OK,那就用它吧,代码如下:
[DllImport("gdi32.dll")]
static public extern uint GetPixel(IntPtr hDC, int XPos, int YPos);
[DllImport("gdi32.dll")]
static public extern IntPtr CreateDC(string driverName, string deviceName, string output, IntPtr lpinitData);
[DllImport("gdi32.dll")]
static public extern bool DeleteDC(IntPtr DC);
static public byte GetRValue(uint color)
{
return (byte)color;
}
static public byte GetGValue(uint color)
{
return ((byte)(((short)(color)) >> 8));
}
static public byte GetBValue(uint color)
{
return ((byte)((color) >> 16));
}
static public byte GetAValue(uint color)
{
return ((byte)((color) >> 24));
}
static public Color GetColorOfScreen(Point screenPoint)
{
IntPtr displayDC = CreateDC("DISPLAY", null, null, IntPtr.Zero);
uint colorref = GetPixel(displayDC, screenPoint.X, screenPoint.Y);
DeleteDC(displayDC);
byte Red = GetRValue(colorref);
byte Green = GetGValue(colorref);
byte Blue = GetBValue(colorref);
return Color.FromArgb(Red, Green, Blue);
}
我现在只要调用GetColorOfScreen这个方法就可以得到相应坐标点的色了,作了下测试,OK!游戏中怪的血条色为:(255, 162, 2, 5)可以Color redColor = Color.FromArgb(255, 162, 2, 5);这可构造出色。
二、获取游戏窗口
如果按逻辑的顺序的话,这一步应该放在第一的,这里也是应用的API
[DllImport("user32.dll")]
static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool SetForegroundWindow(IntPtr hWnd);
IntPtr 这个东西,其实说白了就是窗口的句柄,
同时,我用进程名称要获取这个游戏窗口,如下:
Process[] pros = Process.GetProcessesByName(txtProName.Text.Trim());
if (pros.Length != 0)
{
IntPtr pGame = pros[0].MainWindowHandle;
SetActiveWindow(pGame);
SetForegroundWindow(pGame);
Thread.Sleep(2000);
BeginGame();
}
else
{
MessageBox.Show("找不到相应的窗口");
}
三、开始游戏
先是定义一个色表示为怪物的血条Color redColor = Color.FromArgb(255, 162, 2, 5);
再定义一个点,并获取它的色:
Point p = new Point(293, 35);
Color gameColor = GetColorOfScreen(p);
当这两个色相同时,那么就打怪:
不相等时,再找怪
方法是这样的:
private void BeginGame()
{
Color redColor = Color.FromArgb(255, 162, 2, 5);//(255, 255, 123, 104);
Point p = new Point(293, 35);
Color gameColor = GetColorOfScreen(p);
if (gameColor == redColor)
{
Fight();
BeginGame();
}
else
{
FindMonster();
BeginGame();
}
}
四、找怪
核心: SendKeys.SendWait("{Tab}");
向指定的窗口发送消息,SendKeys还有一个方法Send,有什么区别呢,自己试了就知道了,输出到文本文件里看就明白了,这里表示按了一下Tab这个键,关于其它键的用法如下:
键 代码
BACKSPACE {BACKSPACE}、{BS} 或 {BKSP}
BREAK {BREAK}
CAPS LOCK {CAPSLOCK}
DEL 或 DELETE {DELETE} 或 {DEL}
DOWN ARROW(下箭头键) {DOWN}
END {END}
ENTER {ENTER} 或 ~
ESC {ESC}
HELP {HELP}
HOME {HOME}
INS 或 INSERT {INSERT} 或 {INS}
LEFT ARROW(左箭头键) {LEFT}
NUM LOCK {NUMLOCK}
PAGE DOWN {PGDN}
PAGE UP {PGUP}
PRINT SCREEN {PRTSC}(保留,以备将来使用)
RIGHT ARROW(右箭头键) {RIGHT}
SCROLL LOCK {SCROLLLOCK}
TAB {TAB}
UP ARROW(上箭头键) {UP}
F1 {F1}
F2 {F2}
F3 {F3}
F4 {F4}
F5 {F5}
F6 {F6}
F7 {F7}
F8 {F8}
F9 {F9}
F10 {F10}
F11 {F11}
F12 {F12}
F13 {F13}
F14 {F14}
F15 {F15}
F16 {F16}
数字键盘加号 {ADD}
数字键盘减号 {SUBTRACT}
数字键盘乘号 {MULTIPLY}
数字键盘除号 {DIVIDE}
若要指定与 SHIFT、CTRL 和 ALT 键的任意组合一起使用的键,请在这些键代码之前加上以下一个或多个代码:
键 代码
SHIFT + (SHIFT="+")
CTRL ^ (CTRL="^") 如果输入
ALT %
如果是一般的数据和字母,就只要写进去就可以了,如:SendKeys.SendWait("A");
五、打怪
找怪都做好了,那打怪就好办了,我想(事实不是这样的)
把攻击键放到3这个位置,
SendKeys.SendWait("3");
SendKeys.SendWait("3");
SendKeys.Flush();
OK。这样就差不多了,再加个线程进去,不然,怎么停止呢~~,完整的代码如下(主要部分):
Thread th = null;
//开始挂机
private void btStart_Click(object sender, EventArgs e)
{
th = new Thread(new ThreadStart(StartGame));
th.Start();
btStart.Enabled = false;
btStop.Enabled = true;
}
//停止挂机
private void btStop_Click(object sender, EventArgs e)
{
th.Abort();
btStart.Enabled = true;
btStop.Enabled = false;
}
private void StartGame()
{
Process[] pros = Process.GetProcessesByName(txtProName.Text.Trim());//在窗体上输入游戏进程名
if (pros.Length != 0)
{
IntPtr pGame = pros[0].MainWindowHandle;
SetActiveWindow(pGame);
SetForegroundWindow(pGame);
Thread.Sleep(2000);
BeginGame();
}
else
{
MessageBox.Show("找不到相应的窗口");
}
}
private void BeginGame()
{
Color redColor = Color.FromArgb(255, 162, 2, 5);//(255, 255, 123, 104);
Point p = new Point(293, 35);
Color gameColor = GetColorOfScreen(p);
if (gameColor == redColor)
{
Fight();
BeginGame();
}
else
{
FindMonster();
BeginGame();
}
}
private void Fight()
{
SendKeys.SendWait("3");
SendKeys.SendWait("3");
SendKeys.Flush();
Thread.Sleep(2000);
}
private void FindMonster()
{
SendKeys.SendWait("{Tab}");
Thread.Sleep(2000);
}
到这里为止,我认为就差不多了,测试:
把游戏图片设为桌面背景,打开一个记事本,在窗体中输入notepad,点开始挂机,
找到记事本,OK
由于桌面那张图片是有怪物的,输入33 OK
再把记事本最大化,找不到怪物血条了,输入tab OK。。
哈哈,,说明测试成功!!
但是,问题也出来了,进入游戏后,找怪都可以正常,就是不能按3这个键,,不知道为什么,我在自己写的窗体上都可以的~~~
于是,再做了个窗体;
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
lbMessage.Text=KeyData.ToString();
return base.ProcessCmdKey(ref msg, keyData);
}
这样,窗体上输入的按键都会显示到控件是去,然后用刚刚那个窗口,输入这个程的名称,点开始挂机,在没有怪血条的时候是输入的Tab,OK。但有怪的时候是输入的D3而不是3,啊。。这是怎么回事呢,于是我把打怪那一行改为:SendKeys.SendWait("D3");但是由于下午上班了,没时间测试,晚上回去再试试吧。
后记:
我还用鼠标点示来实现触发打怪,取到攻击技能的坐标,
[Flags]
enum MouseEventFlag : uint
{
Move = 0x0001,
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
XDown = 0x0080,
XUp = 0x0100,
Wheel = 0x0800,
VirtualDesk = 0x4000,
Absolute = 0x8000
}
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
static extern void mouse_event(MouseEventFlag flags, int dx, int dy, uint data, UIntPtr extraInfo);
SetCursorPos(418, 744);
Thread.Sleep(1000);
mouse_event(MouseEventFlag.RightDown, 418, 744, 0, UIntPtr.Zero);
mouse_event(MouseEventFlag.RightUp, 418, 744, 0, UIntPtr.Zero);
MouseEventFlag 这个枚举基本上所有的鼠标事件都有了,不错,可以做很多事啦,但在游戏中还是不行,我怀疑是游戏处理了这种消息的发送的,不让这种模拟的消息传入,那样的话,做外挂可以麻烦了,不过。今天还是有不少的体会的,因为我一开始什么都不懂,到后来还是学到点东西啦~
[DllImport("user32.dll")]
表示调用Windows的一个DLL,它是Windows自带的,不过要对它有所了解才能用它,我们用的时候只要定义好它的方法就行了,实现交给它自身去处理,就是extern