静态库是函数和数据被编译进一个二进制文件中(通常扩展名为.lib)。在使用静态库的情况下,在编译链接可执行文件时,链接器从库中复制这些函数和数据并把它们和应用程序的其它模块组合起来创建最终的可执行文件(.EXE文件)      动态链接库通常都不能直接运行,也不能接收消息。它们是一些独立的文件,其中包含能被可执行程序或其它DLL调用来完成某项工作的函数。只有在其它模块调用动态链接库中的函数时,它才发挥作用。在使用动态库的时候,往往提供两个文件:一个引入库和一个DLL。引入库包含被DLL导出的函数和变量的符号名,DLL包含实际的函数和数据。在编译链接可执行文件时,只需要链接引入库,DLL中的函数代码和数据并不复制到可执行文件中,在运行的时候,再去加载DLL,访问DLL中导出的函数。<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

    动态链接库加载的两种方式:隐式链接,显示加载。关于这两种加载方式的实例说明,请见我的另一篇文章《动态链接库(dll)的调用方法》。以下所有说明均采用隐式链接方式,开发环境为VC
  • 下面说说函数的导出:

创建源文件如下:

_declspec(dllexport) int    add(int a,int b)
{
return a+b;
}

_declspec(dllexport) int    subtract(int a,int b)
{
return a-b;
}
 
观察编译之后,我们看到了导出函数,只是名字被更改了:
Dump of file dll1.dll

File Type DLL

    Section contains the following exports for Dll1.dll

                     0 characteristics
        456BD6E2 time date stamp Tue Nov 28 142746 2006
                0.00 version
                     1 ordinal base
                     2 number of functions
                     2 number of names

        ordinal hint RVA            name

                    1        0 0000100A add@@YAHHH@Z
                    2        1 00001005 subtract@@YAHHH@Z

    Summary

                7000 .data
                1000 .idata
                3000 .rdata
                2000 .reloc
             2A000 .text
 
将刚刚生成动态链接库文件的同一目录下,找到dll1.lib输入库文件,拷贝到我们的工程目录下。此时,我们就可以在应用程序中使用dll1库了。
  #include iostream.h
extern int add(int,int);     声明这个函数在外部定义
extern int subtract(int,int);
_declspec(dllimport) int add(int,int);
_declspec(dllimport) int subtract(int,int);
void main(void)
{
cout3+5 = add(3,5)endl;
cout5-3 = subtract(5,3)endl;
}
  • 下面谈谈关于类的导出:
将整个类都导出:
#ifdef DLL1_API
#else
#define DLL1_API _declspec(dllimport)
#endif
DLL1_API int add(int,int);
DLL1_API int subtract(int,int);
class DLL1_API Point
{
public
void output( int x,int y);
};
 
如果不想将整个类都导出,只将类的部分共有成员函数导出,则可以采用下面的方法:
#ifdef DLL1_API
#else
#define DLL1_API _declspec(dllimport)
#endif
DLL1_API int add(int,int);
DLL1_API int subtract(int,int);
class DLL1_API Point
{
public
DLL1_API void output( int x,int y);
void test();
};
 
看一下库的导出情况
Dump of file dll1.dll

File Type DLL

    Section contains the following exports for Dll1.dll

                     0 characteristics
        456BE737 time date stamp Tue Nov 28 153727 2006
                0.00 version
                     1 ordinal base
                     3 number of functions
                     3 number of names

        ordinal hint RVA            name

                    1        0 00001019 add@@YAHHH@Z
                    2        1 0000101E output@Point@@QAEXHH@Z
                    3        2 00001014 subtract@@YAHHH@Z

    Summary

                7000 .data
                1000 .idata
                3000 .rdata
                2000 .reloc
             31000 .text
 
上面提到了VC++编译器将函数名字进行了改写,这对于其他编译器来说也许就不是什么好事,因为其他编译器很可能找不到导出的函数,因为名字被改动了。
  • 不改变导出函数名称,保证其他编译器可以使用链接库:
如果不希望改变导出函数的名称,可以加上 extern C 标识
dll1.cpp
#define DLL1_API extern C _declspec(dllexport)
#include dll1.h
int    add(int a,int b)
{
return a+b;
}
int    subtract(int a,int b)
{
return a-b;
}

dll1.h
#ifdef DLL1_API
#else
#define DLL1_API extern C _declspec(dllimport)
#endif
DLL1_API int add(int,int);
DLL1_API int subtract(int,int);
 
再看一下导出发现名字没有改变:
Dump of file dll1.dll

File Type DLL

    Section contains the following exports for Dll1.dll

                     0 characteristics
        456BE969 time date stamp Tue Nov 28 154649 2006
                0.00 version
                     1 ordinal base
                     2 number of functions
                     2 number of names

        ordinal hint RVA            name

                    1        0 0000100A add
                    2        1 00001005 subtract

    Summary

                7000 .data
                1000 .idata
                3000 .rdata
                2000 .reloc
                2A000 .text

 

但是用extern C只能表示全局函数,对于类,则不能导出其成员函数。而且,当库的调用约定改变时,函数名还是会被改变。

dll1.cpp
#define DLL1_API extern C _declspec(dllexport)
#include dll1.h
int _stdcall    add(int a,int b)
{
return a+b;
}
int _stdcall    subtract(int a,int b)
{
return a-b;
}

dll1.h
#ifdef DLL1_API
#else
#define DLL1_API extern C _declspec(dllimport)
#endif
DLL1_API int _stdcall add(int,int);
DLL1_API int _stdcall subtract(int,int);
 
采用_stdcall调用约定后的导出情况:
Dump of file dll1.dll

File Type DLL

    Section contains the following exports for Dll1.dll

                     0 characteristics
        456BEBDB time date stamp Tue Nov 28 155715 2006
                0.00 version
                     1 ordinal base
                     2 number of functions
                     2 number of names

        ordinal hint RVA            name

                    1        0 00001005 _add@8
                    2        1 0000100A _subtract@8

    Summary

                7000 .data
                1000 .idata
                3000 .rdata
                2000 .reloc
                2A000 .text
 
采用下述方法可以解决上面的问题。 源程序不变,我们创建一个模块定义文件,命名为dll1.def,然后添加到我们的工程中。其内容如下:
LIBRARY Dll1

EXPORTS
add
subtract

 

注意LIBRARY Dll1这一名字必须和我们的工程名字匹配,EXPORTS表明动态链接库中要导出的函数名字。这时,我们看一下导出情况:

Dump of file dll2.dll

File Type DLL

    Section contains the following exports for Dll2.dll

                     0 characteristics
        456BEECE time date stamp Tue Nov 28 160950 2006
                0.00 version
                     1 ordinal base
                     2 number of functions
                     2 number of names

        ordinal hint RVA            name

                    1        0 0000100A add
                    2        1 00001005 subtract

    Summary

                7000 .data
                1000 .idata
                3000 .rdata
                2000 .reloc
                2A000 .text
这时发现函数名没有改变了。