关于C# windows发送消息

来源:互联网 发布:照片转手绘软件 编辑:程序博客网 时间:2024/05/21 13:21

 

 

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

原创粉丝点击