分享
 
 
 

Half Life 2 Structure

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

本来看hl2源码的目的是想学习他是如何使用IO Completion Port来实现网络通信的。但只是找到了一个文件ThreadedTCPSocket文件。源文件里使用了IO Completion Port方式封装通信类。但我在其他地方实在找不着调用此类的直接应用。所以,我就想从头开始看源码也许能找到使用此类的模块。误打误撞,将之前没看懂的部分弄明白了。将它写出来与大家共享。

我们知道主函数在launcher_main模块内。而launcher_main实际调用的是launcher.dll里输出的LauncherMain函数来完成程序初始化操作。

HINSTANCE launcher = LoadLibrary("launcher.dll");

...

LauncherMain_t main = (LauncherMain_t)GetProcAddress( launcher, "LauncherMain" );

return main( hInstance, hPrevInstance, lpCmdLine, nCmdShow );

上面列出的就是在launcher_main模块里main.cpp的代码。接下来我们再去看anucher模块里看看LauncherMain函数都做了些什么。

在看这部分的源码之前,先说明一下。游戏的功能分散在不同的模块里,均以动态链接库的形式存在。有几个比较重要的先介绍一下。

filesystem_stdio.dll

materialsystem.dll

engine.dll

vgui2.dll

vguimatsurface.dll

具体各实现什么功能我也不清楚。先不管这么多,知道他们都很重要就行了。

LauncherMain函数前面部分也没什么。有些还真不明白,也先跳过。到了while循环这,现在注意了有个很重要的函数要引起我们的注意。那就是,LoadAppSystems。LOAD很明显就是装载东西,但具体装载什么呢?让我们进入函数内看看。进入函数内我们看到了这么一段:

// Start up the file system

FileSystem_LoadFileSystemModule();

if ( !FileSystem_Init() )

return false;

这是此函数的第一部分。从注释中可以了解这些语句的作用是启动文件系统。二话不说,先看去看FileSystem_LoadFileSystemModule()函数。此函数就在此工程的filesystem.cpp文件里。

char *sFileSystemModuleName = "filesystem_stdio.dll";

从第一句申明可以看出,是要装载filesystem_stdio.dll文件。

g_pFileSystemModule = Sys_LoadModule(sFileSystemModuleName);

接着调用Sys_LoadModule装载此动态链接库,并获取其句柄。

g_FileSystemFactory = Sys_GetFactory(g_pFileSystemModule);

接着调用Sys_GetFactory。从字面上解释是从此动态链接库内获得什么工厂。其实是得到一个函数地址,供之后的调用。

g_pFileSystem = (IFileSystem *)g_FileSystemFactory(FILESYSTEM_INTERFACE_VERSION, NULL);

最后利用刚才得到的函数地址,调用此输出函数。并返回一个值赋给g_pFileSystem。然后程序退出。g_pFileSystem是个全局变量就在此文件的开始处申明,是个指向IFileSystem的指针。从这些代码中可以得出FileSystem_LoadFileSystemModule函数的作用就是为了获得动态链接库内地某个东西,并将其保留在本进程的变量里。

这里有很多类型都不明白是些什么。例如,IFileSyste;还有那些函数,Sys_LoadModule、Sys_GetFactory等。很乱,有种理不清头绪的感觉。不管,先看函数再说。刚才列出的两个函数都存在于interface.cpp文件里。Sys_LoadModule函数只要去除那些linux下的代码其实很简单就是装载动态链接库并返回句柄。与我们刚才猜想的一样。好,跳过这个函数再看Sys_GetFactory。

return reinterpret_cast(GetProcAddress(hDLL, CREATEINTERFACE_PROCNAME));

这个函数也很简单,有用的就两句。重要的就是上面这句。GetProcAddress,我知道这个函数不就是获取dll内的函数地址嘛。再来看看具体是哪个函数,CREATEINTERFACE_PROCNAME定义为CreateInterface。这个定义是在interface.h文件里找到的。也就是说所谓的工厂就是指的这个函数CreateInterface。这时我就有点明白了,动态链接库藉由此函数创建什么接口提供给调用此动态链接库的使用方。有点眉目了,再继续查找CreateInterface函数。在interface.h里我们看到了这么一句:

DLL_EXPORT void* CreateInterface(const char *pName, int *pReturnCode);

说明此dll确实输出了CreateInterface。果然,在interface.cpp里我们找到了此函数的定义。也不复杂,就是利用传入的字符参数与其他变量做比较。如果存在相同的,则返回一个函数;否则置错误状态值,并返回空指针。又有点糊涂了,不是返回变量嘛,怎么还是函数?而且函数内做比较用的变量是个类的静态变量。在interface.cpp文件的前面我们找到了它,InterfaceReg *InterfaceReg::s_pInterfaceRegs = NULL;。没办法,再去看InterfaceReg类的申明。也没什么,在CreateInterface函数里几乎所有的类成员变量都有所涉及。而且这些变量都很好理解。InterfaceReg *m_pNext,从这个成员变量可以得知此类的实例会形成一个链表。谁来创建一个类似这样的链表呢?构造函数里好像有所体现。真的是这样,在类事例创建之时,实例会将自己加入到此链表内。除此之外我们好像得不出其他什么信息了。但还是有疑问,谁来创建这个实例。不可能在动态链接库之外,但在模块内我们又没发现其他的信息。

不会吧,又卡壳了!唉,没办法谁让我们看这些没有文档的源码呢。关了这个工程,气死我了。我又打开了Filesystem_stdio.dsp工程。刚才好像是在调用这个dll,说不定能找到什么有用的信息。为什么这个工程里也有interface.cpp和interface.h?打开这两个文件看看,一模一样。明白了,通用的文件。说不定猫腻还在这两个文件里。再仔细找找呗。.cpp没什么,那就去看.h。里面有什么看仔细了,两个类的申明,还有一些函数申明,中间有一大串的#define(看不懂),一个enum,然后是extern。没了。不会吧?还是不能偷懒,看那些#define。苦命啊!有门,第一句就是一个静态InterfaceReg类型的定义。生成了一个类实例不就是创建了链表嘛。接下来三个大同小异只是构造方式不一样而已。再依次在工程内查找这四个#define。EXPOSE_INTERFACE_FN没有,EXPOSE_INTERFACE也没有。靠!骗人的嘛,还不出现。查EXPOSE_SINGLE_INTERFACE_GLOBALVAR。嘿嘿,有了。在filesystem_stdio.cpp文件的最后两行。

EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Stdio, IFileSystem, FILESYSTEM_INTERFACE_VERSION, g_FileSystem_Stdio );

EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Stdio, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION, g_FileSystem_Stdio );

CFileSystem_Stdio、IFileSystem和FILESYSTEM_INTERFACE_VERSION都明白,前两个是类,后一个是字串。g_FileSystem_Stdio,这看起来像个全局变量。嘿,小样儿!就在前面一句申明了此变量。

static CFileSystem_Stdio g_FileSystem_Stdio;

哈哈!我知道了在dll初始化时就会生成一个InterfaceReg类实例。但EXPOSE_SINGLE_INTERFACE_GLOBALVAR这个具体作了些什么还是不明白。好继续分解。

#define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) static void* __Create##className##interfaceName##_interface() {return (interfaceName *)&globalVarName;} static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName);

看上去好像很复杂。没关系就将上面的实际的例子替代一下。

static void* __CreateCFileSystem_StdioIFileSystem_interface()

{

return (IFileSystem*)&g_FileSystem_Stdio;

}

static InterfaceReg __g_CreateCFileSystem_StdioIFileSystem_reg(__CreateCFileSystem_StdioIFileSystem_interface, FILESYSTEM_INTERFACE_VERSION);

这样看上去清除多了。一个函数定义,一个InterfaceReg类实例的创建。函数实际上返回的是个全局变量,类型为CFileSystem_Stdio。怎么强制转换为IFileSystem?哦,看了CFileSystem_Stdio就知道了。IFileSystem是它的虚基类。是可以这么做。再来看类实例的创建。去看他的构造函数。第一个参数是个函数,第二个是个字串。函数将放在此类实例的成员变量m_CreateFn里,字串赋给m_pName。传给构造函数的第一个参数就是利用EXPOSE_SINGLE_INTERFACE_GLOBALVAR一块生成的一个函数。

我明白了。构造InterfaceReg的FILESYSTEM_INTERFACE_VERSION与下面这句的g_pFileSystem = (IFileSystem *)g_FileSystemFactory(FILESYSTEM_INTERFACE_VERSION, NULL)内使用的FILESYSTEM_INTERFACE_VERSION,是同一个字串。其实就是将动态链接库内的g_FileSystem_Stdio全局变量地址赋给调用方的全局变量g_pFileSystem。这样一来,调用方就可以使用动态链接库内类实例。

看下图的图例解释。

这确实是hl2输出接口的方式。在其他模块里我们也找到了interface.cpp和interface.h两个文件。说明所有的模块都是通过这种方式输出接口的。

上面我们分解的是hl2里动态链接库输出接口的方式。CreateInterface应该是恒久不变的。利用此接口我们能存取动态链接库内的类的实例。而且这种方式还能实现版本控制。保证能够调用到正确的类实例。程序在不断发展修正,必然有更新的版本出来。如果在新版本的动态链接库内不存在我们需要的接口,程序也不会调用到错误的接口而是转向到其他正确的动态链接库内去查找需要的接口。这样保证了程序不会出现异常,而是以当前能够运行的版本运行程序。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有