需求:C开发的控制台程序封装为dll,用于C++程序调用
开发环境:VS 2013 Pro
操作步骤
1. 创建dll
- 新建工程:C/C++ -> Win32项目, 工程命名dll_generate
- win32应用程序向导设置:应用程序类型 - DLL,附加选项 - 空项目
- 在解决方案资源管理器中,在项目选项上右键 - 属性
- 弹出的项目属性页面中,配置属性 - 常规,右侧配置选项中选择 平台工具集,在下拉列表中选择Visual Studio 2013 - Windows XP(v120_xp)
- 资源管理器中,头文件 - 右键 - 添加 - 新建项
- 选择头文件,命名 dll_generate.h。同样方法,添加 component1.h 和 component2.h
- 编辑头文件代码如下
// dll_generate.h
#pragma once
#ifdef DLL_IMPLEMENT
#define DLL_API _declspec(dllexport)
#else
#define DLL_API _declspec(dllimport)
#endif
#ifdef _cplusplus
extern "C" {
#endif
int DLL_API Add(int a, int b);
#ifdef _cplusplus
}
#endif
dll_generate.h 中
- DLL_IMPLEMENT用于区分引用该头文件的源文件需要导入dll还是导出dll,由此决定加在函数声明之前的宏定义 DLL_API 为导入命令还是导出命令
- 如果文件中定义了标志_cplusplus,认为该文件为C++文件。C++语言编写的dll头文件,函数声明需要采用
extern "C" { *function declaration* }
的形式 - 只有添加DLL_API或等效的导入、导出语句的函数声明,才是可以使用dll调用的函数,调用形式与函数声明格式相同
// component1.h
#ifndef __COMPONENT1__
#define __COMPONENT1__
extern int Mul(int a, int b);
#endif
component1.h 中声明了一个函数 Mul,实现两数相乘的计算。该函数不在dll中导出,仅在程序内部调用,函数体在源文件中实现
// component2.h
#ifndef __COMPONENT2__
#define __COMPONENT2__
extern int COMP(int a, int b);
#endif
component2.h 中声明了一个函数 COMP,实现两数的比较。该函数不在dll中导出,仅在程序内部调用,函数体在源文件中实现
头文件中的
#ifndef ... #define ... #endif
用于防止重复定义
- 采用类似的方法,新建三个源文件 dll_generate.c, component1.c, component2.c,添加代码如下
// component1.c
#include "component1.h"
int Mul(int a, int b)
{
return (a * b);
}
component1.c 只需要包含 component1.h
component1.c 中实现了其头文件中定义的 Mul 函数,且Mul函数体只依赖传入参数,没有调用其他函数
// component2.c
#include "component2.h"
int COMP(int a, int b)
{
if (a > b)
return a;
else
return b;
}
component2.c 只需要包含 component2.h
component2.c 中实现了其头文件中定义的 COMP 函数,且 COMP 函数体只依赖传入参数,没有调用其他函数
// dll_generate.c
#define DLL_IMPLEMENT
#include "dll_generate.h"
#include "component1.h"
#include "component2.h"
int Add(int a, int b)
{
int c = Mul(a, b);
int d = COMP(a, b);
return (c * d);
}
dll_generate.c 中
- 由于其定义的函数体 Add 中调用了 Mul 和 COMP,需要包含 component1.h 和 component2.h
- 该dll工程需要导出dll,所以需要宏定义 DLL_IMPLEMENT
- 由于dll_generate.h 中的预定义,DLL_IMPLEMENT 宏定义需要在包含 dll_generate.h之前
- Add 函数体中调用了在其他头文件中声明的函数 (Mul 和 COMP),但是在dll_generate.h 中没有包含其他头文件——声明导出函数的头文件中不需要包含其他头文件,但被导出的函数定义中如果调用了其他函数,需要在源文件中包含对应的头文件
-
菜单栏 生成 - 生成解决方案(重新生成解决方案)。输出窗口显示成功,并生成 dll_generate.dll
-
查看生成结果的文件夹(本次采用debug模式,所以产生的文件在Debug目录下),产生了 dll_generate.dll 和 dll_generate.lib 连个文件,dll生成成功
2. 创建测试程序
-
新建项目,选择 Win32控制台程序,命名为 dll_test
-
Win32向导中,选择 控制台应用程序 - 预编译头,完成
-
在dll_test\dll_test 目录下新建目录 include 和 lib,将刚刚生成的 dll_generate.lib 复制到新建的 lib 文件夹下,将 dll_generate项目中包含导出函数声明的的 dll_generate.h 复制到新建的include 文件夹下
-
配置 dll_test 属性
- 配置属性 - 常规 - 平台工具集,选择 Visual Studio 2013 - Windows XP (v120_xp)
- 配置属性 - VC++目录 - 包含目录,添加新建的 include 文件夹路径
- 配置属性 - VC++目录 - 库目录,添加新建的 lib 文件夹
- 链接器 - 输入 - 附加依赖项,手动输入添加 dll_generate.lib
-
头文件 - 添加 - 现有项,添加复制的 dll_generate.h
-
编辑源文件 dll_test.cpp
- 添加预编译语句
#define _cplusplus
#include "./include/dll_generate.h"
此处。由于调用 dll 的 dll_test 为c++编写,所以需要预定义标志 _cplusplus,使 dll_generate.h编译为c++调用的形式
- main 函数中调用 dll 封装的函数
int c = Add(2, 3);
printf("%d", c);
getchar();
- 生成解决方案
不要此时运行程序,否则会由于尚未添加dll文件而出现如下报错
- 将 dll_generate.dll 复制到生成的 dll_test.exe同一目录下(此处为 dll_test\Debug)
- 运行程序,弹出窗口并显示正确结果,说明调用成功
至此,dll的封装和调用成功。
- 本案例仅作为 dll 封装的示范,不同版本的VS,操作和设置可能会有不同
- 案例说明,无论dll中封装的函数调用或嵌套了多少其他函数,无论源文件引用了多少头文件,只需要封装被调用的函数即可,其他内部调用函数不需要封装