C# DIRECTX INPUT 模拟 (鼠标玩FBA街机)

博主通过C#编程实现了一个全局鼠标钩子,目标是使用鼠标控制街机游戏模拟器FBA。经过一系列尝试,解决了模拟键盘消息无法被FBA识别的问题,最终成功用鼠标控制游戏,但还需优化程序以实现更流畅的操作。涉及到的技术包括全局鼠标钩子、模拟键盘事件、窗口句柄操作等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1943很经典的街机游戏

我突然想用鼠标来控制飞机可以吗?于是网上找了一下,没有找鼠标移动映射键盘的软件。

自己建了一个C#项目,加入别人写好的全局鼠标钩子。

很简单的代码,实现了获取全局鼠标坐标并做相应的操作:

using Gma.System.MouseKeyHook;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace GetMousePoint
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
        public static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo);

        private IKeyboardMouseEvents m_GlobalHook;

        int x_tmp;
        int y_tmp;

        public Form1()
        {
            InitializeComponent();         
            Control.CheckForIllegalCrossThreadCalls = false;
            Subscribe();
        }


        public void Subscribe()
        {
            // Note: for the application hook, use the Hook.AppEvents() instead
            m_GlobalHook = Hook.GlobalEvents(); 
            m_GlobalHook.MouseMoveExt += M_GlobalHook_MouseMoveExt;

        }

        private void M_GlobalHook_MouseMoveExt(object sender, MouseEventExtArgs e)
        {
            label1.Text = e.X.ToString() + "," + e.Y.ToString();

            if (e.X > x_tmp) {label2.Text = "→"; keybd_event(Keys.D, 0, 0, 0); }
            if (e.X < x_tmp) {label2.Text = "←"; ; keybd_event(Keys.A, 0, 0, 0);}

            if (e.Y < y_tmp) {label4.Text = "↑";; keybd_event(Keys.W, 0, 0, 0);}
            if (e.Y > y_tmp) {label4.Text = "↓";; keybd_event(Keys.S, 0, 0, 0);}

            x_tmp = e.X;
            y_tmp = e.Y;         

        } 
   }

}

 但FBA V2.4.0 版本的游戏模拟器油盐不进,对它发消息,它没有反应,SPY++看不到它的任何消息。今天晚上时间不够了,就研究到这里。

------2022.2.20------

仔细看了一下,是SPY++64位32位的问题,确认可以收到消息后,模拟键盘按键给FBA发了键盘消息。又出现了新问题,SPY++可以看到程序发给FBA的消息和实际的消息完全一样,但是FBA不能识别。

可能是因为FBA使用了DIRECTX INPUT,一般的模拟键盘消息对它无效,程序也尝试了线程挂接,只是没再进一步使用HOOK注入。

找到一个WINIO方法,据说可以对付DIRECTX INPUT不识别消息的顽症。

C#的例子程序写的很完整,但是WINIO32.DLL居然网上下载不到,文章地址:

一个C#写的模拟键盘输入的例子_lucky811的专栏-CSDN博客

那么,后面还需要找一个WIN10,VS2019环境下可以使用WINIO32.DLL的DEMO。

-------2022.2.25-----------

WINIO32.DLL这条路不通,在百度搜索 directx input 模拟键盘,在知乎上有个回答,还有一个是WIN10场景最新的场景。看来可行。 

-------2022.2.26-----------

今天终于成功了,用右手控制鼠标控制了一下飞机,但是要达到极致体验,程序还需要调试一下,让鼠标的移动跟上飞机。比如:一个方向一个线程,因为程序只会傻填键盘缓冲区,四个方向都是单独线程控制才比较合理。

 全部的C# 代码如下(鼠标中键工作,右键停止):

using Gma.System.MouseKeyHook;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;


namespace GetMousePoint
{

   public partial class Form1 : Form
    {
        [DllImport("user32.dll")] static extern IntPtr GetForegroundWindow();
        [DllImport("user32.dll")] static extern bool SetForegroundWindow(IntPtr hWnd);
        //----------------------------------------------------------------------
        [DllImport("user32 ")]
        private static extern IntPtr EnumChildWindows(int hWndParent, EnumWindowsProc lpEnumFunc, int lParam);
        public delegate bool EnumWindowsProc(int hWnd, int lParam);

        [DllImport("user32.dll", EntryPoint = "GetClassName")]
        public static extern int GetClassName(int hWnd, StringBuilder lpString, int nMaxCont);

        [DllImport("User32.dll")]
        static extern int GetWindowText(IntPtr handle, StringBuilder text, int MaxLen);
        //------------------------------------------------------------------------
        [DllImport("user32.dll", EntryPoint = "keybd_event", SetLastError = true)]
        public static extern void keybd_event(Keys bVk, byte bScan, uint dwFlags, uint dwExtraInfo);
        [DllImport("user32.dll")] 
        static extern byte MapVirtualKey(byte wCode, int wMap);


        public static List<WinObject> ListWin = new List<WinObject>();

        private IKeyboardMouseEvents m_GlobalHook;

        int x_tmp;
        int y_tmp;      


        int mouse_x = 0;
        int mouse_y = 0;
        bool IsOpen = false;

        MouseEventExtArgs gv_e;


        public Form1()
        {
            InitializeComponent();
            //加载全局钩子
            Subscribe();
            Control.CheckForIllegalCrossThreadCalls = false;
              
            Thread t11 = new Thread(new ThreadStart(Worker11));
            t11.Start();              
            Thread t12 = new Thread(new ThreadStart(Worker12));
            t12.Start();
            Thread t13 = new Thread(new ThreadStart(Worker13));
            t13.Start();
            Thread t14 = new Thread(new ThreadStart(Worker14));
            t14.Start();



            Thread t2 = new Thread(new ThreadStart(Worker2));
            t2.Start();                 
            Thread t3 = new Thread(new ThreadStart(Worker3));
            t3.Start();

        }




        public void Worker11()
        {
            while (true)
            {
                Thread.Sleep(5);
                if (IsOpen)
                {
                    if (mouse_y == -1) { MyKey(Keys.S); }
                   
                }

            }
        }


        public void Worker12()
        {
            while (true)
            {
                Thread.Sleep(5);
                if (IsOpen)
                {              
                    if (mouse_y == 1) { MyKey(Keys.W); }
                }

            }
        }


        public void Worker13()
        {
            while (true)
            {
                Thread.Sleep(5);
                if (IsOpen)
                {
                    if (mouse_x == 1) { MyKey(Keys.D); }                  

                }

            }
        }


        public void Worker14()
        {
            while (true)
            {
                Thread.Sleep(5);
                if (IsOpen)
                {                 
                    if (mouse_x == -1) { MyKey(Keys.A); }
                }

            }
        }



        public void Worker2()
        {
            while (true)
            {
                Thread.Sleep(100);

                if (gv_e != null)
                {
                    label1.Text = gv_e.X.ToString() + "," + gv_e.Y.ToString();

                    if (gv_e.X > x_tmp) { label2.Text = "→"; mouse_x = 1; }
                    if (gv_e.X == x_tmp) { label2.Text = "0"; mouse_x = 0; }
                    if (gv_e.X < x_tmp) { label2.Text = "←"; mouse_x = -1; }         

                    x_tmp = gv_e.X;
                
                }

               
            }
        }


        public void Worker3()
        {
            while (true)
            {
                Thread.Sleep(100);

                if (gv_e != null)
                {   

                    if (gv_e.Y < y_tmp) { label4.Text = "↑"; mouse_y = 1; }
                    if (gv_e.Y == y_tmp) { label4.Text = "0"; mouse_y = 0; }
                    if (gv_e.Y > y_tmp) { label4.Text = "↓"; mouse_y = -1; }

                
                    y_tmp = gv_e.Y;
                }

               
            }
        }

        public void Subscribe()
        {
            // Note: for the application hook, use the Hook.AppEvents() instead
            m_GlobalHook = Hook.GlobalEvents(); 
            m_GlobalHook.MouseMoveExt += M_GlobalHook_MouseMoveExt;       
            m_GlobalHook.MouseClick += M_GlobalHook_MouseClick;
        }       

        private void M_GlobalHook_MouseClick(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Middle)
            {
                EnumChildWindows(0, new EnumWindowsProc(EnumWindowCallBack), 0);
                if (ListWin.Count > 0) { label6.Text = "locked windows"; }
                SetForegroundWindow(ListWin.FirstOrDefault().hand);

                IsOpen = true;
                label3.Text = "IsOpen: true";
            };

            if (e.Button == MouseButtons.Right)
            {
                IsOpen = false; 
                label3.Text = "IsOpen: false";
                label6.Text = "UnLocked windows";
            }
        }      

        private void M_GlobalHook_MouseMoveExt(object sender, MouseEventExtArgs e)
        {
            
            gv_e = e;           
        }
       
       

        private void MyKey(System.Windows.Forms.Keys x)
        {             
                keybd_event(x, (byte)MapVirtualKey(Convert.ToByte(x), 0), 0, 0);
                Thread.Sleep(5);
                keybd_event(x, (byte)MapVirtualKey(Convert.ToByte(x), 0), 0x0002, 0);       

        }



        public class WinObject
        {
            public bool isprocess { get; set; }
            public IntPtr hand { get; set; }
            public string ClassName { get; set; }
            public string text { get; set; }

        }

        public static bool EnumWindowCallBack(int hWnd, int lParam)
        {
            try
            {
                WinObject one = new WinObject();
                one.isprocess = false;
                one.hand = (IntPtr)hWnd;

                StringBuilder name = new StringBuilder(256);
                GetClassName(hWnd, name, 256);
                one.ClassName = name.ToString();

                StringBuilder text = new StringBuilder(100);
                GetWindowText((IntPtr)hWnd, text, 100);
                one.text = text.ToString();

                if (one.ClassName.Equals("FBA shuffle"))
                {
                    ListWin.Add(one);
                }

            }
            catch { }
            return true;
        }

        
    }

}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘欣的博客

你将成为第一个打赏博主的人!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值