Window中创建和使用静态库与动态库

一、静态库的创建和使用

1. 静态库的创建

(1) 在VS2013中选择菜单->File->New->Project,选择Visual C++ ->Win32选项,然后点击Win32 Project图标,选择Win32 Application Wizard,选择Application type下的Static library选项。工程名字为TestStaticLib。

(2)在Solution Explorer的Header Files下新建一个test_header.h头文件

 1 #ifndef TEST_HEADER_H_
 2 #define TEST_HEADER_H_
 3 
 4 #ifdef _WIN32
 5 #ifdef _LIB
 6 #define DECLSPEC __declspec(dllexport)
 7 #else
 8 #define DECLSPEC __declspec(dllimport)
 9 #endif
10 #else
11 #define DECLSPEC
12 #endif
13 
14 #endif

__declspec修饰符仅在Windows平台下有效。
__declspec(dllexport)的作用是将当前函数或者类导出,以便可以从DLL中调用。

__declspec(dllimport)的作用是在应用程序中可以使用导出的DLL函数或者类。

在VS2013编译器中新建一个Static library工程的时候,预定义了_LIB宏,所以上面的代码中

#define DECLSPEC __declspec(dllexport)是有效的。

(3) 在Solution Explorer的Header Files下新建一个Source.h文件

 1 #ifndef SOURCE_H_
 2 #define SOURCE_H_
 3 #include "test_header.h"
 4 
 5 DECLSPEC int MyFunction(int a,int b);
 6 
 7 class DECLSPEC MyClass
 8 {
 9 public:
10     MyClass(int a, int b) : m_a(a), m_b(b){}
11     ~MyClass();
12     int add();
13 private:
14     int m_a;
15     int m_b;
16 };
17 #endif

(4) 在Solution Explorer的Source Files文件夹下新建一个Source.cpp文件

 1 #include "Source.h"
 2 
 3 int  MyFunction(int a,int b)
 4 {
 5     return a + b;
 6 }
 7 
 8 MyClass::~MyClass()
 9 {
10 }
11 
12 int MyClass::add()
13 {
14     return m_a + m_b;
15 }

(5) 编译工程,在工程目录的Debug文件夹下会生成TetsStaticLib.lib静态库文件。
2. 静态库的使用

新建一个Win32 Console工程,名字为TestLib。

设置TestLib的工程属性

①Configuration Properties->C/C++->General->Additional Include Directories

D:ProgramC++ProTestStaticLibTestStaticLib

②Configuration Properties->Linker->General->Additional Library Directories

D:ProgramC++ProTestStaticLibDebug

③Configuration Properties->Linker->Input->Additional Dependencies

TestStaticLib.lib

添加如下测试代码

 1 #include "stdafx.h"
 2 #include "Source.h"
 3 #include <iostream>
 4 using namespace std;
 5 
 6 
 7 int _tmain(int argc, _TCHAR* argv[])
 8 {
 9     int c = MyFunction(1, 2);
10     cout << c << endl;
11 
12     MyClass cla(2, 4);
13     cout << cla.add() << endl;
14     system("pause");
15     return 0;
16 }

 编译运行生成的TestLib.exe,可以看到程序结果,这时将TestLib.exe拷贝到任意文件夹也可以运行。

 二、动态库的创建和使用

1. 动态库的创建

(1) 在VS2013中选择菜单->File->New->Project,选择Visual C++ ->Win32选项,然后点击Win32 Project图标,选择Win32 Application Wizard,选择Application type下的DLL选项。工程名字为TestDLL。

(2)在Solution Explorer的Header Files下新建一个test_header.h头文件

 1 #ifndef TEST_HEADER_H_
 2 #define TEST_HEADER_H_
 3 
 4 #ifdef _WIN32
 5 #ifdef TESTDLL_EXPORTS
 6 #define DECLSPEC __declspec(dllexport)
 7 #else
 8 #define DECLSPEC __declspec(dllimport)
 9 #endif
10 #else
11 #define DECLSPEC
12 #endif
13 
14 #endif

(3)在Solution Explorer的Header Files下新建一个Source.h文件

 1 #ifndef SOURCE_H_
 2 #define SOURCE_H_
 3 #include "test_header.h"
 4 
 5 DECLSPEC int MyFunction(int a,int b);
 6 
 7 class DECLSPEC MyClass
 8 {
 9 public:
10     MyClass(int a, int b) : m_a(a), m_b(b){}
11     ~MyClass();
12     int add();
13 private:
14     int m_a;
15     int m_b;
16 };
17 #endif

(4) 在Solution Explorer的Source Files文件夹下新建一个Source.cpp文件

 1 #include "stdafx.h"
 2 #include "Source.h"
 3 
 4 int  MyFunction(int a,int b)
 5 {
 6     return a + b;
 7 }
 8 
 9 MyClass::~MyClass()
10 {
11 }
12 
13 int MyClass::add()
14 {
15     return m_a + m_b;
16 }

(5) 编译工程,在工程目录的Debug文件夹下会生成TestDLL.dll动态链接文件以及它的导出符号文件TestDLL.lib。
2. 动态库的使用

(1) 隐式使用

新建一个Win32 Console工程,名字为TestLib。

设置TestLib的工程属性

①Configuration Properties->C/C++->General->Additional Include Directories

D:ProgramC++ProTestStaticLibTestDLL

②Configuration Properties->Linker->General->Additional Library Directories

D:ProgramC++ProTestStaticLibDebug

③Configuration Properties->Linker->Input->Additional Dependencies

TestDLL.lib

添加如下测试代码

 1 #include "stdafx.h"
 2 #include "Source.h"
 3 #include <iostream>
 4 using namespace std;
 5 
 6 
 7 int _tmain(int argc, _TCHAR* argv[])
 8 {
 9     int c = MyFunction(1, 2);
10     cout << c << endl;
11 
12     MyClass cla(2, 4);
13     cout << cla.add() << endl;
14     system("pause");
15     return 0;
16 }

 编译运行生成的TestLib.exe,可以看到程序结果,注意TestLib.exe必须与TestDLL.dll同一目录才可以运行。

(2) 显示使用(该方法不能使用导出的类)

修改Source.h

1 #ifndef SOURCE_H_
2 #define SOURCE_H_
3 #include "test_header.h"
4 
5 extern "C" DECLSPEC int MyFunction(int a, int b);
6 
7 #endif

重新编译TestDLL工程
添加如下测试代码:

 1 #include "stdafx.h"
 2 #include <Windows.h>
 3 #include <iostream>
 4 using namespace std;
 5 
 6 
 7 int _tmain(int argc, _TCHAR* argv[])
 8 {
 9     typedef int (*MyFunctionSame)(int a, int b);
10 
11     HINSTANCE handle = LoadLibrary(L"TestDLL.dll");
12     if (!handle)
13     {
14         cout << "Cannot load plugin!" << endl;
15         exit(1);
16     }
17     MyFunctionSame fptr = (MyFunctionSame)GetProcAddress(handle, "MyFunction");
18     if (fptr == (MyFunctionSame)NULL)
19     {
20         cout << "Cannnot find function in plugin: " << endl;
21         FreeLibrary(handle);
22         exit(1);
23     }
24     
25     cout << fptr(1, 2) << endl;
26     FreeLibrary(handle);
27 
28     system("pause");
29     return 0;
30 }

显示加载的好处是不许要.lib导入库文件以及.h头文件。但是缺点是只能导出函数。另外,在导出函数的前面必须加extern "C"。
extern "C"的作用是强制使用C语言的函数命名规则,可以使用下面的命令查看当前DLL下的导出函数表。

 DUMPBIN /EXPORTS TestDLL.dll

 3.关于.def文件

除了使用__declspec声明修改源文件外,还可以使用模块定义文件(.def文件)

修改TestDLL工程中的Source.h文件如下:

1 #ifndef SOURCE_H_
2 #define SOURCE_H_
3 #include "test_header.h"
4 
5 int __stdcall MyFunction(int a, int b);
6 
7 #endif

新建一个Source.def文件

1 LIBRARY "TestDLL"
2 EXPORTS
3     MyFunction @1

注意上面的@1就是DLL文件中的导出序号,可以通过DUMPBIN命令查看DLL文件。
设置TestDLL的工程属性

Configuration Properties->Linker->Input->Module Definition File

Source.def

重新编译TestDLL工程,这样生成的TestDLL.dll文件自带导出符号信息。

新建一个Win32 Console工程,测试TestDLL.dll文件

 1 #include <Windows.h>
 2 #include <iostream>
 3 using namespace std;
 4 
 5 
 6 int _tmain(int argc, _TCHAR* argv[])
 7 {
 8     typedef int(__stdcall *MyFunctionSame)(int a, int b);
 9 
10     HINSTANCE handle = LoadLibrary(L"TestDLL.dll");
11     if (!handle)
12     {
13         cout << "Cannot load plugin!" << endl;
14         exit(1);
15     }
16     MyFunctionSame fptr = (MyFunctionSame)GetProcAddress(handle, "MyFunction");
17     if (fptr == (MyFunctionSame)NULL)
18     {
19         cout << "Cannnot find function in plugin: " << endl;
20         FreeLibrary(handle);
21         exit(1);
22     }
23     
24     cout << fptr(1, 2) << endl;
25     FreeLibrary(handle);
26 
27     system("pause");
28     return 0;
29 }

 4.关于调用约定_stdcall与_cdecl

_stdcall:参数按从右向左的顺序入栈,由被调用函数清理堆栈.
_cdecl :参数按从右向左的顺序入栈,由调用函数清理堆栈.

一般来说,C++使用的是__cdecl调用约定,C#,JAVA等编程语言则是__stdcall,默认情况下使用的是__cdecl方式,

所以说,如果用c++写的DLL文件想被其它语言调用,必须使用__stdcall方式。

如果使用__stdcall方式,那么

即使在函数前加extern "C",导出的函数名也会有变化,例如:

extern "C" DECLSPEC int  _stdcall  MyFunction(int a, int b);

通过DUMPBIN工具查看函数名为"MyFunction@8",@8的意思是该函数占用的字节数。

如果我们使用静态库或者动态库的隐式调用方式,一点影响都没有。

如果使用动态库的显示调用方式,则无法找到MyFunction函数。

所以在这种情况下,一定要使用.def的方式去修改导出函数名。参见上面第3条。

原文地址:https://www.cnblogs.com/elitiwin/p/4298994.html