2013年 总版技术专家分年内排行榜第一
2014年 总版技术专家分年内排行榜第三
2010年6月 .NET技术大版内专家分月排行榜第二
2010年7月 .NET技术大版内专家分月排行榜第三2010年5月 .NET技术大版内专家分月排行榜第三
2010年 总版技术专家分年内排行榜第一2009年 总版技术专家分年内排行榜第一
2011年 总版技术专家分年内排行榜第二
本帖子已过去太久远了,不再提供回复功能。C#简单游戏外挂制作(以Warcraft Ⅲ为例)
C#简单游戏外挂制作(以Warcraft Ⅲ为例)
网上有很多游戏外挂制作的教程,大多是讲针对大型网络游戏的,主要包含一些抓包、反汇编、C++的知识综合。事实也如此,常见的外挂都是使用VC++写的,从来没有过C#或者其他.NET语言编写的外挂。
作为微软.NET技术的忠实粉丝,这难免是一种遗憾。不过不要紧,下面流牛木马就教大家两招,包教包会,免收学费。
其实作为游戏外挂来说,主要就是三个功能:模拟键盘操作、模拟鼠标操作、修改内存数据。修改内存数据比较难,但模拟鼠标键盘的操作却很简单。很多流行游戏的外挂,都可以只通过模拟鼠标键盘来实现,例如:劲舞团、QQ音速、连连看、各类网页游戏,以及各类大型网游中的自动打怪、自动吃药等等。
Warcraft Ⅲ,学名魔兽争霸之冰封王座,俗称魔兽,简称war3,在最近六七年风靡全球。最近两年,war3在中国又掀起了玩DOTA的新高潮。
本文制作DOTA游戏中的显血、改键外挂为例,简单地介绍如何使用C#语言制作游戏外挂。
最终界面如下:
本示例包含两个功能:显血;将Q键改为小键盘的7键。玩war3的同学都知道,这两个功能对于war3(尤其是DOTA)相当重要。
首先简单介绍一下,外挂程序模拟键盘的原理。
外挂程序与游戏程序是两个不同的进程。外挂程序使用Windows提供的API找到游戏程序的进程,并设置键盘钩子(什么叫做钩子?你不知道,但百度知道。)设置完钩子后,我们再监控游戏进程中用户的按键,并根据用户需求进行处理,完成某些模拟键盘动作。
了解了这个过程之后,我们就可以开始整理思路了。完成外挂一共需要以下四个步骤:
一、声明Windows API 中的函数和常量
//键盘Hook结构函数 [StructLayout(LayoutKind.Sequential)] & publicclass&KeyBoardHookStruct & { & publicint&vkC & publicint&scanC & publicint& & publicint& & publicint&dwExtraI & } & #region&DllImport //设置钩子 [DllImport(&user32.dll&,&CharSet&=&CharSet.Auto,&CallingConvention&=&CallingConvention.StdCall)] & publicstaticexternint&SetWindowsHookEx(int&idHook,&HookProc&lpfn,&IntPtr&hInstance,&int&threadId); & [DllImport(&user32.dll&,&CharSet&=&CharSet.Auto,&CallingConvention&=&CallingConvention.StdCall)] & //抽掉钩子 publicstaticexternbool&UnhookWindowsHookEx(int&idHook); & [DllImport(&user32.dll&,&CharSet&=&CharSet.Auto,&CallingConvention&=&CallingConvention.StdCall)] & //调用下一个钩子 publicstaticexternint&CallNextHookEx(int&idHook,&int&nCode,&IntPtr&wParam,&IntPtr&lParam); & //取得模块句柄& [DllImport(&kernel32.dll&,&CharSet&=&CharSet.Auto,&CallingConvention&=&CallingConvention.StdCall)] & privatestaticextern&IntPtr&GetModuleHandle(string&lpModuleName); & //寻找目标进程窗口&[DllImport(&USER32.DLL&)] publicstaticextern&IntPtr&FindWindow(string&lpClassName, & string&lpWindowName); & //设置进程窗口到最前&[DllImport(&USER32.DLL&)] publicstaticexternbool&SetForegroundWindow(IntPtr&hWnd); & //模拟键盘事件&[DllImport(&User32.dll&)] publicstaticexternvoid&keybd_event(Byte&bVk,&Byte&bScan,&Int32&dwFlags,&Int32&dwExtraInfo);//释放按键的常量 privateconstint&KEYEVENTF_KEYUP&=2;&&
本例所使用的函数比较少,它们都在系统的USER32.dll里,包括:设置和取消钩子、调用下一个钩子、导入进程、模拟键盘等等。我们依次导入它们。 这些函数的命名规范合理,几乎只根据函数名就能知道其功能。 如果读者对于其中的某些函数不熟悉,请自行搜索MSDN。
二、使用Windows API设置钩子
有了以上windows API函数的声明,下一步就是设置钩子了。
寥寥两行代码,但包含了相当丰富的内容。
//委托 publicdelegateint&HookProc(int&nCode,&IntPtr&wParam,&IntPtr&lParam);&publicvoid&Hook_Start() & { & //&***键盘钩子 if&(hHook&==&0) & { & KeyBoardHookProcedure&=&new&HookProc(KeyBoardHookProc); & hHook&=&SetWindowsHookEx(WH_KEYBOARD_LL,&KeyBoardHookProcedure,&GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName),&0); & } & }&&
先介绍一下设置钩子的明星函数:SetWindowsHookEx 。它的参数说明如下。
SetWindowsHookEx(
idHook: I {钩子类型}
lpfn: TFNHookP {函数指针}
hmod: HINST; {包含钩子函数的模块(EXE、DLL)句柄; 一般是 HI 如果是当前线程这里可以是 0}
dwThreadId: DWORD {关联的线程; 可用 GetCurrentThreadId 获取当前线程; 0 表示是系统级钩子}
): HHOOK; {返回钩子的句柄; 0 表示失败}&
请注意lpfn这个参数。上面的解释是&函数指针&。在C#中,是不能直接使用指针的,更不要说函数指针了。我们可以采用C#中的委托(delegate)来实现函数指针的功能。
于是乎,在上面的代码中,我们定义了一个处理键盘消息函数的委托KeyBoardHookProcedure = new HookPro(KeyBoardHookProc),并将它作为参数传入SetWindowsHookEx 内。KeyBoardHookProc就是被委托的具体函数。
三、监控用户操作
设置好钩子后,我们可以在被委托的函数中写入监控用户操作与模拟键盘的代码。
publicstaticint&KeyBoardHookProc(int&nCode,&IntPtr&wParam,&IntPtr&lParam) & { & //监控用户键盘输入&KeyBoardHookStruct&input&=&(KeyBoardHookStruct)Marshal.PtrToStructure(lParam,&typeof(KeyBoardHookStruct)); //截获Home&键&if&(input.vkCode&==&(int)Keys.Home) { & //此处写入其他操作逻辑&} //&继续执行下一个钩子程序 return&CallNextHookEx(hHook,&nCode,&wParam,&lParam); & }&&
四、根据用户需要模拟键盘操作
显血功能:玩war3的都知道,war3自带的显血快捷键有3个。Alt键是显示所有单位生命,[ 键显示友方单位生命,] 键显示地方单位生命。外挂需要做的事情仅仅是模拟一直按着某个键不松手而已。由于Alt键与其他很多键构成组合键,故我们不能模拟长按Alt,否则会影响正常游戏。我们的解决方案应该是模拟长按 [ 键和 ] 键。代码如下:
//获得魔兽程序的句柄 IntPtr&wcHandle&=&FindWindow(null,&&Warcraft&III&); & //如果钩子有效 if&(wcHandle&!=&IntPtr.Zero) & { & //设置游戏窗口到最前 SetForegroundWindow(wcHandle);&byte&VK_NUM1&=&219;&//键盘上&[&键的代码。按[可显示友方单位生命值。 byte&VK_NUM2&=&221;&//&键盘上]&键的代码。按]可显示敌方单位生命值。 keybd_event(VK_NUM1,&0,&0,&0);&//长按[ keybd_event(VK_NUM2,&0,&0,&0);&//长按]&}&
改键: 小键盘(Numpad)上的快捷键很不方便按,所以很多玩家喜欢把小键盘上的键改到左边的字母键盘。玩DOTA的同学都知道,没有任何英雄的技能使用&Q&这个快捷键(召唤师有一种球是&Q&(不是技能))。于是我们把小键盘上的7键改到Q上,也不会造成任何冲突。方法也很简单:如果监控到用户按&Q&键,则像游戏进程发送小键盘上的&7&键。代码如下:
//如果用户按了Q键 if&(input.vkCode&==&(int)Keys.Q) & { & //获得魔兽程序的句柄 IntPtr&wcHandle&=&FindWindow(null,&&Warcraft&III&); & //如果钩子有效 if&(wcHandle&!=&IntPtr.Zero) & { & //设置游戏窗口到最前 SetForegroundWindow(wcHandle); & byte&VK_Q&=&(byte)Keys.NumPad7; & keybd_event(VK_Q,&0,&0,&0);//按下小键盘7 keybd_event(VK_Q,&0,&KEYEVENTF_KEYUP,&0);&//松开小键盘7 } & return&1; & }&
好了,到这里就把模拟键盘的外挂介绍完了。模拟鼠标与之非常类似,请用户自行揣摩。本文仅做抛砖引玉,欢迎感兴趣的朋友来流牛木马的博客进行讨论。
附件:外挂成品下载(运行需要.net 2.0以上环境)
参考文献:《精通.NET互操作》。感谢作者黄际洲、崔晓源的赠书,我终于学以致用了一回~ :)
H3C认证Java认证Oracle认证
基础英语软考英语项目管理英语职场英语
.NETPowerBuilderWeb开发游戏开发Perl
二级模拟试题一级模拟试题一级考试经验四级考试资料
软件测试软件外包系统分析与建模敏捷开发
法律法规历年试题软考英语网络管理员系统架构设计师信息系统监理师
高级通信工程师考试大纲设备环境综合能力
路由技术网络存储无线网络网络设备
CPMP考试prince2认证项目范围管理项目配置管理项目管理案例项目经理项目干系人管理
职称考试题目
招生信息考研政治
网络安全安全设置工具使用手机安全
生物识别传感器物联网传输层物联网前沿技术物联网案例分析
Java核心技术J2ME教程
Linux系统管理Linux编程Linux安全AIX教程
Windows系统管理Windows教程Windows网络管理Windows故障
数据库开发Sybase数据库Informix数据库
&&&&&&&&&&&&&&&
希赛网 版权所有 & &&中国领先的IT技术网站
51CTO旗下网站
浅析C#插件式程序开发经验
本文作者将给大家分享一下C#插件式程序开发经验,在共同讨论的过程中能共同进步。
作者:杨盛超来源:博客园| 10:15
在网上找了下插件式编程的资料,这里自己先借鉴下别人的,同时发现有自己的看法,不过由于本人水平有限,不一定有参考价值,写出来一方面是为了总结自己,以求提高,另一方面也希望各为朋友看到我的不足,给我提出宝贵意见。
什么是插件式编程
提起插件式,我们首先想到的是firefox,用过firefox的人都知道它是一个插件式程序。当一个功能需要,完全可以从网上下载一个插件后,重启后,就能使用。这个功能给我们带来许多的方便之处,这就是插件式程序的好处。
插件的本质在于不修改程序主体(平台)的情况下对软件功能进行拓展与加强,当插件的接口公开后,任何公司或个人都可以制作自己的插件来解决一些操作上的不便或增加新功能,也就是真正意义上实现&即插即用&软件开发。
平台+插件软件结构是将一个待开发的目标软件分为两部分,一部分为软件的主体或框架,可定义为平台,这是预先编译后的程序。另一部分为功能或补充模块,可定义为插件。这个就是后来要进行***的插件程序。
假设你的程序已经部署在用户的计算机上,并且能够正常运行了。但是有一天,用户打来***&&他们需要增加新的功能。确定了用户的需求后,你竟然发现原有的软件架构已经无法胜任新增任务的需求&&你需要重新设计这个应用了!但问题是,就算你又用了一个开发周期完成了用户需要的应用,切不能保证用户的需求不会再次变更。也就是说,需求蔓延的可能性依然存在。因此,这种情况下插件架构更能显示出它的优越性。
可以这么说,用它可以带来方便的地方,而且开发它,也很简单。而且这样的主程序根本就不需要改动。需要插件时,拿来就能用,插件更新时,也只需更新这个插件即可。
从程序开发这角度,一般是先开发主程序,决定哪些功能由主程序来完成,然后再建立接口,申明接口的内容,这些内容决定着插件功能的扩展,及方向的。这些都是有主程序开发者预先准备好的。插件开发者,从主程序开发者那里得到接口的内容,并书写继承这些接口的类,来完成具体的功能。
下面来写个例子,这个例子没实际意义,纯属学习思想。例子是网上的经过自己改造的,发现别人某些地方不合理。
首先,新建一个类库,里面定义接口,这里定义两个方法,一个有返回值的,一个无返回值的。
using&S &using&System.Collections.G &using&System.T &namespace&IMsg{&&& &&&&&&&&&{&&&&&&& &&void&OnShowDlg();&&&& &&&&&string&OnShowInfo();& &&&&}}&
将上面的类库生成IMsg.dll,新建一个类库MYPlugin1,添加刚出的引用,分别新建两个类来实现IMsg中定义的接口。
using&S &using&System.Collections.Gusing&System.Tusing&IM &namespace&MYPlugin1{&&&& &public&class&myConsole&:&IMsgPlug&&& &&{&&&&&&& ®ion&IMsgPlug&成员&&&&&&& &&public&&void&OnShowDlg()&&&&& &&&&{&&&&&&&&& &&&&Console.WriteLine(&控制台调用插件的OnShowDlg方法&);&&&&& &&&&}&&&& &&&&&public&&string&OnShowInfo()&&&& &&&&&{&&&&&&&&&&&&return&&myConsole&;&&&& &&&&&}&&&&&&&endregion&& &&&}} &using&S &using&System.Collections.G &using&System.T &using&System.Windows.F &using&IMnamespace&MYPlugin1{& &public&&class&MYDlg:Form,IMsgPlug&&& &&{&&&&&&®ion&IMsgPlug&成员& &&&&&&&&public&&void&OnShowDlg()&&& &&&&&&{&&&&&&&&&&&&this.Text&=&&插件子窗体&;&&&&&&&&&&&&this.ShowDialog(); &&&&&public&&string&OnShowInfo()&& &&&&&&&{&&&&&&&&&&&&return&&MyDlg&;&&&&& &&&&}&&&&&&&endregion&& &&&}}&
将上面的都生成dll,生成目录可以设置为新建exe工程的bin目录plugins文件夹下。Plugins文件夹是新建的,专门存放插件的。 新建一个 WinForm项目,来使用刚才的插件.
using Susing System.Collections.Gusing ponentMusing System.Dusing System.Dusing System.Tusing System.Windows.Fusing System.Cusing System.IO;using System.Rnamespace MsgBoxMain{
public partial class FormMain : Form
/// &summary&
/// 存放插件的集合
/// &/summary&
private ArrayList plugins = new ArrayList();
public FormMain()
InitializeComponent();
/// &summary&
/// 载入所有插件
/// &/summary&
private void LoadAllPlugs()
//获取插件目录(plugins)下所有文件
string[] files = Directory.GetFiles(Application.StartupPath + @&\plugsins&);
foreach (string file in files)
if (file.ToUpper().EndsWith(&.DLL&))
Assembly ab = Assembly.LoadFrom(file);
Type[] types = ab.GetTypes();
foreach (Type t in types)
//如果某些类实现了预定义的IMsg.IMsgPlug接口,则认为该类适配与主程序(是主程序的插件)
if (t.GetInterface(&IMsgPlug&)!=null)
plugins.Add(ab.CreateInstance(t.FullName));
listBox1.Items.Add(t.FullName);
catch (Exception ex)
MessageBox.Show(ex.Message);
private void btnLoadPlug_Click(object sender, EventArgs e)
LoadAllPlugs();
//调用插件的方法
private void btnExecute_Click(object sender, EventArgs e)
if (this.listBox1.SelectedIndex == -