使用LIBCTINY.LIB为EXE和DLL文件减肥
2010-07-15 20:45:18 来源:WEB开发网LIBCTINY''s 的启动流程
现在看看 LIBCTINY 如何支持 DLLs 和 EXEs,一个窍门是消灭不必要的代码,让DLL 载入代码仅可能的小。Figure4 展现了一个极小的 DLL 启动代码。
当你 DLL 装载的时候,_DllMainCRTStartup 是你DLL最开始执行的地方而不是 DllMain。LIBCTINY 首先检查是不是 DLL_PROCESS_ATTACH,如果是,就调用 _atexit_init,_initterm 呼叫所有的静态构造器。 而函数的核心是调用 DllMain, 它是你的 DLL 代码的一部分。
DllMainCRTStartup最后要做的是检查 DLL 是不是要DLL_PROCESS_DETACH。如果是,调用 _DoExit。如前所叙,它将调用静态析构器。如果你对控制台 和 GUI 模式的启动代码感到好奇,可以看看 CRT0TCON.CPP 和 CRT0TWIN.CPP(这些代码都有相应的下载,可以在文章的开始处找到)。 另外一个要做的事就是找到 DLLCRTO.CPP (Figure4) 中下面一行:#pragma comment(linker, "/OPT:NOWIN98")
上面一行直接告诉连接器使用 /OPT:NOWIN98 开关。它的好处是你不要手工的添加到 makefile 文件或者 工程文件,我要说明的是,如果你使用 LIBCTINY, 请务必打开 /OPT:NOWIN98 开关。
使用 LIBCTINY.LIB
使用 LIBCTINY.LIB 是很简单的。你所要做的就是将LIBCTINY.LIB 加入到你的连接库列表当中。如果你使用 Visual Studio IDE,那就加到 Projects | Settings | Link tab,你编译的二进制类型无所谓 (console EXE, GUI EXE 或 DLL),只要 LIBCTINY.LIB 有正确的入口就可以了。 看看 Figure5 中的 TEST.CPP。
程序代码中只使用了 LIBCTINY.LIB 所实现函数的很少一部分,并且包含了一个静态的构造器和析构器调用。当我用Visual C++ 6.0 的普通编译参数时:CL /O1 TEST.CPP
结果可执行文件有 32768 个字节。现在把 LIBCTINY.LIB 链接上,CL /O1 TEST.CPP LIBCTINY.LIB
最终的可执行文件只有 3072 个字节。
你可能会担心这个LIBCTINY 是不完整的,举个例子,在TEST.CPP 中,有个 strtchr 的调用。 但是这些都没有问题,因为函数可以在 LIBC.LIB 或 LIBCMT.LIB 等 Visual C++ 提供的库中找到。 LIBCTINY.LIB 和 LIBC.LIB 都实现了一系列的函数,但是 LIBCTINY 显然要小得多。
最后,需要重申的是 LIBCTINY 并不适应所有的情况,比如,你使用了多线程,且使用了运行库的线程私有数据(译者著:指的是TLS-线程本地存储,比如为每个线程保留一个errno变量)的支持,LIBCTINY 就不合适,我一般是先试试,如果能够运转,那就太好了!如果不行,我就使用一般的运行库。
文章修正
在2000年十月的MSDN杂志上有篇我的文章 "Avoiding DLL Hell: Introducing Application Metadata in the Microsoft .NET Framework"。我写到:使用Visual C++ 6.0 #import 会使得编译器会读一个COM类型的库,并为所有库里面的接口产生一个 ATL 的头文件。同时指出 #import 产生了头文件,而不是 ATL。
Richard Grimes -- <<Professional ATL COM Programming (Wrox Press, 1998)>>的作者,友好地指出 #import 产生头文件为链接器支持的COM类,实际上是由 COMDEF.H 产生的。Richard 还说到,”链接器支持的COM类和ATL中支持的COM类有很多的不同。最大不同点就是ATL不使用C++异常。实际上,ATL类比链接器支持的COM类更加轻量级,所以我会很高兴用它来生成 ATL 代码。
我确实应该在我写之前多研究一下。我在 ATL方面的经验就仅限于 Visual C++ 的向导和自动生成的代码。我很少用#import ,没有足够的理由断言和 ATL 没有联系。感谢Richard ,指出我的错误,也会激励我以后做每件事情三思而后行。
本文配套源码
更多精彩
赞助商链接