在Visual Studio中使用C++创建和使用DLL
DLL是一个包含可由多个程序同时使用的代码和数据的库。例如:在Windows操作系统中,Comdlg32 DLL执行与对话框有关的常见函数。因此,每个程序都可以使用该DLL中包含的功能来实现“打开”对话框。这有助于促进代码重用和内存的有效使用。这篇文章的目的就是让你一次性就能了解和掌握DLL。
view plain copy
- #ifndef _MYCODE_H_
- #define _MYCODE_H_
- #ifdef DLLDEMO1_EXPORTS
- #define EXPORTS_DEMO _declspec( dllexport )
- #else
- #define EXPORTS_DEMO _declspec(dllimport)
- #endif
- extern "C" EXPORTS_DEMO int Add (int a , int b);
- #endif
在MyCode.cpp中输入以下代码:
[cpp] view plain copy
- #include "stdafx.h"
- #include "MyCode.h"
- int Add ( int a , int b )
- {
- return ( a + b );
- }
编译工程,就会生成DLLDemo1.dll文件。在代码中,很多细节的地方,我稍后进行详细的讲解。
view plain copy
- #include
- #include
- //#include "..\\DLLDemo1\\MyCode.h"
- using namespace std;
- #pragma comment(lib, "..\\debug\\DLLDemo1.lib")
- extern "C" _declspec(dllimport) int Add(int a, int b);
- int main(int argc, char *argv[])
- {
- cout<
- return 0;
- }
运行时动态链接:
[cpp] view plain copy
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
- int main(int argc, char *argv[])
- {
- HMODULE hDll = LoadLibrary("DLLDemo1.dll");
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- }
- #include
- #include
- //#include "..\\DLLDemo1\\MyCode.h"
- using namespace std;
- #pragma comment(lib, "..\\debug\\DLLDemo1.lib")
- extern "C" _declspec(dllimport) int Add(int a, int b);
- int main(int argc, char *argv[])
- {
- cout<
- return 0;
- }
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
- int main(int argc, char *argv[])
- {
- HMODULE hDll = LoadLibrary("DLLDemo1.dll");
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- }
上述代码都在DLLDemo1工程中。(工程下载)。
工程下载)都有体现。
view plain copy
- LIBRARY "DLLDemo2"
- EXPORTS
- Add @ 1 ;Export the Add function
- LIBRARY "DLLDemo2"
- EXPORTS
- Add @ 1 ;Export the Add function
Module-Definition File(.def)文件的格式如下:
- LIBRARY语句说明.def文件对应的DLL;
- EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号有一定的作用)。
使用def文件,生成了DLL,客户端调用代码如下:
[cpp] view plain copy- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
- int main(int argc, char *argv[])
- {
- HMODULE hDll = LoadLibrary("DLLDemo2.dll");
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, MAKEINTRESOURCE(1));
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- }
view plain copy
- /*
- ** FileName : Extern Demo
- ** Author : Jelly Young
- ** Date : 2013/11/18
- ** Description : More information, please go to http://www.jellythink.com
- */
- #include
- using namespace std;
- int main(int argc, char *argv[])
- {
- extern int a;
- cout<
- }
- int a = 100;
- 在多文件的程序中,如果多个文件都要使用同一个外部变量,不能在各个文件中各定义一个外部变量,否则会出现“重复定义”的错误。正确的做法是在任意一个文件中定义外部变量,其它文件用extern对变量做“外部变量声明”。在编译和链接时,系统会知道该变量是一个已经在别处定义的外部变量,并把另一文件中外部变量的作用域扩展到本文件,这样在本文件就可以合法地使用该外部变量了。写过MFC程序的人都知道,在在CXXXApp类的头文件中,就使用extern声明了一个该类的变量,而该变量的实际定义是在CXXXApp类的实现文件中完成的;
- 外部函数,在定义函数时,如果在最左端加关键字extern,表示此函数是外部函数。C语言规定,如果在定义时省略extern,则隐含为外部函数。而内部函数必须在前面加static关键字。在需要调用此函数的文件中,用extern对函数作声明,表明该函数是在其它文件中定义的外部函数。
接着说”C”的含义。我们都知道C++通过函数参数的不同类型支持重载机制,编译器根据参数为每个重载函数产生不同的内部标识符;但是,如果遇到了C++程序要调用已经被编译后的C函数,那该怎么办呢?比如上面的int Add ( int a , int b )函数。该函数被C编译器后在库中的名字为_Add,而C++编译器则会生成像_Add_int_int之类的名字用来支持函数重载和类型安全。由于编译后的名字不同,C++程序不能直接调用C函数,所以C++提供了一个C连接交换指定符号extern “C”来解决这个问题;所以,在上面的DLL中,Add函数的声明格式为:extern “C” EXPORTS_DEMO int Add (int a , int b)。这样就告诉了C++编译器,函数Add是个C连接的函数,应该到库中找名字_Add,而不是找_Add_int_int。当我们将上面DLL中的”C”去掉,编译生成新的DLL,使用Dependency Walker工具查看该DLL,如图:
请注意导出方式为C++,而且导出的Add函数的名字添加了很多的东西,当使用这种方式导出时,客户端调用时,代码就是下面这样:
[cpp] view plain copy
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
- int main(int argc, char *argv[])
- {
- HMODULE hDll = LoadLibrary("DLLDemo1.dll");
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, "?Add@@YAHHH@Z");
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- }
请注意GetProcAddress函数的第二个参数,该参数名就是导出的函数名,在编码时,写这样一个名字是不是很奇怪啊。当我们使用extern “C”方式导出时,截图如下:
注意导出方式为C,而且函数名现在就是普通的Add了。我们再使用GetProcAddress时,就可以直接指定Add了,而不用再加那一长串奇怪的名字了。
工程下载)
果冻说所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。如果这篇文章对你有帮助,你可以请我喝杯咖啡。? 本文链接:http://www.jellythink.com/archives/111? 订阅本站:http://www.jellythink.com/feed
? 转载请注明来源:果冻想 ? ******************************************************************************************************
参考上面的内容,自己在VS2010开发环境上测试了一遍,测试步骤如下:
view plain copy
- #ifndef _MYCODE_H_
- #define _MYCODE_H_
- #ifdef DLLDEMO1_EXPORTS
- #define EXPORTS_DEMO _declspec( dllexport )
- #else
- #define EXPORTS_DEMO _declspec(dllimport)
- #endif
- extern "C" EXPORTS_DEMO int Add (int a , int b);
- #endif
MyCode.cpp文件:
[cpp] view plain copy
- #include "MyCode.h"
- int Add ( int a , int b )
- {
- return ( a + b );
- }
编译工程,就会在Debug文件下生成DllDemo.dll文件。
view plain copy
- // DllTest.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include
- //#include "..\\DLLDemo1\\MyCode.h"
- using namespace std;
- #pragma comment(lib, "..\\debug\\DllDemo.lib") //***********************************************************************问题1
- extern "C" _declspec(dllimport) int Add(int a, int b);
- int _tmain(int argc, _TCHAR* argv[])
- {
- cout<
- while(1);//程序运行到这,方便看运行结果
- return 0;
- }
运行结果如下图:
注意:导入库文件的目录必须在本工程的目录下,也就是说要把生成的dll和lib文件都要拷贝到该工程的目录下,因为不再该目录下,尽管修改了路径,仍然提示找不到DllDemo.dll,不知道为什么?
view plain copy
- // DllTest1.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
-
- int _tmain(int argc, _TCHAR* argv[])
- {
-
- HMODULE hDll = LoadLibrary(_T("DllDemo.dll"));
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- while(1);
- }
运行结果如下图:
view plain copy
- #ifndef _MYCODE_H_
- #define _MYCODE_H_
- extern "C" int Add (int a , int b);
- #endif
MyCode.cpp文件:
[cpp] view plain copy
- #include "MyCode.h"
- int Add ( int a , int b )
- {
- return ( a + b );
- }
然后添加模块定义文件(.def文件):
添加代码:
[cpp] view plain copy
- LIBRARY "DllDemo" //这里的字符串名和工程名要一致
- EXPORTS
- Add @1;Export the Add function
编译工程,即刻生成DllDemo.dll文件。
使用def文件,生成了DLL,客户端调用代码如下:
[cpp] view plain copy
- // DllTest2.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
- int _tmain(int argc, _TCHAR* argv[])
- {
- HMODULE hDll = LoadLibrary("DllDemo.dll");
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, MAKEINTRESOURCE(1));
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- while(1);
- }
工程代码下载:
1. 生成动态链接库(_declspec(dllexport)方式导出函数)
2.生成动态链接库(以.def文件(模块定义文件)方式导出函数)
3.以加载时动态链接方式调用DLL
4.以运行时动态链接方式调用DLL
5.以模块定义方式(.def文件)建立的动态链接库的调用
遇到的问题:
1.库导入的时候目录的问题。对应文中的问题1,后面有解释。
2.字符集的问题(是Unicode字符集还是多字节集),两种方案,一种修改字符集为多字节集,二是将字符串前面加 _T(""),文中问题2
3.不知道怎么通过模块定义文件方式生成DLL,通过看参考博客的代码找到了答案,主要修改头文件,和添加模块定义文件。
4.模块定义文件中的库文件名应和工程名一致。
参考上面的内容,自己在VS2010开发环境上测试了一遍,测试步骤如下:
view plain copy
- #ifndef _MYCODE_H_
- #define _MYCODE_H_
- #ifdef DLLDEMO1_EXPORTS
- #define EXPORTS_DEMO _declspec( dllexport )
- #else
- #define EXPORTS_DEMO _declspec(dllimport)
- #endif
- extern "C" EXPORTS_DEMO int Add (int a , int b);
- #endif
MyCode.cpp文件:
[cpp] view plain copy
- #include "MyCode.h"
- int Add ( int a , int b )
- {
- return ( a + b );
- }
编译工程,就会在Debug文件下生成DllDemo.dll文件。
view plain copy
- // DllTest.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include
- //#include "..\\DLLDemo1\\MyCode.h"
- using namespace std;
- #pragma comment(lib, "..\\debug\\DllDemo.lib") //***********************************************************************问题1
- extern "C" _declspec(dllimport) int Add(int a, int b);
- int _tmain(int argc, _TCHAR* argv[])
- {
- cout<
- while(1);//程序运行到这,方便看运行结果
- return 0;
- }
运行结果如下图:
注意:导入库文件的目录必须在本工程的目录下,也就是说要把生成的dll和lib文件都要拷贝到该工程的目录下,因为不再该目录下,尽管修改了路径,仍然提示找不到DllDemo.dll,不知道为什么?
view plain copy
- // DllTest1.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
-
- int _tmain(int argc, _TCHAR* argv[])
- {
-
- HMODULE hDll = LoadLibrary(_T("DllDemo.dll"));
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- while(1);
- }
运行结果如下图:
view plain copy
- #ifndef _MYCODE_H_
- #define _MYCODE_H_
- extern "C" int Add (int a , int b);
- #endif
MyCode.cpp文件:
[cpp] view plain copy
- #include "MyCode.h"
- int Add ( int a , int b )
- {
- return ( a + b );
- }
然后添加模块定义文件(.def文件):
添加代码:
[cpp] view plain copy
- LIBRARY "DllDemo" //这里的字符串名和工程名要一致
- EXPORTS
- Add @1;Export the Add function
编译工程,即刻生成DllDemo.dll文件。
使用def文件,生成了DLL,客户端调用代码如下:
[cpp] view plain copy
- // DllTest2.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
- int _tmain(int argc, _TCHAR* argv[])
- {
- HMODULE hDll = LoadLibrary("DllDemo.dll");
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, MAKEINTRESOURCE(1));
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- while(1);
- }
工程代码下载:
1. 生成动态链接库(_declspec(dllexport)方式导出函数)
2.生成动态链接库(以.def文件(模块定义文件)方式导出函数)
3.以加载时动态链接方式调用DLL
4.以运行时动态链接方式调用DLL
5.以模块定义方式(.def文件)建立的动态链接库的调用
遇到的问题:
1.库导入的时候目录的问题。对应文中的问题1,后面有解释。
2.字符集的问题(是Unicode字符集还是多字节集),两种方案,一种修改字符集为多字节集,二是将字符串前面加 _T(""),文中问题2
3.不知道怎么通过模块定义文件方式生成DLL,通过看参考博客的代码找到了答案,主要修改头文件,和添加模块定义文件。
4.模块定义文件中的库文件名应和工程名一致。
- // DllTest.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include
- //#include "..\\DLLDemo1\\MyCode.h"
- using namespace std;
- #pragma comment(lib, "..\\debug\\DllDemo.lib") //***********************************************************************问题1
- extern "C" _declspec(dllimport) int Add(int a, int b);
- int _tmain(int argc, _TCHAR* argv[])
- {
- cout<
- while(1);//程序运行到这,方便看运行结果
- return 0;
- }
view plain copy
- // DllTest1.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
-
- int _tmain(int argc, _TCHAR* argv[])
- {
-
- HMODULE hDll = LoadLibrary(_T("DllDemo.dll"));
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, "Add");
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- while(1);
- }
运行结果如下图:
view plain copy
- #ifndef _MYCODE_H_
- #define _MYCODE_H_
- extern "C" int Add (int a , int b);
- #endif
MyCode.cpp文件:
[cpp] view plain copy
- #include "MyCode.h"
- int Add ( int a , int b )
- {
- return ( a + b );
- }
然后添加模块定义文件(.def文件):
添加代码:
[cpp] view plain copy
- LIBRARY "DllDemo" //这里的字符串名和工程名要一致
- EXPORTS
- Add @1;Export the Add function
编译工程,即刻生成DllDemo.dll文件。
使用def文件,生成了DLL,客户端调用代码如下:
[cpp] view plain copy
- // DllTest2.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
- int _tmain(int argc, _TCHAR* argv[])
- {
- HMODULE hDll = LoadLibrary("DllDemo.dll");
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, MAKEINTRESOURCE(1));
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- while(1);
- }
工程代码下载:
1. 生成动态链接库(_declspec(dllexport)方式导出函数)
2.生成动态链接库(以.def文件(模块定义文件)方式导出函数)
3.以加载时动态链接方式调用DLL
4.以运行时动态链接方式调用DLL
5.以模块定义方式(.def文件)建立的动态链接库的调用
遇到的问题:
1.库导入的时候目录的问题。对应文中的问题1,后面有解释。
2.字符集的问题(是Unicode字符集还是多字节集),两种方案,一种修改字符集为多字节集,二是将字符串前面加 _T(""),文中问题2
3.不知道怎么通过模块定义文件方式生成DLL,通过看参考博客的代码找到了答案,主要修改头文件,和添加模块定义文件。
4.模块定义文件中的库文件名应和工程名一致。
- #ifndef _MYCODE_H_
- #define _MYCODE_H_
- extern "C" int Add (int a , int b);
- #endif
- #include "MyCode.h"
- int Add ( int a , int b )
- {
- return ( a + b );
- }
添加代码: [cpp] view plain copy
- LIBRARY "DllDemo" //这里的字符串名和工程名要一致
- EXPORTS
- Add @1;Export the Add function
- // DllTest2.cpp : 定义控制台应用程序的入口点。
- //
- #include "stdafx.h"
- #include
- #include
- using namespace std;
- typedef int (*AddFunc)(int a, int b);
- int _tmain(int argc, _TCHAR* argv[])
- {
- HMODULE hDll = LoadLibrary("DllDemo.dll");
- if (hDll != NULL)
- {
- AddFunc add = (AddFunc)GetProcAddress(hDll, MAKEINTRESOURCE(1));
- if (add != NULL)
- {
- cout<
- }
- FreeLibrary(hDll);
- }
- while(1);
- }