【dll】windows下使用vs编译动态链接库dll与使用

本文详细记录了如何使用Visual Studio创建并编译C语言函数库DLL,包括工程配置、代码声明与编译,以及如何验证和导入dll。适合初学者解决VS配置问题,确保DLL库的正确生成。

一、需求

 编译出可供其他平台使用的基于c语言的函数库dll。

二、前言

 网上很多相关操作,但部分说明不详细或是没有配套的示意图导致操作遇到困难,且使用vs时会由于需要进行各种配置导致编译生成失败,因此才准备将每一步操作都记录下来,确保按照操作步骤能够顺利生成dll库。分为两部分,第一部分是工程创建,经过各种工程类型选择的测试,使用如下方法可减少更多的配置操作。第二部分是c代码编写,涉及到MicrosoftVC的一些关键字与声明的操作。

三、代码工程与项目操作

默认已安装且可正常使用visual stdio开发项目。

3.1新建项目

左上角标签卡,“文件”->“新建”->“项目”,如下图:

3.2使用向导新建

经试验多个项目类,其他类需要创建后进行配置,使用向导新建项目后可直接编译生成dll库。因此选择“Visual C++”->“Windows桌面”->"Windows桌面向导“,命名工程后确认生成,如下图:

 3.3选择应用程序类型

应用程序类型默认为”.exe“,此时需进入下拉选框选择”动态链接库(dll)“。然后勾选”空项目“确保新生成的工程中代码干净。如下图: 

3.4创建完成

此时工程相关操作已完成。

3.5平台切换

默认为x86平台,需切换到x64平台,否则生成的dll会造成不兼容导致无法准确读取的情况,配置如图:

四、c代码操作

文件创建与基本声明部分省略,源码会放到文章末尾的附录处。

4.1声明修饰

如需使用dll,在函数的外部声明处需使用”__declspec(dllexport)“和”__declspec(dllimport)“关键词进行修饰。

  • __declspec(dllexport):用于通过编译产生dll进行数据导出的函数
  • __declspec(dllimport):用于通过调用dll进行获取导入数据的函数

上述修饰一般在函数类型声明前,如下:

 4.2编译

编译成功后会在子目录下产生对应工程名称的dll文件,如下:

五、验证使用

验证需使用另外一个全新的项目,例如自带”hello world!“的 控制台应用。

5.1头文件声明

必备声明如下:

#include<Windows.h>//必备

 5.2读取dll文件

使用LoadLibrary函数读取,用HINSTANCE类型变量接收。

HINSTANCE hDll = LoadLibrary(_T("dll1.dll"));
if (hDll == NULL) {
	printf("lib not exist.");
	return 1;
}

 5.3定义函数类型

已知dll函数的声明类型与参数类型。

using functionPtrCreate = t_obj*(*)();
using functionPtrSetID = void(*)(t_obj*,long);
using functionPtrGetID = long(*)(t_obj*);

5.4GetProcAddress加载函数

functionPtrCreate objCreate = (functionPtrCreate)GetProcAddress(hDll, "objCreate");
functionPtrSetID objSetID = (functionPtrSetID)GetProcAddress(hDll, "objSetID");
functionPtrGetID objGetID = (functionPtrGetID)GetProcAddress(hDll, "objGetID");

5.5使用加载的函数输出结果

输入:

objSetID(temp, 10086);

 输出:

std::cout << objGetID(temp);

结果: 

附录 源码

1、dll_config_global.h

#ifndef _DLL_CONFIG_GLOBAL_H_
#define _DLL_CONFIG_GLOBAL_H_

/*逻辑分支,判断环境*/
#if defined(_MSC_VER) || defined(WIN64) || defined(_WIN64) || defined(__WIN64__) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__)
#  define DLL_CONFIG_DECL_EXPORT __declspec(dllexport)//用于dll数据导出
#  define DLL_CONFIG_DECL_IMPORT __declspec(dllimport)//用于导入dll数据
#else
#  define DLL_CONFIG_DECL_EXPORT     __attribute__((visibility("default")))//失效
#  define DLL_CONFIG_DECL_IMPORT     __attribute__((visibility("default")))//失效
#endif

#endif // _DLL_CONFIG_GLOBAL_H_

 2、dll_demo.h

#ifndef _DLL_DEMO_H_
#define _DLL_DEMO_H_

/*dll编译需要使用的全局配置文件*/
#include "dll_config_global.h"

/*c库*/
#include<stdio.h>
#include<stdint.h>
#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#include<math.h>

/*结构体类声明*/
typedef struct _obj {
	bool En;//使能
	long ID;//id
	const char * name;//一般名称
	void * data;//数据包
} t_obj;//基本对象

/*函数外部声明*/
#ifdef _cplusplus
extern "C" {
#endif // _cplusplus{}

	DLL_CONFIG_DECL_EXPORT t_obj* objCreate();
	DLL_CONFIG_DECL_EXPORT void objEnable(t_obj*obj);
	DLL_CONFIG_DECL_EXPORT void objDisable(t_obj*obj);
	DLL_CONFIG_DECL_EXPORT bool objGetEn(t_obj*obj);
	DLL_CONFIG_DECL_EXPORT void objClearID(t_obj*obj);
	DLL_CONFIG_DECL_EXPORT void objSetID(t_obj*obj, long val);
	DLL_CONFIG_DECL_EXPORT long objGetID(t_obj*obj);
	DLL_CONFIG_DECL_EXPORT void objClearName(t_obj*obj);
	DLL_CONFIG_DECL_EXPORT void objSetName(t_obj*obj, char*val);
	DLL_CONFIG_DECL_EXPORT const char* objGetName(t_obj*obj);
	DLL_CONFIG_DECL_EXPORT void objClearData(t_obj*obj);
	DLL_CONFIG_DECL_EXPORT void objSetData(t_obj*obj, void*p, long length);
	DLL_CONFIG_DECL_EXPORT void* objGetData(t_obj*obj);

#ifdef _cplusplus

#endif // _cplusplus{}


#endif // _DLL_DEMO_H_

3、dll_demo.c

#include"dll_demo.h"

//----节0
/*创建基本对象*/
DLL_CONFIG_DECL_EXPORT t_obj* objCreate() {
	return (t_obj*)malloc(sizeof(t_obj));
}

//----节1
/*使能对象*/
DLL_CONFIG_DECL_EXPORT void objEnable(t_obj*obj)
{
	obj->En = true;
}
/*失能对象*/
DLL_CONFIG_DECL_EXPORT void objDisable(t_obj*obj)
{
	obj->En = false;
}
/*获取对象状态*/
DLL_CONFIG_DECL_EXPORT bool objGetEn(t_obj*obj)
{
	return obj->En;
}

//----节2
/*清空id*/
DLL_CONFIG_DECL_EXPORT void objClearID(t_obj*obj)
{
	obj->ID = 0x00;
}
/*设置id*/
DLL_CONFIG_DECL_EXPORT void objSetID(t_obj*obj,long val)
{
	obj->ID = val;
}
/*获取对象id*/
DLL_CONFIG_DECL_EXPORT long objGetID(t_obj*obj)
{
	return obj->ID;
}

//----节3
/*清空名称*/
DLL_CONFIG_DECL_EXPORT void objClearName(t_obj*obj)
{
	obj->name = NULL;
}
/*设置名称*/
DLL_CONFIG_DECL_EXPORT void objSetName(t_obj*obj,char*val)
{
	obj->name = val;
}
/*获取对象名称*/
DLL_CONFIG_DECL_EXPORT const char* objGetName(t_obj*obj)
{
	return obj->name;
}

//----节4
/*清空数据包*/
DLL_CONFIG_DECL_EXPORT void objClearData(t_obj*obj)
{
	free(obj->data);
	obj->data = NULL;
}
/*设置数据包*/
DLL_CONFIG_DECL_EXPORT void objSetData(t_obj*obj,void*p,long length)
{
	obj->data = malloc(length);
	memcpy(obj->data, p, length);
}
/*获取对象数据包*/
DLL_CONFIG_DECL_EXPORT void* objGetData(t_obj*obj)
{
	return obj->data;
}


4、dllUse.cpp

#include<iostream>
#include<Windows.h>//必备
#include<tchar.h>
//#pragma comment(lib,"dll1.lib")
typedef struct _obj {
	bool En;//使能
	long ID;//id
	const char * name;//一般名称
	void * data;//数据包
} t_obj;//基本对象

int main()
{
	HINSTANCE hDll = LoadLibrary(_T("dll1.dll"));
	if (hDll == NULL) {
		printf("lib not exist.");
		return 1;
	}
	using functionPtrCreate = t_obj*(*)();
	using functionPtrSetID = void(*)(t_obj*,long);
	using functionPtrGetID = long(*)(t_obj*);
	//找到所需要的函数
	
	functionPtrCreate objCreate = (functionPtrCreate)GetProcAddress(hDll, "objCreate");
	functionPtrSetID objSetID = (functionPtrSetID)GetProcAddress(hDll, "objSetID");
	functionPtrGetID objGetID = (functionPtrGetID)GetProcAddress(hDll, "objGetID");
	t_obj* temp = objCreate();
	objSetID(temp, 10086);
	std::cout << objGetID(temp);

}

使用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、付费专栏及课程。

余额充值