zoukankan      html  css  js  c++  java
  • C++创建动态链接库(*.dll)

    1.      从 “文件”菜单中,选择 “新建”,然后选择 “项目…”

    2.      在“项目类型”窗格中,选择“Visual C++”下的“Win32”

    3.      在“模板”窗格中,选择“Win32 控制台应用程序”

    4.      为项目选择一个名称,如 MathFuncsDll,并将其键入“名称”字段。 为解决方案选择一个名称,如 DynamicLibrary,并将其键入“解决方案名称”字段。

    5.      单击“确定”启动 Win32 应用程序向导。 在“Win32 应用程序向导”对话框的“概述”页中,单击“下一步”

    6.      在“Win32 应用程序向导”中的“应用程序设置”页中,),选择“控制台应用程序”,取消预编译。

    7.      在“Win32 应用程序向导”“应用程序设置”页中,选择“附加选项”下的“空项目”

    8.      单击“完成”创建项目。


    更改生成为*.dll程序



    主要代码:

    #pragma once
    namespace MathFun
    {
    	class MathFuns
    	{
    	public:
    		MathFuns(void);
    		~MathFuns(void);
    		static   _declspec(dllexport) double Add(double num1, double num2);//_declspec(dllexport) 为到处动态链接库的函数 外部可用 如果没改关键字的函数 外部不可调用
    		static _declspec(dllexport) double Subtract(double num1, double num2);
    		static _declspec(dllexport) double Multiply(double num1, double num2);
    		static _declspec(dllexport) double Devide(double num1, double num2);
    	};
    
    }
    #include "MathFuns.h"
    namespace MathFun
    {
    	MathFuns::MathFuns(void)
    	{
    	}
    
    
    	MathFuns::~MathFuns(void)
    	{
    	}
    
    	double MathFuns::Add(double num1, double num2)
    	{
    		return num1 + num2;
    	}
    
    	double MathFuns::Subtract(double num1, double num2)
    	{
    		return num1 + num2;
    	}
    
    	double MathFuns::Multiply(double num1, double num2)
    	{
    		return num1 * num2;
    	}
    
    	double MathFuns::Devide(double num1, double num2)
    	{
    		return num1 / num2;
    	}
    }


    运行生成成功:会生成 Dll.dll 和 Dll.lib(引入库,相当于头文件的作用)

    编写测试程序:可以把MathFuns.h考到现在工程的代码目录下,或者工程-属性-添加附加包含目录,把MathFuns.h所在目录添加上去。

    #include "stdafx.h"
    #include <iostream>
    #include "MathFuns.h"
    using namespace std;
    using namespace MathFun;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	double num1 = 0.0;
    	double num2 = 0.0;
    	cout << "请输入: ";
    	cin >> num1 >> num2;
    	cout << MathFuns::Add(num1, num2) << endl;
    	
    	cin.get();
    	cin.get();
    	return 0;
    }


    然后编译运行,会报错显示系统缺少Dll.dll链接库,可以把Dll.dll链接库所在的路径加入到环境变量中,或粘贴到window - system32 或system64的目录下

    最好放在现在现在的测试工程 生成的*.exe 目录下

    运行:



    成功了。

    vs自带的dumpbin工具 可以查看动态链接库,导出的函数,用vs下面带的命令打开,输入dumpbin直接有效,

    在cmd窗口中的其他目录想命令有效,需要 把D:Program Files (x86)vs2012VCin 目录下 vcvars32.bat文件拖到cmd窗口中,然后直接运行,不用删除两边“”,

    然后输入dumpbin 会出来提示


    然后切换到Dll.dll目录下:


    红圈中为导出函数:名字乱码是因为C++编译器为了实现重载,而按自己方式重新生成函数名字


    切换到测试程序中 输入已下命令查看,该exe导入的dll都有哪些以及有哪些函数



    新建一个控制台空项目-配置属性-配置类型选择(*.dll)

    添加-新建项-添加一个cpp文件,添加代码:

    _declspec(dllexport) double Add(double num1, double num2)
    {
    	return num1+ num2;
    }
    _declspec(dllexport) double Subtract(double num1, double num2)
    {
    	return num1 - num2;
    }


    生成(工程名为Dll2)Dll2.dll和Dll2.lib(引入库文件)


    添加测试工程,

    1, Dll2.lib 放在测试工程的代码目录下(或工程-属性-链接器常规-附加库包含目录中添加DLL2.lib所长目录)

    2, 工程-属性- 连接器-输入-附加依赖项中添加库的名字 Dll2.lib

    测试代码:

    #include "stdafx.h"
    #include <iostream>
    using namespace std;
    //extern double  Add(double num1, double num2);
    _declspec(dllimport) double  Add(double num1, double num2);
    int _tmain(int argc, _TCHAR* argv[])
    {
    	double num1 = 0.0;
    	double num2 = 0.0;
    	cout << "请输入: ";
    	cin >> num1 >> num2;
    	cout << Add(num1, num2) << endl;
    	
    	cin.get();
    	cin.get();
    	return 0;
    }
    


    extern和 _declspec(dllimport)都是可以的, 不过_declspec(dllimport)效率更高就是指定到 动态链接库中查找;

    成功生成*.exe程序,还要把Dll2.dll拷贝到该目录下

    运行:



    让test程序中不需要添加引用的函数声明的方法:

    修改Dll2.dll工程:

    添加头文件:添加预编译命令行

    #ifndef DLL_API
    #define DLL_API  _declspec(dllimport)
    #else
    
    #endif
    
    DLL_API double Add(double num1, double num2);
    
    DLL_API double Subtract(double num1, double num2);


    //#include ""函数一定要在

    #define DLL_API  _declspec(dllexport)

    下面 要不会出错


    #define DLL_API  _declspec(dllexport)
    #include "MathFunc.h"
    DLL_API double Add(double num1, double num2)
    {
    	return num1+ num2;
    }
    DLL_API double Subtract(double num1, double num2)
    {
    	return num1 - num2;
    }


    然后把MathFunc.h 和 dll2.dll拷贝到测试工程代码的目录下

    把dll2.dll放到生成的*.exe目录下:

    修改测试代码:

    #include "stdafx.h"
    #include "MathFunc.h"
    #include <iostream>
    using namespace std;
    //extern double  Add(double num1, double num2);
    //_declspec(dllimport) double  Add(double num1, double num2);
    int _tmain(int argc, _TCHAR* argv[])
    {
    	double num1 = 0.0;
    	double num2 = 0.0;
    	cout << "请输入: ";
    	cin >> num1 >> num2;
    	cout << Add(num1, num2) << endl;
    	
    	cin.get();
    	cin.get();
    	return 0;
    }


    运行成功:同上;

    动态链接库中导入类 class _declspec(dllexport) test{}, 类中也会有访问限制



    生成函数名不乱码的动态链接库的方法:

    #ifndef DLL_API
    #define DLL_API  extern "C" _declspec(dllimport)
    #else
    
    #endif
    
    DLL_API double Add(double num1, double num2);
    
    DLL_API double Subtract(double num1, double num2);
    #define DLL_API extern "C" _declspec(dllexport)
    #include "MathFunc.h"
    DLL_API double Add(double num1, double num2)
    {
    	return num1+ num2;
    }
    DLL_API double Subtract(double num1, double num2)
    {
    	return num1 - num2;
    }


    这样导出的*.dll的函数名字就不会改变函数名字,注: extern "C" 不能导出类的成员函数, 只能导出全局函数



    重新替换 MathFunc.h 和 dll2.lib 和 dll2.dll 就可运行。


    标准调用约定:_stdcall 

    extern "C" _declspec(dllexport) double _stdcall Subtract(double num1, double num2)
    {
    	return num1 - num2;
    }


    改变调用约定, extern "C" 导出的函数名也会变, 后面的数字16为参数的字节数大小,两个double为16


    模块定义文件方式改变动态链接库,导出函数名字改变的问题:

    *.def改变导出函数名字问题,这种方法不好用,有了解的同学可以告知一下。


    程序中动态的加载动态链接库:

    测试代码:

    #include "stdafx.h"
    //#include "MathFunc.h"
    #include <iostream>
    #include <windows.h> //句柄函数及加载动态链接库的头文件
    
    using namespace std;
    //extern double  Add(double num1, double num2);
    //_declspec(dllimport) double  Add(double num1, double num2);
    int _tmain(int argc, _TCHAR* argv[])
    {
    	double num1 = 0.0;
    	double num2 = 0.0;
    	cout << "请输入: ";
    	cin >> num1 >> num2;
    	//cout << Add(num1, num2) << endl;
    
    	HINSTANCE hinst;//声明window 句柄
    	//const char *p = "Dll2.dll";
    	hinst = LoadLibrary(L"Dll2.dll"); //获取动态链接库 句柄
    	
    	typedef double(*AddProc)(double a, double b);//定义函数指针 如果调用约定为标准调用约定 _stdcall  这个改为typedef double(_stdcall *AddProc)(double a, double b);
    
    	AddProc Add = (AddProc)GetProcAddress(hinst, "Add");//获取动态链接库中的Add函数,改函数名要严格等于dumpbin /exports dll2.dll的导出函数名
    	cout << Add(num1, num2) << endl;
    	cin.get();
    	cin.get();
    	return 0;
    }


    字符串前面加L"xxxx",  将ANSI字符串转换成unicode的字符串。

    表示转换成宽字符,就是每个字符占用两个字节。   

      例:strlen("asd")   =   3;   
      strlen(L"asd")   =   6;

    运行结果同上。

    动态的加载,好处很多,如果是静态引入库方式的加载动态链接库,这个链接库暂时没有用到也会加载进去,造成程序启动缓慢。

    exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是DllMainMSDN帮助文档。


    大概就这样了!







  • 相关阅读:
    点击按钮倒计时
    js实现-小框框全选
    CSS文字,文本,背景,盒模型等记录
    xps9560黑苹果展示
    面试——谈谈你对抽象和接口的理解(小知识大考点)
    谈谈你对Java 面向对象思想的理解
    谈谈你对Java 平台的理解
    HashMap1.7 问题总结
    2.3.2 InnoDB内存
    2.3 InnoDB 体系架构
  • 原文地址:https://www.cnblogs.com/riasky/p/3478625.html
Copyright © 2011-2022 走看看