通向天才之路 : 把模块塞到动态连接库里去
你还在用静态链接库生成一个巨大的EXE文件吗?过时了,换动态链接库吧。其中的好处被微软说了一大堆,我就不在这里废话了。说实话,这样做没什么好处,只是大家都这样做,我也跟着就这样做,仅仅如此。
为此,在下特意看了一些资料,微软那帮人的资料虽然详细,但是实在是罗嗦。唉,不如拉个人来问问来的快,呵呵。
说回正题,微软的DLL标准可真是麻烦,除了标准的C/C++外还有MFC DLL和扩展的MFC DLL,汗~~。不过我只对写标准C/C++的DLL感兴趣。其实这个接口也很简单,在DLL那边只需要在函数声明前加一个前缀,就可以在主程序中得到这个函数的入口地址了,之后想干什么都可以。具体的做法如下:
// 输出函数的前缀
#define DLL_EXPORT extern "C" __declspec( dllexport )
DLL_EXPORT VOID Func()
{
...
}
简单吧?这样DLL这边就搞定了,你还可以输出类和全局变量,但是我是不需要了。
// 输出类的前缀
#define DLL_CLASS_EXPORT __declspec( dllexport )
// 输出全局变量的前缀
#define DLL_GLOBAL_EXPORT extern __declspec( dllexport )
之后是EXE这边,虽然多了点东西,不过还是很简单的。第一件要做的事情是LoadLibrary,把要想要打开的DLL载入。
HINSTANCE hInst = NULL;
hInst = LoadLibrary("Render.dll");
if (!hInst)
{
MessageBox(g_hWnd,"无法加载 Render.Dll ","Error",MB_OK);
}
还记得上面我声明的那个Func()函数吗?我不能直接得到那个函数,但是可以把那个函数的地址取出来。其实函数地址使用起来和函数是一样的。只不过,为了使用方便,需要定义一个函数指针的类型。如果要指向上面的那个Func(),则它的函数指针的类型定义如下:
typedef void (CALLBACK* LPFUNC)(void)
之后需要做的是声明一个指针,然后得到DLL中Func()的地址。GetProcAddress函数的第一个参数是之前得到的DLL的实例句柄,后面一个是DLL中那个函数的函数名。
LPFUNC pFunc = NULL;
LPFUNC pFunc = (LPFUNC)GetProcAddress(hInst, "Func");
OK,到此为止已经差不多完成了,只欠最后一步调用那个函数:
pFunc();
以上的知识是基础中的基础,接下来才是技巧所在,所以一定要认真看哦!
-----------------------------------------------------------------------------
你一定想过把整个渲染模块制作成一个对象,然后得到这个对象的指针后就可以任意渲染了。如果全部写成代码,那无疑是最快捷的,但是缺点是很会暴露细节给不要或者你不希望的人看到,甚至于拿到了整个渲染模块的代码,这是相当可怕的。当然你也可以制作成静态链接库(Lib),这无疑是个不错的想法,但是缺点也是非常明显的。你可能会发现在发布版本中想替换调其中一些部分是不可能的,而对开发者而言使用静态链接库则更苦:他不得不使用你所用得编译器或是相关的开发环境。哦~天哪,这对于我而言也是个不小的麻烦,我想用GCC编译我的主程序,因为我想试试我刚拿到的一个新的十分小巧的集成开发环境:Relo v0.99。而可恶的微软却只提供了VC编译器的Lib,迫使我不得不使用VC来开发图形渲染模块。所以我也就不得不做好写DLL的心里准备。
于是我动手干了。我做了许多打算,最后发现最简单的方法是最好的方法。所以我按照我最原始的本能开始设计我的DLL。我做了一些限制:这样让程序看起来比较简单,条理也较为清楚。我规定每个DLL引擎对象都需要有一个ID来标识,以及DLL中唯一的接口函数:GetEngineObject。它的作用就是向主EXE程序返回一个DLL中与所给ID相对应的对象的地址。只要你愿意,你可以通过这个函数得到所有在那个DLL中的对象的地址。我想,这样的限制应该不过分吧。
对于主EXE来说,要做的事情就是像上面的那些代码一样,得到GetEngineObject函数的地址,然后导出全部EXE需要的引擎对象。对于DLL来说,则需要解决一个如何让GetEngineObject得到引擎对象地址的问题。这个问题其实也不难解决,只要在引擎对象中声明一个全局的队列,当创建了一个需要暴露给EXE的引擎对象的时候,把它的地址和相对应的ID写到这个全局的队列中。而当主EXE调用GetEngineObject的时候,GetEngineObject就到这个全局队列中查找给它的ID,并返回对应的地址。实现一个队列我想对各位看官来说并不难吧,呵呵。
OK,你的程序现在已经可以得到DLL中的对象了,现在你可以放心的把你的DLL交给需要它的人了。