1. vs工程类型相关知识
在使用vs创建工程时,如果在“模板”中选的是“Win32”,不管是“Win32控制台应用程序”、还是“Win32项目”,工程创建完成后,在“属性--C/C++--预处理器定义”中都会有宏定义:"WIN32",因此,可以在代码中通过检查是否有宏定义"WIN32"对代码做好windows和linux的控制;
在创建win32的工程时,不论是选择“Win32控制台应用程序”、还是“Win32项目”最终都会跳到”应用程序设置“向导,该向导中需要使用者选择”应用程序类型“,共包括四种:windows应用程序、控制台程序、DLL(D)、静态库(S)。只是起初如果选择的是“Win32控制台应用程序”这里vs将默认在此向导中把类型选择为”控制台应用程序“,如果起初选择的是“Win32项目”这里vs将默认在此向导中把类型选择为”控制台应用程序“windows应用程序”。当我们是要封装dll接口时,在这里要选择“Dll(D)”
在vs创建工程时,如果选择的应用类型是“DLL(D)”时,工程创建完成后,在“属性--C/C++--预处理器定义”中会定义:宏定义"WIN32"、“工程名称(大写字母)_EXPORTS”。
2. 创建dll类型工程的相关知识
在Dll类型的工程中,dll头文件中的宏定义如下:
1
2
3
4
|
1. // 下列 ifdef 块是创建使从 DLL 导出更简单的 2. // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 ONEDLL_EXPORTS 3. // 符号编译的。在使用此 DLL 的 1. // 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 |
// ONEDLL_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的// 符号视为是被导出的。#ifdef ONEDLL_EXPORTS#define ONEDLL_API extern "C" __declspec(dllexport)#else#define ONEDLL_API extern "C" __declspec(dllimport)
#endif
__declspec(dllexport) 表示函数为DLL输出函数,即其他应用程序可以调用该函数
1
|
extern “C” __declspec(dllexport) int add(int a, int b); |
其中 extern “C”为声明为C编译。由于C++编译器在编译的时候会造成其函数名的该变,在其他应用程序中导致函数不可调用,而C编译器则不会在编译后改变其函数名。这样如果用C编译的程序来调用该dll中的函数时,可能会造成找不到该函数。
1
|
__declspec ( dllexport ) __declspec ( dllimport )一般是使用宏的形式 |
这样在DLL代码本身就是__declspec(dllexport) ,在使用DLL的程序中就变成了__declspec(dllimport),这两标志分别用来指明当前的函数将被导出,和当前函数是被导入的。
定义一个接口函数
1.ONEDLL_API int fnOneDll(void);
3. dll的调用方式
DLL的调用分为两种方式:动态和静态(显示动态链接和隐式动态链接)
(1) 动态调用(显示动态链接):
1
2
3
4
5
|
typedef int (*lpAddFun)( int , int ); //宏定义函数指针类型 lpAddFun add; //函数指针 HINSTANCE hDll=LoadLibrary(“path”); add=(lpAddFun)GetProcAddress(hDll, "add" ); //获得dll中的add函数指针 FreeLibrary(hDll); |
在从dll调用中返回的函数、指针或者类都是以指针的方式的,即返回的是函数、变量或类的地址。这里一定要注意,不能简单的用函数名来赋值。
(2) 静态调用(隐式动态链接):
将生成的.dll和.lib文件拷入到调用dll的工程中,用命令
1
2
3
4
5
6
7
8
9
|
#pragma comment(lib,"dllTest.lib"),实现静态调用,即把该dll在编译的时候也编译到exe文件中去,而后在工程中调用时用下面的代码: #pragma comment(lib,"dllTest.lib")//.lib文件中仅仅是关于其对应DLL文件中函数的重定位信息 extern "C" __declspec ( dllimport ) add( int x, int y); int main( int argc, char * argv[]) { int result = add(2,3); printf ( "%d" ,result); return 0; } |
由上述代码可以看出,静态调用方式的顺利进行需要完成两个动作:
(1) 告诉编译器与DLL相对应的.lib文件所在的路径及文件名,#pragma comment(lib,"dllTest.lib")就是起这个作用。程序员在建立一个DLL文件时,连接器会自动为其生成一个对应的.lib文件,该文件包含了DLL 导出函数的符号名及序号(并不含有实际的代码)。在应用程序里,.lib文件将作为DLL的替代文件参与编译。另外一种显式调用的方式是设置vc中的目录和includefiles来实现
(2) 声明导入函数,extern "C" __declspec(dllimport) add(int x,int y)语句中的__declspec(dllimport)发挥这个作用。静态调用方式不再需要使用系统API来加载、卸载DLL以及获取DLL中导出函数的地址。这是因为,当程序员通过静态链接方式编译生成应用程序时,应用程序中调用的与.lib文件中导出符号相匹配的函数符号将进入到生成的EXE 文件中,.lib文件中所包含的与之对应的DLL文件的文件名也被编译器存储在EXE文件内部。当应用程序运行过程中需要加载DLL文件时,Windows将根据这些信息发现并加载DLL,然后通过符号名实现对DLL 函数的动态链接。这样,EXE将能直接通过函数名调用DLL的输出函数,就象调用程序内部的其他函数一样。
总结
以上所述是小编给大家介绍的visual studio 建立工程(dll、控制台程序)等,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对服务器之家网站的支持!
原文链接:https://blog.csdn.net/wangyjfrecky/article/details/80341017