理解dlllibcpphWord下载.docx
- 文档编号:16521785
- 上传时间:2022-11-24
- 格式:DOCX
- 页数:14
- 大小:32.84KB
理解dlllibcpphWord下载.docx
《理解dlllibcpphWord下载.docx》由会员分享,可在线阅读,更多相关《理解dlllibcpphWord下载.docx(14页珍藏版)》请在冰豆网上搜索。
但也可以在模块定义(DEF)文件中列出导出函数,不过这样做常常引起更多的麻烦。
在应用程序方面,要求像下面这样明确声明相应的输入函数:
__declspec(dllimport)intMyFuncition(intn);
仅有导入和导出声明并不能使应用程序内部的函数调用链接到相应的DLL文件上。
应用程序的项目必须为链接程序指定所需的输入库(LIB文件)(适用于隐式链接)。
而且应用程序事实上必须至少包含一个对DLL函数的调用。
二、与DLL模块建立链接
应用程序导入函数与DLL文件中的导出函数进行链接有两种方式:
隐式链接和显式链接。
所谓的隐式链接是指在应用程序中不需指明DLL文件的实际存储路径,程序员不需关心DLL文件的实际装载。
而显式链接与此相反。
采用隐式链接方式,程序员在建立一个DLL文件时,链接程序会自动生成一个与之对应的LIB导入文件。
该文件包含了每一个DLL导出函数的符号名和可选的标识号,但是并不含有实际的代码。
LIB文件作为DLL的替代文件被编译到应用程序项目中。
当程序员通过静态链接方式编译生成应用程序时,应用程序中的调用函数与LIB文件中导出符号相匹配,这些符号或标识号进入到生成的EXE文件中。
LIB文件中也包含了对应的DLL文件名(但不是完全的路径名),链接程序将其存储在EXE文件内部。
当应用程序运行过程中需要加载DLL文件时,Windows根据这些信息发现并加载DLL,然后通过符号名或标识号实现对DLL函数的动态链接。
显式链接方式对于集成化的开发语言(例如VB)比较适合。
有了显式链接,程序员就不必再使用导入文件,而是直接调用Win32的LoadLibary函数,并指定DLL的路径作为参数。
LoadLibary返回HINSTANCE参数,应用程序在调用GetProcAddress函数时使用这一参数。
GetProcAddress函数将符号名或标识号转换为DLL内部的地址。
假设有一个导出如下函数的DLL文件:
extern"
C"
__declspec(dllexport)doubleSquareRoot(doubled);
下面是应用程序对该导出函数的显式链接的例子:
typedefdouble(SQRTPROC)(double);
HINSTANCEhInstance;
SQRTPROC*pFunction;
VERIFY(hInstance=:
:
LoadLibrary("
c:
\\winnt\\system32\\mydll.dll"
));
VERIFY(pFunction=(SQRTPROC*):
GetProcAddress(hInstance,"
SquareRoot"
doubled=(*pFunction)(81.0);
//调用该DLL函数
在隐式链接方式中,所有被应用程序调用的DLL文件都会在应用程序EXE文件加载时被加载在到内存中;
但如果采用显式链接方式,程序员可以决定DLL文件何时加载或不加载。
显式链接在运行时决定加载哪个DLL文件。
例如,可以将一个带有字符串资源的DLL模块以英语加载,而另一个以西班牙语加载。
应用程序在用户选择了合适的语种后再加载与之对应的DLL文件。
三、使用符号名链接与标识号链接
在Win16环境中,符号名链接效率较低,所有那时标识号链接是主要的链接方式。
在Win32环境中,符号名链接的效率得到了改善。
Microsoft现在推荐使用符号名链接。
但在MFC库中的DLL版本仍然采用的是标识号链接。
一个典型的MFC程序可能会链接到数百个MFCDLL函数上。
采用标识号链接的应用程序的EXE文件体相对较小,因为它不必包含导入函数的长字符串符号名。
四、编写DllMain函数
DllMain函数是DLL模块的默认入口点。
当Windows加载DLL模块时调用这一函数。
系统首先调用全局对象的构造函数,然后调用全局函数DLLMain。
DLLMain函数不仅在将DLL链接加载到进程时被调用,在DLL模块与进程分离时(以及其它时候)也被调用。
下面是一个框架DLLMain函数的例子。
HINSTANCEg_hInstance;
intAPIENTRYDllMain(HINSTANCEhInstance,DWORDdwReason,LPVOIDlpReserved)
{
if(dwReason==DLL_PROCESS_ATTACH)
TRACE0("
EX22A.DLLInitializing!
\n"
);
//在这里进行初始化
}
elseif(dwReason=DLL_PROCESS_DETACH)
EX22A.DLLTerminating!
//在这里进行清除工作
return1;
//成功
如果程序员没有为DLL模块编写一个DLLMain函数,系统会从其它运行库中引入一个不做任何操作的缺省DLLMain函数版本。
在单个线程启动和终止时,DLLMain函数也被调用。
正如由dwReason参数所表明的那样。
五、模块句柄
进程中的每个DLL模块被全局唯一的32字节的HINSTANCE句柄标识。
进程自己还有一个HINSTANCE句柄。
所有这些模块句柄都只有在特定的进程内部有效,它们代表了DLL或EXE模块在进程虚拟空间中的起始地址。
在Win32中,HINSTANCE和HMODULE的值是相同的,这个两种类型可以替换使用。
进程模块句柄几乎总是等于0x400000,而DLL模块的加载地址的缺省句柄是0x10000000。
如果程序同时使用了几个DLL模块,每一个都会有不同的HINSTANCE值。
这是因为在创建DLL文件时指定了不同的基地址,或者是因为加载程序对DLL代码进行了重定位。
模块句柄对于加载资源特别重要。
Win32的FindResource函数中带有一个HINSTANCE参数。
EXE和DLL都有其自己的资源。
如果应用程序需要来自于DLL的资源,就将此参数指定为DLL的模块句柄。
如果需要EXE文件中包含的资源,就指定EXE的模块句柄。
但是在使用这些句柄之前存在一个问题,你怎样得到它们呢?
如果需要得到EXE模块句柄,调用带有Null参数的Win32函数GetModuleHandle;
如果需要DLL模块句柄,就调用以DLL文件名为参数的Win32函数GetModuleHandle。
六、应用程序怎样找到DLL文件
如果应用程序使用LoadLibrary显式链接,那么在这个函数的参数中可以指定DLL文件的完整路径。
如果不指定路径,或是进行隐式链接,Windows将遵循下面的搜索顺序来定位DLL:
1.包含EXE文件的目录,
2.进程的当前工作目录,
3.Windows系统目录,
4.Windows目录,
5.列在Path环境变量中的一系列目录。
这里有一个很容易发生错误的陷阱。
如果你使用VC++进行项目开发,并且为DLL模块专门创建了一个项目,然后将生成的DLL文件拷贝到系统目录下,从应用程序中调用DLL模块。
到目前为止,一切正常。
接下来对DLL模块做了一些修改后重新生成了新的DLL文件,但你忘记将新的DLL文件拷贝到系统目录下。
下一次当你运行应用程序时,它仍加载了老版本的DLL文件,这可要当心!
七、调试DLL程序
Microsoft的VC++是开发和测试DLL的有效工具,只需从DLL项目中运行调试程序即可。
当你第一次这样操作时,调试程序会向你询问EXE文件的路径。
此后每次在调试程序中运行DLL时,调试程序会自动加载该EXE文件。
然后该EXE文件用上面的搜索序列发现DLL文件,这意味着你必须设置Path环境变量让其包含DLL文件的磁盘路径,或者也可以将DLL文件拷贝到搜索序列中的目录路径下
dlllibexe的联系与区别
2011-07-3120:
12
什么是lib文件,lib和dll的关系如何
转自
(1)lib是编译时需要的,dll是运行时需要的。
如果要完成源代码的编译,有lib就够了。
如果也使动态连接的程序运行起来,有dll就够了。
在开发和调试阶段,当然最好都有。
(2)一般的动态库程序有lib文件和dll文件。
lib文件是必须在编译期就连接到应用程序中的,而dll文件是运行期才会被调用的。
如果有dll文件,那么对应的lib文件一般是一些索引信息,具体的实现在dll文件中。
如果只有lib文件,那么这个lib文件是静态编译出来的,索引和实现都在其中。
静态编译的lib文件有好处:
给用户安装时就不需要再挂动态库了。
但也有缺点,就是导致应用程序比较大,而且失去了动态库的灵活性,在版本升级时,同时要发布新的应用程序才行。
(3)在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件,引入库文件包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件,库中的函数和数据并不复制到可执行文件中,因此在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中所要调用的函数的内存地址,这样当一个或多个应用程序运行是再把程序代码和被调用的函数代码链接起来,从而节省了内存资源。
从上面的说明可以看出,DLL和.LIB文件必须随应用程序一起发行,否则应用程序将会产生错误。
一、开发和使用dll需注意三种文件
1、
dll头文件
它是指dll中说明输出的类或符号原型或数据结构的.h文件。
当其它应用程序调用dll时,需要将该文件包含入应用程序的源文件中。
2、
dll的引入库文件
它是dll在编译、链接成功后生成的文件。
主要作用是当其它应用程序调用dll时,需要将该文件引入应用程序。
否则,dll无法引入。
3、
dll文件(.dll)
它是应用程序调用dll运行时,真正的可执行文件。
dll应用在编译、链接成功后,.dll文件即存在。
开发成功后的应用程序在发布时,只需要有.exe文件和.dll文件,不必有.lib文件和dll头文件。
动态链接库(DLL)是作为共享函数库的可执行文件。
动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。
函数的可执行代码位于一个DLL中,该DLL包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。
DLL还有助于共享数据和资源。
多个应用程序可同时访问内存中单个DLL副本的内容。
动态链接与静态链接的不同之处在于:
动态链接允许可执行模块(.dll文件或.exe文件)仅包含在运行时定位DLL函数的可执行代码所需的信息。
在静态链接中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。
使用动态链接代替静态链接有若干优点。
DLL节省内存,减少交换操作,节省磁盘空间,更易于升级,提供售后支持,提供扩展MFC库类的机制,支持多语言程序,并使国际版本的创建轻松完成。
lib与dll文件最大区别在调用方面
dll可以静态陷入
lib与DLL
从这一章起,我讲述的内容将特定于windows平台。
其实这篇文章也可看作是我在windows下的开发经验总结,因为以后我决定转unix了。
前面有一章说编译与链接的,说得很简略,其实应该放到这一章一块儿来说的。
许多单讲C++的书其实都过于学院派,对于真实的工作环境,上百个源文件怎么结合起来,几乎没有提及。
我引导读者一步步看看lib与DLL是怎么回事。
一个最简单的C++程序,只需要一个源文件,这个源文件包含了如下语句
intmain(){return0;
自然,这个程序什么也不做。
当需程序需要做事情时,我们会把越来越多的语句添加到源文件中,例如,我们会开始在main函数中添加代码:
#include<
stdio.h>
intmain()
printf("
HelloWorld!
return0;
由于人的智力水平的限制,当一个函数中包含了太多的语句时,便不太容易被理解,这时候开始需要子函数:
voidShowHello()
ShowHello();
同样的道理,一个源文件中包含了太多的函数,同样不好理解,人们开始分多个源文件了
//main.cpp
voidShowHello();
//[1]
//hello.cpp
将这两个文件加入到一个VC工程中,它们会被分别编译,最后链接在一起。
在VC编译器的输出窗口,你可以看到如下信息
--------------------Configuration:
hello-Win32Debug--------------------
Compiling...
main.cpp
hello.cpp
Linking...
hello.exe-0error(s),0warning(s)
这展示了它们的编译链接过程。
接下来,大家就算不知道也该猜到,当一个工程中有太多的源文件时,它也不好理解,于是,人们想到了一种手段:
将一部分源文件预先编译成库文件,也即lib文件,当要使用其中的函数时,只需要链接lib文件就可以了,而不用再理会最初的源文件。
在VC中新建一个staticlibrary类型的工程,加入hello.cpp文件,然后编译,就生成了lib文件,假设文件名为hello.lib。
别的工程要使用这个lib有两种方式:
1在工程选项-〉link-〉Object/LibraryModule中加入hello.lib
2可以在源代码中加入一行指令
#pragmacomment(lib,"
hello.lib"
)
注意这个不是C++语言的一部分,而是编译器的预处理指令,用于通知编译器需要链接hello.lib
根据个人爱好任意使用一种方式既可。
这种lib文件的格式可以简单的介绍一下,它实际上是任意个obj文件的集合。
obj文件则是cpp文件编译生成的,在本例中,lib文件只包含了一个obj文件,如果有多个cpp文件则会编译生成多个obj文件,从而生成的lib文件中也包含了多个obj,注意,这里仅仅是集合而已,不涉及到link,所以,在编译这种静态库工程时,你根本不会遇到链接错误。
即使有错,错误也只会在使用这个lib的EXE或者DLL工程中暴露出来。
关于静态lib,就只有这么多内容了,真的很简单,现在我们介绍另外一种类型的lib,它不是obj文件的集合,即里面不含有实际的实现,它只是提供动态链接到DLL所需要的信息。
这种lib可以在编译一个DLL工程时由编译器生成。
涉及到DLL,问题开始复杂起来,我不指望在本文中能把DLL的原理说清楚,这不是本文的目标,我介绍操作层面的东西。
简单的说,一个DLL工程和一个EXE工程的差别有两点:
1EXE的入口函数是main或者WinMain,而DLL的入口函数是DllMain
2EXE的入口函数标志着一段处理流程的开始,函数退出后,流程处理就结束了,而DLL的入口函数对系统来说,只是路过,加载DLL的时候路过一次,卸载DLL的时候又路过一次[2],你可以在DLL入口函数中做流程处理,但这通常不是DLL的目的,DLL的目的是要导出函数供其它DLL或EXE使用。
你可以把DLL和EXE的关系理解成前面的main.cpp和hello.cpp的关系,有类似,实现手段不同罢了。
先看如何写一个DLL以及如何导出函数,读者应该先尝试用VC创建一个新的动态链接库工程,创建时选项不选空工程就可以了,这样你能得到一个示例,以便开始在这个例子基础上工作。
看看你创建的例子中的头文件有类似这样的语句:
#ifdefDLL_EXPORTS
#defineDLL_API__declspec(dllexport)
#else
#defineDLL_API__declspec(dllimport)
#endif
这就是函数的导出与使用导出函数的全部奥妙了。
你的DLL工程已经在工程设置中定义了一个宏DLL_EXPORTS,因此你的函数声明只要前面加DLL_API就表示把它导出,而DLL的使用者由于没有定义这个宏,所以它包含这个头文件时把你的函数看作导入的。
通过模仿这个例子,你就可以写一系列的标记为导出的函数了。
导出函数还有另一种方法,是使用DEF文件,DEF文件的作用,在现在来说只是起到限定导出函数名字的作用,这里,我们要引出第二种[4]使用DLL的方法:
称为显示加载,通过WindowsAPI的LoadLibrary和GetProcAddress这两个函数来实现[5],这里GetProcAddress的参数需要一个字符串形式的函数名称,如果DLL工程中没有使用DEF文件,那么很可能你要使用非常奇怪的函数名称(形如:
?
fnDll@@YAHXZ)才能正确调用,这是因为C++中的函数重载机制把函数名字重新编码了,如果使用DEF文件,你可以显式指定没编码前的函数名。
有了这些知识,你可以开始写一些简单的DLL的应用,但是我可以百分之百的肯定,你会遇到崩溃,而之前的非DLL的版本则没有问题。
假如你通过显式加载来使用DLL,有可能会是调用约定不一致而引起崩溃,所谓调用约定就是函数声明前面加上__stdcall__cdecl等等限定词,注意一些宏如WINAPI会定义成这些限定词之一,不理解他们没关系,但是记住一定要保持一致,即声明和定义时一致,这在用隐式加载时不成问题,但是显示加载由于没有利用头文件,就有可能产生不一致。
调用约定并不是我真正要说的,虽然它是一种可能。
我要说的是内存分配与释放的问题。
请看下面代码:
voidfoo(string&
str)
str="
hello"
;
stringstr;
foo(str);
%s\n"
str.c_str());
当函数foo和main在同一个工程中,或者foo在静态库中时,不会有问题,但是如果foo是一个DLL的导出函数时,请不要这么写,它有可能会导致崩溃[6]。
崩溃的原因在于“一个模块中分配的内存在另一个模块中释放”,DLL与EXE分属两个模块,例子中foo里面赋值操作导致了内存分配,而main中return语句之后,string对象析构引起内存释放。
我不想穷举全部的这类情况,只请大家在设计DLL接口时考虑清楚内存的分配释放问题,请遵循谁分配,谁释放的原则来进行。
如果不知道该怎么设计,请抄袭我们常见的DLL接口--微软的API的做法,如:
CreateDC
ReleaseDC
的成对调用,一个函数分配了内存,另外一个函数用来释放内存。
回到我们有可能崩溃的例子中来,怎么修改才能避免呢?
这可以做为一个练习让读者来做,这个练习用的时间也许会比较长,如果你做好了,那么你差不多就出师了。
一时想不到也不用急,我至少见过两个有五年以上经验的程序员依然犯这样的错误。
注[1]:
为了说明的需要,我这里使用直接声明的方式,实际工程中是应该使用头文件的。
注[2]:
还有线程创建与销毁也会路过DLL的入口,但是这对新手来说意义不大。
注[3]:
DEF文件格式很简单,关于DEF文件的例子,可以通过新建一个ATLCOM工程看到。
注[4]:
第一种方法和使用静态库差不多,包含头文件,链接库文件,然后就像是使用普通函数一样,称为隐式加载。
注[5]:
具体调用方法请参阅MSDN。
注[6]:
之所以说有可能是因为,如果两个工程的设置都是采用动态连接到运行库,那么分配释放其实都在运行库的DLL中进行,那么这种情况便不会发生崩溃
:
dll和.lib都是程序集合,便于代码重用。
都是二进制的文件。
.dll也叫动态链接库,与程序链接的方式为运行时链接(run-timelinked),为PE(portableexecutable)格式,也就是程完整的程序。
.exe、.dll、.fon、.mod、.drv、.ocx等等都是动态链接库。
如.exe为系统调用的函数集合。
.dll不存在同名引用,且有导出表,与导入表。
.lib也叫静态链接库,在编译时与程序链接(link-timelinked),将“嵌入”到程序中。
会有冗余(程序文件代码的冗余和运行时内存存储的冗余),当两个lib相链接时地址会重新建立同。
在使用.lib之前,要在程序源代码中引用lib对应的头文件.
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 理解 dlllibcpph