有lib格式的软件吗,dll文件需要编译吗

2023-05-06 21:30:41 阅读:164412 作者: 2345

什么是lib文件? lib和dll的关系怎么样? 2008-04-1819:44:37。

)1)编译时需要lib,运行时需要dll。

有lib就足够完成源代码的编译了。

如果要让动态连接的程序也运行,有dll就足够了。

在开发和调试阶段,当然最好一切都有。

)2)典型的动态库程序包括lib和dll文件。 lib文件必须在编译时连接到APP应用程序,但dll文件将在运行时调用。 如果存在dll文件,则对应的lib文件一般是一些索引信息,并且特定实现在dll文件中。 如果只有lib文件,则此lib文件是静态编译的,包含索引和实现。 静态编译的lib文件有一个优点。 安装用户时不需要挂动态库。 但是,还有一个缺点,就是APP比较大,动态库失去了灵活性,升级时必须同时发布新的APP应用程序。

)3)对于动态库,有两个文件。 一个是库).LIB )文件的输入,另一个是DLL文件。 导入库文件包含由DLL导出的函数的名称和位置,而DLL包含实际函数和数据。 由于APP应用程序使用LIB文件链接到所需的DLL文件,并且不会将库中的函数和数据复制到可执行文件中,因此APP应用程序可执行文件必须具有以DLL调用的函数的存储器地址,而不是调用的函数代码这样,当执行一个或多个APP应用程序时,程序代码和调用的函数代码就会链接在一起,从而节省内存资源。 从上面的说明中可以看出,DLL和. LIB文件必须与APP应用程序一起发布。 如果不发出,APP应用程序将出现错误。

一.开发和使用dll需要注意三类文件

1、dll头文件

这是dll中描述输出的类或元件的原型或数据结构的. h文件。 当其他APP应用程序调用dll时,必须将该文件包含在APP应用程序的源文件中。

2、dll部署库文件

这是dll编译、链接成功后生成的文件。 主要作用是当其他APP应用程序调用dll时,必须将该文件引入到APP应用程序中。 否则,无法部署dll。

3、dll文件(.dll )。

这是APP应用程序调用dll运行时的真正可执行文件。 dll APP应用程序将被编译并链接,然后包含. dll文件。 发行时,成功开发的APP应用程序需要. exe和. dll文件,而不需要. lib和dll头文件。

动态链接库(DLL )是作为共享函数库的可执行文件。 通过动态链接,进程可以调用不属于可执行代码的函数。 函数的可执行代码位于包含一个或多个函数的DLL中,这些函数被编译、链接并与使用它们的进程分开存储。 DLL还有助于共享数据和资源。 多个APP应用程序可以同时访问内存中单个DLL副本的内容。

动态链接和静态链接的区别在于,使用动态链接时,可执行文件. dll或. exe只包含在运行时查找dll函数的可执行代码所需的信息。 在静态链接中,链接器获取从静态链接库引用的所有函数,并将库与代码一起放置在可执行文件中。

用动态链接代替静态链接有几个优点。 DLL提供了一种节省内存、减少交换操作、节省磁盘空间、便于升级、提供售后服务、扩展MFC库类的机制,支持多语言程序,并便于创建国际版本

lib和dll文件最大的区别是调用

dll可以陷入静态

lib和DLL

从本章开始,我要说的内容是特定于windows平台的。 其实这篇文章也可以看作是我在windows上开发经验的总结。 因为我现在决定搬到unix。

上一章谈到编译和链接,简单地说了,其实应该总结在这一章里说。 很多只有C的书其实只是学派,对于实际的工作环境,几百个源文件是如何合并的,几乎没有提到。 我让读者一步一步地看看lib和DLL怎么样了。

是最简单的c程序,只需要一个源文件。 此源文件包含以下语句

intmain () {return0; }

当然,这个程序什么也不做。

当程序需要做某件事时,向源文件中添加语句。 例如,开始向main函数添加代码。

#includestdio.h

intmain () )

{

打印(hello world! n ';

返回0;

}

由于人类智力水平的限制,如果一个函数含有很多语句,就不太容易被理解了。 此时,需要子函数。

#includestdio.h

voidShowHello ()

{

打印(hello world! n ';

}

int main () )

{

ShowHello (;

返回0;

}

同样,一个源文件中包含很多函数,很难理解,人们开始分成多个源文件

//main.cpp

void ShowHello (; //[1]

int main () )

{

ShowHello (;

返回0;

}

//hello.cpp

#include stdio.h

void ShowHello ()

{

打印

f("Hello World!n");
}
将这两个文件加入到一个VC工程中,它们会被分别编译,最后链接在一起。在VC编译器的输出窗口,你可以看到如下信息
--------------------Configuration: hello - Win32 Debug--------------------
Compiling...
main.cpp
hello.cpp
Linking... 
hello.exe - 0 error(s), 0 warning(s)
这展示了它们的编译链接过程。
接下来,大家就算不知道也该猜到,当一个工程中有太多的源文件时,它也不好理解,于是,人们想到了一种手段:将一部分源文件预先编译成库文件,也即lib文件,当要使用其中的函数时,只需要链接lib文件就可以了,而不用再理会最初的源文件。
在VC中新建一个static library类型的工程,加入hello.cpp文件,然后编译,就生成了lib文件,假设文件名为hello.lib。
别的工程要使用这个lib有两种方式:
1 在工程选项-〉link-〉Object/Library Module中加入hello.lib
2 可以在源代码中加入一行指令
#pragma comment(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工程的差别有两点:
1 EXE的入口函数是main或者WinMain,而DLL的入口函数是DllMain
2 EXE的入口函数标志着一段处理流程的开始,函数退出后,流程处理就结束了,而DLL的入口函数对系统来说,只是路过,加载DLL的时候路过一次,卸载DLL的时候又路过一次[2],你可以在DLL入口函数中做流程处理,但这通常不是DLL的目的,DLL的目的是要导出函数供其它DLL或EXE使用。你可以把DLL和EXE的关系理解成前面的main.cpp和hello.cpp的关系,有类似,实现手段不同罢了。
先看如何写一个DLL以及如何导出函数,读者应该先尝试用VC创建一个新的动态链接库工程,创建时选项不选空工程就可以了,这样你能得到一个示例,以便开始在这个例子基础上工作。
看看你创建的例子中的头文件有类似这样的语句:
#ifdef DLL_EXPORTS
#define DLL_API __declspec(dllexport)
#else
#define DLL_API __declspec(dllimport)
#endif
这就是函数的导出与使用导出函数的全部奥妙了。你的DLL工程已经在工程设置中定义了一个宏DLL_EXPORTS,因此你的函数声明只要前面加DLL_API就表示把它导出,而DLL的使用者由于没有定义这个宏,所以它包含这个头文件时把你的函数看作导入的。通过模仿这个例子,你就可以写一系列的标记为导出的函数了。
导出函数还有另一种方法,是使用DEF文件,DEF文件的作用,在现在来说只是起到限定导出函数名字的作用,这里,我们要引出第二种[4]使用DLL的方法:称为显示加载,通过Windows API的LoadLibrary和GetProcAddress这两个函数来实现[5],这里GetProcAddress的参数需要一个字符串形式的函数名称,如果DLL工程中没有使用DEF文件,那么很可能你要使用非常奇怪的函数名称(形如:?fnDll@@YAHXZ)才能正确调用,这是因为C++中的函数重载机制把函数名字重新编码了,如果使用DEF文件,你可以显式指定没编码前的函数名。 
 

有了这些知识,你可以开始写一些简单的DLL的应用,但是我可以百分之百的肯定,你会遇到崩溃,而之前的非DLL的版本则没有问题。假如你通过显式加载来使用DLL,有可能会是调用约定不一致而引起崩溃,所谓调用约定就是函数声明前面加上__stdcall __cdecl等等限定词,注意一些宏如WINAPI会定义成这些限定词之一,不理解他们没关系,但是记住一定要保持一致,即声明和定义时一致,这在用隐式加载时不成问题,但是显示加载由于没有利用头文件,就有可能产生不一致。
调用约定并不是我真正要说的,虽然它是一种可能。我要说的是内存分配与释放的问题。请看下面代码:
void foo(string& str)
{
str = "hello";
}
int main()
{
string str;
foo(str);
printf("%sn", str.c_str());
return 0;
}
当函数foo和main在同一个工程中,或者foo在静态库中时,不会有问题,但是如果foo是一个DLL的导出函数时,请不要这么写,它有可能会导致崩溃[6]。崩溃的原因在于“一个模块中分配的内存在另一个模块中释放”,DLL与EXE分属两个模块,例子中foo里面赋值操作导致了内存分配,而main中return语句之后,string对象析构引起内存释放。
我不想穷举全部的这类情况,只请大家在设计DLL接口时考虑清楚内存的分配释放问题,请遵循谁分配,谁释放的原则来进行。
如果不知道该怎么设计,请抄袭我们常见的DLL接口--微软的API的做法,如:
CreateDC
ReleaseDC
的成对调用,一个函数分配了内存,另外一个函数用来释放内存。
回到我们有可能崩溃的例子中来,怎么修改才能避免呢?
这可以做为一个练习让读者来做,这个练习用的时间也许会比较长,如果你做好了,那么你差不多就出师了。一时想不到也不用急,我至少见过两个有五年以上经验的程序员依然犯这样的错误。

注[1]:为了说明的需要,我这里使用直接声明的方式,实际工程中是应该使用头文件的。
注[2]: 还有线程创建与销毁也会路过DLL的入口,但是这对新手来说意义不大。
注[3]:DEF文件格式很简单,关于DEF文件的例子,可以通过新建一个ATL COM工程看到。
注[4]:第一种方法和使用静态库差不多,包含头文件,链接库文件,然后就像是使用普通函数一样,称为隐式加载。
注[5]:具体调用方法请参阅MSDN。
注[6]:之所以说有可能是因为,如果两个工程的设置都是采用动态连接到运行库,那么分配释放其实都在运行库的DLL中进行,那么这种情况便不会发生崩溃 

1.本文部分素材来源网络,版权归原作者所有,如涉及作品版权问题,请与我联系删除。

2.未经原作者允许不得转载本文内容,否则将视为侵权;

3.转载或者引用本文内容请注明来源及原作者;

4.对于不遵守此声明或者其他违法使用本文内容者,本人依法保留追究权等。

下面是我的个人微信公众号,关注【一个早起的程序员】精彩系列文章每天不断。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。

标签: 格式   文件

Copyright © 2022 恩蓝号 Inc. 保留所有权利。 Powered by 恩蓝号

页面耗时0.0205秒, 内存占用107.28 KB, 访问数据库2次