那么界面部分这里就不多介绍了。用 BCB 也就是拖拖控件的。我们先来看下如何实现和主逻辑模块交换信息。
1, 首先我们需要一个结构体来组织需要交换的信息。
typedef struct _ControlSetting {
u8 beginGJ;
u8 basePos;
u8 onlyMoney;
u8 MLev;
Location baseLoc;
u32 area;
u8 redp;
u8 bluep;
u8 hgp;
u8 ridep;
} ControlSetting;
这个结构组织了设置挂机需要的信息。当然和界面的控件一一映射。比如 area 就是挂机的范围, baseLoc 就是挂机的坐标, MLev 是要打的怪物等级。
typedef struct _RoleInfo {
u32 knownVal;
char name[16];
u32 money;
Location pos;
u16 tred;
u16 tblue;
u16 red;
u16 blue;
u16 id;
u8 type;
u8 level;
u8 hg;
u8 ride;
} RoleInfo;
这个结构是主逻辑模块传回的当前角色信息,也就一些值,包括血量,蓝,坐标啊。
有了结构我们需要找到种方式将它传到游戏中运行的逻辑模块。前面也说过几种方式,我是懒人。。。当然选择最简单的 -- 内存文件映射。
下面是这部分操作接口的头文件(在主程序和主逻辑模块都共用以下的接口和上面的结构):
[ProcessDataManager.h]
int OpenChannel(int isOpen); // 打开内存文件映射。
int GetData(void* buf); // 读取通道中的数据。
int SetData(void *buf,int idx,int len); // 向通道里放数据。
void CloseChannel();
我们用通道时会用一个公共 Buf 。
#define BUF_SIZE 1024
char *pBuf;
这个 buf 分两部分,前 512Byte 是供主程序设置来自用户设定的挂机参数(也就是在界面设置的值)。后 512Byte 是给主逻辑模块更新游戏角色当前信息用以显示在主程序界面上给你看。
我们来看下这几个接口的实现:
上面看的 szName 定义如下,它实际上通道(内存文件映射)名,随便起就好。但要注意两点:
1, 通道两边要共用这个名,因为它是标识这个通道的。如果不同的话,要么就和其它通道对接了,要么就建了个新的。
2, 这个通道名不能和其它重复,要保证唯一性。所以尽量起的看似复杂随机些。
char szName[]=TEXT("Global//SockHookerMappingObject115736");
这样在主程序里就可以调用以下代码来获得角色信息并显示到界面上了:
void ControlManager::UpdateRoleInfo()
{
char buf[1024];
GetData(buf);
memcpy((void*)&CrtRoleInfo,&buf[512],sizeof(RoleInfo));
}
当然可以调用下面函数来将挂机的参数信息设置给主逻辑模块:
void ControlManager::UpdateControlSetting()
{
SetData((void*)&CrtControlSetting,0,sizeof(ControlSetting));
}
应该看到这里使用了一种编程手法上的技巧,概念上结构体就内存嘛。我们根本无需在设置时给他们一一赋值。拷贝整个内存就 OK ,但有个需要注意的地方。主程序和主逻辑模块( sockhookFun.dll )要共用相同的结构。
其实拷贝结构甚至可以这样玩:
CrtControlSetting = *(ControlSetting*)buf;
Buf 就是块内存嘛。 (ControlSetting*) 其实是告诉编译器以什么样的组织结构操作这块内存。一个描述而已。 *(ControlSetting*) 也就是以 ControlSetting 这种结构取内存中的值。和内存大小无法。只要大于 ControlSetting 这个结构就 OK 。这种拷贝结构体的技巧后面会常用。所以这边解释下。
再比如 CrtRoleInfo 可以:
CrtRoleInfo = *(RoleInfo*)(buf+512);
或者 : CrtRoleInfo = *(RoleInfo*)(&buf[512]);
这篇先介绍到这里,后面会介绍技术性比较强的注入和布局主逻辑模块的实现。