当前位置: 首页 > 科技观察

彻底搞懂动态库、静态库、运行时库、引入库之间的区别

时间:2023-03-15 22:13:30 科技观察

彻底了解动态库、静态库、运行时库和导入库的区别)、exit()等,用户可以创建自己的运行时(Windows中的DLL),具体细节取决于编译器和操作系统。静态库:函数和数据被编译成一个二进制文件(通常以.lib为扩展名),静态库实际上是在链接时链接到EXE,库本身不需要与可执行文件一起分发。动态库:用VC++创建的动态库包含两个文件,一个lib文件和一个dll文件。这个lib文件是导入库,不是静态库。导入库有时也称为输入库或导入库。注意:Windows操作系统下动态库和运行时库的扩展名都是.dll,COM组件的扩展名也是.dll,动态库的导入库和静态库的扩展名都是.lib。windows下调用动态库的方法:(1)隐式加载:即在程序中包含lib文件和.h文件,隐式链接在加载时有时也称为静态加载或动态链接。例如:#include"somedll.h"#pragmacomment(lib,"somedll.lib")那么就可以直接调用这个dll中的函数了,注意运行时还是需要somedll.dll的。(2)显示加载:使用loadlibrary、GetProcAddress、FreeLibrary,不需要.h文件和.lib文件,但需要知道函数的原型。显式链接有时称为动态加载或运行时动态链接。(3)区别:如果进程启动时没有找到DLL,操作系统会使用隐式链接终止进程。同样在这种情况下,使用显式链接的进程不会终止,并且可以尝试从错误中恢复。决定使用哪种链接方法:有两种类型的链接:隐式链接和显式链接。1.隐式链接隐式链接发生在应用程序代码调用导出的DLL函数时。当编译或汇编调用可执行文件的源代码时,DLL函数调用会在目标代码中生成外部函数引用。要解析此外部引用,应用程序必须链接到DLL创建者提供的导入库(.LIB文件)。导入库只包含加载DLL的代码和实现DLL函数调用的代码。当在导入库中找到外部函数时,会通知链接器该函数的代码在DLL中。要解析对DLL的外部引用,链接器只需向可执行文件添加信息,告诉系统在进程启动时在哪里查找DLL代码。当系统启动一个包含动态链接引用的程序时,它会使用该程序的可执行文件中的信息来定位所需的DLL。如果系统找不到DLL,它会终止进程并显示一个对话框来报告错误。否则,系统将DLL模块映射到进程的地址空间。如果任何DLL具有入口点函数(用于初始化代码和终止代码),操作系统将调用此函数。在传递给入口点函数的参数中,指定了一个代码来指示DLL正在附加到进程。如果入口点函数不返回TRUE,系统终止进程并报错。最后,系统修改进程的可执行代码,提供DLL函数的起始地址。与程序代码的其余部分一样,DLL代码在进程启动时映射到进程的地址空间,并且仅在需要时加载到内存中。因此,.def文件用来控制早期版本Windows加载的PRELOAD和LOADONCALL代码属性不再具有任何意义。2.显式链接大多数应用程序使用隐式链接,因为这是最容易使用的链接方法。但有时也需要显式链接。以下是使用显式链接的一些常见原因:应用程序直到运行时才知道它需要加载的DLL的名称。例如,应用程序可能需要从配置文件中获取DLL的名称和导出函数的名称。如果进程启动时没有找到DLL,操作系统将使用隐式链接终止进程。同样在这种情况下,使用显式链接的进程不会终止,并且可以尝试从错误中恢复。例如,进程可以通知用户错误并让用户指定DLL的不同路径。如果它链接到的任何DLL具有失败的DllMain函数,则使用隐式链接的进程也会终止。同样在这种情况下,使用显式链接的进程不会终止。由于Windows在加载应用程序时加载所有DLL,因此隐式链接到许多DLL的应用程序启动速度会较慢。为了提高启动性能,应用程序可以在加载后立即隐式链接到那些需要的DLL,并等待它们需要显式链接到其他DLL。通过显式链接,无需将应用程序与导入库链接。如果DLL中的更改导致导出序号发生更改,则使用显式链接的应用程序不需要重新链接(假设它们使用函数名而不是序号值调用GetProcAddress),而使用隐式链接的应用程序必须重新链接到新的导入库.以下是需要注意的显式链接的两个缺点:如果DLL具有DllMain入口点函数,则操作系统会在调用LoadLibrary的线程的上下文中调用此函数。如果由于先前调用LoadLibrary而没有相应调用FreeLibrary函数而导致DLL已附加到进程,则不会调用此入口点函数。如果DLL使用DllMain函数为进程的每个线程执行初始化,则显式链接会导致问题,因为调用LoadLibrary(或AfxLoadLibrary)时存在的线程将不会被初始化。如果DLL将静态作用域数据声明为__declspec(thread),则DLL在显式链接时会导致保护错误。使用LoadLibrary加载DLL后,每当代码引用此数据时,DLL将导致保护错误。(静态范围数据包括全局和局部静态项。)因此,在创建DLL时应避免使用线程局部存储,或者应告知DLL的用户(当他们尝试动态加载时)潜在的陷阱。3.隐式链接:要隐式链接到DLL,可执行文件必须从DLL的提供程序获得以下内容:包含导出函数和/或C++类声明的头文件(.h文件)。类、函数和数据都应具有__declspec(dllimport),有关详细信息,请参阅dllexport、dllimport。导入要链接的库(.LIB文件)。(链接器在构建DLL时创建导入库。)实际的DLL(.dll文件)。使用DLL的可执行文件必须包含一个头文件,该文件包含每个包含对导出函数的调用的源文件中的导出函数(或C++类)。从编码的角度来看,对导出函数的函数调用就像任何其他函数调用一样。要生成调用可执行文件,它必须与导入库链接。如果您使用的是外部联编文件,请指定导入库的文件名,其中列出了要链接到的其他对象(.obj)文件或库。操作系统在加载调用可执行文件时必须能够找到DLL文件。4.显式链接:在显式链接下,应用程序必须在运行时进行函数调用以显式加载DLL。要显式链接到DLL,应用程序必须:调用LoadLibrary(或类似函数)以加载DLL并获取模块句柄。调用GetProcAddress以获得指向应用程序要调用的每个导出函数的函数指针。由于应用程序通过指针调用DLL的函数,编译器不会产生外部引用,所以不需要与导入库链接。使用DLL后调用FreeLibrary。