VS 2015 DLL的创建、静态调用和动态调用

DLL的创建

创建步骤

文件 -> 新建 -> 项目 -> "新建项目"对话框 -> “已安装” -> 模板 -> 其他语言 -> Vistual C++ -> Win32 控制台应用程序。

这里写图片描述

在"Win32 应用程序向导"对话框中

  • “控制台程序类型"选择"DLL(D)”
  • “附加选项"勾选"导出符号(X)”

这里写图片描述

这里写图片描述

导入(导出)标记的宏定义

下列 ifdef 块是创建使从 DLL 导出更简单的宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的DLLDEMO_EXPORTS符号编译的。在使用此 DLL 的任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将DLLDEMO_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的符号视为是被导出的。

#ifdef DLLDEMO_EXPORTS
#define DLLDEMO_API __declspec(dllexport)
#else
#define DLLDEMO_API __declspec(dllimport)
#endif

导出符号标识的宏定义位于:解决方案资源管理器 -> 项目属性 -> "项目属性页"对话框 -> “配置属性” -> C/C++ -> 预处理器 -> 预处理器定义

这里写图片描述

修改DLL项目

DllDemo.h

#ifdef DLLDEMO_EXPORTS
#define DLLDEMO_API __declspec(dllexport)
#else
#define DLLDEMO_API __declspec(dllimport)
#endif

// 此类是从 DllDemo.dll 导出的
class DLLDEMO_API CDllDemo {
public:
	CDllDemo(void);
	// TODO:  在此添加您的方法。
};

extern DLLDEMO_API int nDllDemo;
extern "C" DLLDEMO_API int nExternCDllDemo;

DLLDEMO_API int fnDllDemo(void);
extern "C" DLLDEMO_API int fnExternCDllDemo(void);

char DLLDEMO_API fnDefault(char, int, float);
char DLLDEMO_API __stdcall fnstdcall(char, int, float);
char DLLDEMO_API __cdecl fncdecl(char, int, float);
char DLLDEMO_API __fastcall fnfastcall(char, int, float);

dllmain.cpp

// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "stdafx.h"
#include <stdio.h>
#include <tchar.h>

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
					 )
{
	/*
	获取模块文件名,如果是dll的话,获取的是调用它的可执行文件路径
	WINBASEAPI 
	_Success_(return != 0)
	_Ret_range_(1, nSize) 
	DWORD WINAPI GetModuleFileNameW(
		_In_opt_ HMODULE hModule,
		_Out_writes_to_(nSize, ((return < nSize) ? (return + 1) : nSize)) LPWSTR lpFilename,
		_In_ DWORD nSize
    );
	*/
	TCHAR lpFilename[MAX_PATH];
	DWORD ret = GetModuleFileName(NULL, lpFilename, MAX_PATH);
	if (ret) {
		_tprintf(_T("GetModuleFileName -> lpFilename=%s\n"), lpFilename);
	}
	else {
		printf("GetModuleFileName -> fail(%ld)", GetLastError());
	}
	
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
		printf("DLL_PROCESS_ATTACH\n");
		break;
	case DLL_THREAD_ATTACH:
		printf("DLL_THREAD_ATTACH\n");
		break;
	case DLL_THREAD_DETACH:
		printf("DLL_THREAD_DETACH\n");
		break;
	case DLL_PROCESS_DETACH:
		printf("DLL_PROCESS_DETACH\n");
		break;
	}
	return TRUE;
}

DllDemo.cpp

// DllDemo.cpp : 定义 DLL 应用程序的导出函数。

#include "stdafx.h"
#include "DllDemo.h"

// 这是导出变量的一个示例
DLLDEMO_API int nDllDemo = 1;
DLLDEMO_API int nExternCDllDemo = 2;

// 这是导出函数的一个示例。
DLLDEMO_API int fnDllDemo(void)
{
    return 42;
}
DLLDEMO_API int fnExternCDllDemo(void)
{
	return 142;
}

char DLLDEMO_API fnDefault(char, int, float) 
{
	return 'a';
}
char DLLDEMO_API __stdcall fnstdcall(char, int, float)
{
	return 'b';
}
char DLLDEMO_API __cdecl fncdecl(char, int, float)
{
	return 'c';
}
char DLLDEMO_API __fastcall fnfastcall(char, int, float)
{
	return 'd';
}

// 这是已导出类的构造函数。
// 有关类定义的信息,请参阅 DllDemo.h
CDllDemo::CDllDemo()
{
    return;
}

生成DLL有以下几种方案

  • 解决方案资源管理器 -> "DLL项目"右键 -> 生成(U)
  • 菜单栏 -> 生产DLL(U)(Shift + F6)

然后会在项目的根目录的Debug或Release文件夹下生成相应的文件。

dumpbin查看dll(或lib)的导出符号

**dumpbin.exe 位于 **:C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\bin

这里写图片描述

查看编译后的DLL函数名用到的只是"EXPORTS"参数而已。函数名为name列等号的左侧。

可以看到extern "C"修饰的函数名与原函数名一致。原因可以参看《函数重载与Extern “C”》一文: http://blog.csdn.net/chy555chy/article/details/53015808。另外说明一点,使用extern "C"修饰的C++函数不能使用C++的重载特性,因为导出的函数符号只是原函数名,编译器没有将参数类型信息加到导出的函数名中,因此无法区分重载函数。

这里写图片描述

DLL的调用

导入DLL

(1)当"DLL项目"和"可执行项目"属于同一个解决方案时。在"DLL项目"右键"生成"可以在项目的Debug目录下生成相应的DLL和LIB。然后在"可执行项目"右键"设为启动项目",然后点击"本地Windows调试器"即可。

  • 静态调用
    直接运行会报无法打开LIB(不加后缀默认是LIB)或DLL
    这里写图片描述
    这里写图片描述
    解决方案:在"可执行项目"右键 -> 添加(D) -> 引用®…
    这里写图片描述
    可以看到引用处多了相应的DLL文件
    这里写图片描述

  • 动态调用
    如果是动态调用,此时可正常运行
    这里写图片描述

(2)如果是外部项目的dll

  • 静态调用(有以下2种方法)

  • #param comment("lib", "path\\*lib")中指定其路径。

  • 解决方案资源管理器 -> “可执行项目"右键"属性” -> "项目属性页"对话框 -> 配置属性 -> “链接器” -> 输入 -> 附加依赖项目 -> 编辑 -> 输入lib文件的完整路径(而不是lib的文件名)
    这里写图片描述

  • 动态调用
    LoadLibrary(_T("path\\*.dll"));中指定其路径。

DLL调用有两种方式,一种是静态调用,另外一种是动态调用

静态调用(同时需要头文件、LIB和DLL文件,缺一不可)

静态调用是一种显式的调用方式,即在编程的时候便知道了被调用的DLL中的接口函数,在编译链接的时候将DLL与工程生成的exe相关联。

#include "stdafx.h"
#include "../DllDemo/DllDemo.h"

//lib后缀可以省略,但不可以改为dll
#pragma comment(lib, "DllDemo.lib")

int main()
{
	printf("%d\n", nDllDemo);
	printf("%d\n", fnDllDemo());
	printf("%d\n", fnExternCDllDemo());
	_tsystem(_T("pause"));
    return 0;
}

动态调用(仅需要DLL,不需要头文件和LIB)

动态调用是一种隐式的调用方式,即程序运行过程中装载DLL,然后获取指定函数名称的接口函数,然后再调用之。

#include "stdafx.h"
#include <Windows.h>

int main() 
{
	//参考 http://blog.csdn.net/g5dsk/article/details/6680698
	HMODULE hModule = LoadLibrary(_T("DllDemo.dll"));// 虽然 MSDN Library 说这里如果指定了路径,要用 backslashes (\),不要用 forward slashes (/),但其实用二者都可以。注:如果用 \,要用 \\。
	if (hModule == NULL || hModule == INVALID_HANDLE_VALUE) {
		return -1;
	}
	/*
	WINBASEAPI FARPROC WINAPI GetProcAddress(
		_In_ HMODULE hModule,
		_In_ LPCSTR  lpProcName //这个是dump /EXPORT *.dll “name”列等号前的值
    );
	返回的是函数或变量的地址,即函数指针或指向变量地址的指针
	*/
	typedef int(*TYPE_fnDllDemo) ();
	typedef int(*TYPE_fnExternCDllDemo) ();
	int *nDllDemo = (int *)GetProcAddress(hModule, "?nDllDemo@@3HA");
	TYPE_fnDllDemo fnDllDemo = (TYPE_fnDllDemo)GetProcAddress(hModule, "?fnDllDemo@@YAHXZ");
	int *nExternCDllDemo = (int *)GetProcAddress(hModule, "nExternCDllDemo");
	TYPE_fnExternCDllDemo fnExternCDllDemo = (TYPE_fnExternCDllDemo)GetProcAddress(hModule, "fnExternCDllDemo");
	if(nDllDemo != NULL)
		printf("*nDllDemo = %d\n", *nDllDemo);
	if(fnDllDemo != NULL)
		printf("fnDllDemo() = %d\n", fnDllDemo());
	if (nExternCDllDemo != NULL)
		printf("*nExternCDllDemo = %d\n", *nExternCDllDemo);
	if(fnExternCDllDemo != NULL)
		printf("fnExternCDllDemo() = %d\n", fnExternCDllDemo());
	_tsystem(_T("pause"));
	return 0;
}

运行截图

注意:DLL_PROCESS_DETACH是在关闭命令行的时候被调用,而不是不会调用。这里被system(“pause”)暂停了,并不是说程序结束。另外即使点击关闭,基本也看不到,因为打印完窗口立马就被关闭了,这些都是一瞬间完成的。

这里写图片描述

使用VS2015编译和调用动态链接库dll 1. 首先建工程,选择dll,记得勾上“导出符号” 后面不用自己搞那些宏定义会省事很多。 建立工程myDll,记得勾上“导出符号” 类型选择dll 2. IDE自动生成的代码已经把整个架构弄好了,其中和项目同名的.h和.cpp文件就是我们自己写代码的地方了。我想写的dll是导出一个类,在这里我就直接在它自动生成的CmyDll类上面改了。 myDll.h myDll.cpp 在mydll.h和mydll.cpp中给类添加成员函数 //mydll.h class MYDLL_API CmyDll { public: CmyDll(void); // TODO: 在此添加您的方法。 int myFunction(int a, int b); }; //mydll.cpp int CmyDll::myFunction(int a, int b) { return a*b; } 3.编译的时候我选择了release,这里可以用默认的debug也行 在mydll.h和mydll.cpp中给类添加成员函数 最后生成解决方案后产生的mydll.lib和mydll.dll就是我们需要的二进制文件了。lib文件是编译是要用的,而dll调用这个库的程序运行时需要的。 调用dll 1.重新建立一个工程 这回选择普通的控制台程序就行了。我建了个名为myDllCall的工程。 2.把库的头文件include进来,以及连接lib文件 其中 include进来的 myDll.h 和 **#pragma comment()**的lib根据自己的路径写。 #include "stdafx.h" #include "../../myDll/myDll/myDll.h" //头文件 #pragma comment(lib,"../../myDll/Release/myDll.lib") //调用自己写的外部库 #include int main() { CmyDll mydll; int a, b; std::cin >> a >> b; std::cout << mydll.myFunction(a, b) <> a >> b; std::cout << mydll.myFunction(a, b) << std::endl; return 0; } 3.dll放到可执行文件同一目录下面 刚刚的代码直接编译没问题,运行会报错. 直接编译没问题,运行会报错 原因是dll要和生成的可执行文件在同一个目录下,我把mydll.dll放进去之后就解决了。 我们成功的在自己的工程里调用了外部的类 可以看到我们成功的在自己的工程里调用了外部的类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值