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:\Program\C++Pro\TestStaticLib\TestStaticLib
②Configuration Properties->Linker->General->Additional Library Directories
D:\Program\C++Pro\TestStaticLib\Debug
③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:\Program\C++Pro\TestStaticLib\TestDLL
②Configuration Properties->Linker->General->Additional Library Directories
D:\Program\C++Pro\TestStaticLib\Debug
③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条。