贝贝从去年开始,开始需要在应用程序的开发时使用第三方的库文件。一看到一大堆的.dll人就晕了。别笑话我,菜鸟入门尤其难。刚开始,有样学样,别人加include,我就加include,别人加lib,我就加lib。但,加的是什么,为什么这么加,一直不懂。从OSG、shapelib、opticks到最近自己想编编cppunit。每次一开始编译,就开始头大,不知道哪里又会有点小问题等着我。一出问题,就一通include的乱加。这篇文章写写我对开源项目编译和应用的一些感性认识。包括动态链接/静态链接、lib/dll/.h文件、怎样在VS中设置包含目录等等。
首先,定位。贝贝是应用程序开发者,我需要应用别人开发好的库文件。但我的终极目的是让用户能够使用我的程序。那么涉及到的问题是:我需要从库开发者那里得到些什么放到我的程序中?我向用户提供什么让他们的机器能够直接运行我的程序?
先来解决第二个问题。有两种方式向用户提供程序:动态链接和静态链接。对这两个东西,贝贝的理解是:采用静态链接的方式时,我把第三方库文件在编译阶段,直接编译进我的.exe可执行文件,.exe里面就包含有所有可以解决问题的代码(不管是真的代码还是什么二进制代码);采用动态链接的方式时,在编译阶段,.exe可执行文件不包含所有可以解决问题的代码,有一部分存在于.dll文件中,.exe只是知道要去寻找它要用到的.dll文件。.dll也被称为动态链接库,是在程序运行时再去找去用的。所以,客户使用时,也必须拥有和我一样的.dll文件,光有应用程序的.exe文件是不行的。
总结一下,如果应用程序开发者(也就是我啦)决定采用动态链接的方式编译,那么我最终提供给用户的不仅有我自己的.exe文件,还有所有的我用到的第三方库的.dll文件;如果我决定采用静态链接的方式编译,那么我最终提供给用户的只需.exe文件即可。
再来解决第一个问题。第三方库是怎样应用到我的应用程序中的?这包括两个步骤。一、编译出可移植的库文件;二、应用到具体代码中。
第一个步骤。通常情况下,开源项目我们拿到的都是源代码,因为使用者的平台不同,所以大家都重新编译以适应环境;少数情况下,有编译好的包,那么只需下载对应的版本即可。我们编译的是源代码,最终希望得到的是可移植应用的.lib、.dll文件。拿到源代码后,一般一个解决方案下都有多个项目,有的项目对应的输出文件是.lib(即静态链接所需的原料),有的项目对应的输出文件是.dll(即动态链接所需的原料)。这个视源代码的项目属性而定。有的项目只提供.dll,有的项目同样的库会同时提供静态链接和动态链接的版本。编译完成后,静态链接会产生.lib文件,动态链接会产生.lib和.dll文件(此lib非彼lib,这个lib只含引导信息,告诉使用者真正需要的.dll是什么)。
第二个步骤。应用程序怎样应用第三方库?很多第三方库经过编译后,源代码的目录下有include和lib两个子文件夹,一般的,include文件夹存储的是.h头文件,而lib文件夹存储的是静态链接的.lib文件和动态链接的.lib文件(引导文件)。那我们的应用程序希望应用第三方库的话,应用程序就必须知道.h和.lib的位置,这个在VS的项目->属性->C/C++->附加包含目录和项目->属性->链接器->附加库目录中设置这两个文件夹的位置(可以不在工程目录底下哦~~~)。在真正写代码的时候,需要用到哪个库,就#include哪个.h头文件,并且#pragma comment哪个.lib文件。
经过这两个步骤,应用程序已经知道到哪里去找它所需要的资源了。此时,再编译我们的程序,没有问题了,而且用上第三方库了。这时再把应用程序连同第三方库相应的.dll文件放到同一路径下,打包发给用户,就OK了。
最后总结一下,如下表所示,首先必须说明的是,贝贝理解,静态链接或动态链接的方式是由第三方库开发者决定的,他愿意提供.lib文件,咱就能用静态链接;他只愿意提供.dll文件,咱就只能用动态链接。在静态链接的方式下,第三方库开发者写的是源代码,而二次开发者需要用到.h和.lib文件,发布给最终用户的是.exe文件;在动态链接的方式下,二次开发者需要用到.h、.lib和.dll文件,发布给最终用户的是.exe和第三方库的.dll文件。
第三方库开发者 | 二次开发者 | 最终用户 | |
静态链接 | .h .cpp | .h .lib | .exe |
动态链接 | .h .cpp | .h .lib .dll | .exe .dll |
小思考:为什么提供给最终用户的没有.h和.lib文件?
嘿嘿,虫虫说,编译器编译时把这些信息都编译到.exe文件中去啦~~~
再一个小思考:.h、.lib、.dll里面到底有些什么性质的信息呢?
等思考更深入一点再来解答这个问题吧。