vs dll 使用与explore

来源:互联网 发布:乐高42056淘宝 编辑:程序博客网 时间:2024/06/09 19:54

又弄了一下dll,好久没弄都快忘光了


制作dll

简单的dll不难:

(1)记得在头文件暴露 

__declspec(dllexport)
(2)还有在cpp文件中一定要引用这个头文件,否则无法生成暴露的接口


探究 __declspec(dllexport) 函数与普通函数签名是否一样

//Hello_Ma.h//int Hello_Ma();#include<stdio.h>extern "C" {__declspec(dllexport) int Hello_Ma(); //export dll/*__declspec(dllexport) int Hello_Wang() {printf("Hello Wang");return 0;}*/}


//Hello_Ma.cpp#include <iostream>#include "Hello_Ma.h"using namespace std;int Hello_Ma() {cout << "Hello, this is Ma1" << endl;return 0;}


//Main.cpp#include<stdio.h>int Hello_Ma() {printf("Hello Ma2\n");return 0;}int main() {Hello_Ma();return 0;}

能够编译通过,且打印Hello_Ma2,说明这两个不是同一个函数


以下内容来自:http://www.cnblogs.com/houkai/archive/2013/06/05/3119513.html


目录

1.dll的优点

代码复用是提高软件开发效率的重要途径。一般而言,只要某部分代码具有通用性,就可将它构造成相对独立的功能模块并在之后的项目中重复使用。比较常见的例子是各种应用程序框架,ATL、MFC等,它们都以源代码的形式发布。由于这种复用是“源码级别”的,源代码完全暴露给了程序员,因而称之为“白盒复用”。“白盒复用”的缺点比较多,总结起来有4点。 
暴露了源代码;多份拷贝,造成存储浪费; 
容易与程序员的“普通”代码发生命名冲突; 
更新功能模块比较困难,不利于问题的模块化实现; 
实际上,以上4点概括起来就是“暴露的源代码”造成“代码严重耦合”。为了弥补这些不足,就提出了“二进制级别”的代码复用。使用二进制级别的代码复用一定程度上隐藏了源代码,对于缓解代码耦合现象起到了一定的作用。这样的复用被称为“黑盒复用”。 
说明:实现“黑盒复用”的途径不只dll一种,静态链接库甚至更高级的COM组件都是。

2.dll的创建

参考程序原文:http://msdn.microsoft.com/zh-cn/library/ms235636.aspx 
新建“Win32项目”,选择应用程序类型为"DLL”,其他默认。添加头文件testdll.h

复制代码
//testdll.h#ifdef TESTDLL_EXPORTS  #define TESTDLL_API __declspec(dllexport)   #else  #define TESTDLL_API __declspec(dllimport)   #endif  namespace MathFuncs  {      // This class is exported from the testdll.dll      class MyMathFuncs      {      public:           // Returns a + b          static TESTDLL_API double Add(double a, double b);        // Returns a - b          static TESTDLL_API double Subtract(double a, double b);        // Returns a * b          static TESTDLL_API double Multiply(double a, double b);        // Returns a / b          // Throws const std::invalid_argument& if b is 0          static TESTDLL_API double Divide(double a, double b);    };  }
复制代码

当定义了符号TESTDLL_EXPORTS,TESTDLL_API被设置为 __declspec(dllexport) 修饰符,This modifier enables the function to be exported by the DLL so that it can be used by other applications。若未定义则TESTDLL_API被设置为__declspec(dllimport),This modifier enables the compiler to optimize the importing of the function from the DLL for use in other applications。当DLL项目生成时,TESTDLL_EXPORTS默认是定义的,所以默认设置的是__declspec(dllexport) 修饰符。 
添加cpp文件

复制代码
// testdll.cpp : 定义 DLL 应用程序的导出函数。#include "stdafx.h"#include "testdll.h"  #include <stdexcept>  using namespace std;  namespace MathFuncs  {      double MyMathFuncs::Add(double a, double b)      {          return a + b;      }      double MyMathFuncs::Subtract(double a, double b)      {          return a - b;      }      double MyMathFuncs::Multiply(double a, double b)      {          return a * b;      }      double MyMathFuncs::Divide(double a, double b)      {          if (b == 0)          {              throw invalid_argument("b cannot be zero!");          }          return a / b;      }  }
复制代码

编译就会生成对应的dll文件,同时也会生成对应的lib文件。 
注意:a.DLL中导出函数的声明有两种方式:在函数声明中加上__declspec(dllexport);采用模块定义(.def)文件声明。详见:http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792099.html 
b.对于C文件创建dll时或者想使用C编译器创建dll时,建议使用 extern “C” 标志,参见extern "C"的简单解析

3.dll的调用

应用程序使用DLL可以采用两种方式:一种是隐式链接(调用),另一种是显式链接。在使用DLL之前首先要知道DLL中函数的结构信息。VS在VC\bin目录下提供了一个名为Dumpbin.exe的小程序,用它可以查看DLL文件中的函数结构。两种的对比详见:http://blog.sina.com.cn/s/blog_53004b4901009h3b.html 
隐式链接采用静态加载的方式,比较简单,需要.h、.lib、.dll三件套。新建“控制台应用程序”或“空项目”。配置如下: 
项目->属性->配置属性->VC++ 目录-> 在“包含目录”里添加头文件testdll.h所在的目录 
项目->属性->配置属性->VC++ 目录-> 在“库目录”里添加头文件testdll.lib所在的目录 
项目->属性->配置属性->链接器->输入-> 在“附加依赖项”里添加“testdll.lib”(若有多个 lib 则以空格隔开) 
添加cpp文件

复制代码
//mydll.cpp#include <iostream>  #include "testdll.h"  using namespace std;  int main()  {      double a = 7.4;      int b = 99;      cout << "a + b = " <<          MathFuncs::MyMathFuncs::Add(a, b) << endl;      cout << "a - b = " <<          MathFuncs::MyMathFuncs::Subtract(a, b) << endl;      cout << "a * b = " <<          MathFuncs::MyMathFuncs::Multiply(a, b) << endl;      cout << "a / b = " <<          MathFuncs::MyMathFuncs::Divide(a, b) << endl;      try      {          cout << "a / 0 = " <<              MathFuncs::MyMathFuncs::Divide(a, 0) << endl;       }      catch (const invalid_argument &e)       {          cout << "Caught exception: " << e.what() << endl;       }      return 0;  }
复制代码

现在可以编译通过了,但是程序运行就报错,还需要将testdll.dll复制到当前项目生成的可执行文件所在的目录。 
显式链接是应用程序在执行过程中随时可以加载DLL文件,也可以随时卸载DLL文件,这是隐式链接所无法作到的,所以显式链接具有更好的灵活性,对于解释性语言更为合适。 
新建项目,不需要特殊配置,添加cpp文件

复制代码
/* *作者:侯凯 *说明:显式调用DLL *日期:2013-6-5*/#include<Windows.h> //加载的头文件#include<iostream>using namespace std;int main()  {      typedef double (*pAdd)(double a, double b);    typedef double (*pSubtract)(double a, double b);     HMODULE hDLL = LoadLibrary("testdll.dll"); //加载dll文件     if(hDLL != NULL)      {          pAdd fp1 = pAdd(GetProcAddress(hDLL, MAKEINTRESOURCE(1))); //得到dll中的第一个函数        if(fp1 != NULL)          {               cout<<fp1(2.5, 5.5)<<endl;         }          else          {              cout<<"Cannot Find Function "<<"add"<<endl;          }          pSubtract fp2 = pSubtract(GetProcAddress(hDLL, "?Subtract@MyMathFuncs@MathFuncs@@SANNN@Z")); //得到dll中标示为"?..."的函数,C++编译器考虑了函数的参数        if(fp2 != NULL)          {              cout<<fp2(5.5, 2.5)<<endl;          }          else          {              cout<<"Cannot Find Function "<<"Subtract"<<endl;          }          FreeLibrary(hDLL);      }      else      {          std::cout<<"Cannot Find "<<"testdll"<<std::endl;      }      return 1;  }
复制代码

显式调用的问题:在DLL文件中,dll工程中函数名称在编译生成DLL的过程中发生了变化(C++编译器),在DLL文件中称变化后的字符为“name标示”。GetProcAddress中第二个参数可以由DLL文件中函数的顺序获得,或者直接使用DLL文件中的”name标示”,这个标示可以通过Dumpbin.exe小程序查看。如果C++编译器下,想让函数名更规范(和原来工程中一样),具体方法详见:http://blog.csdn.net/btwsmile/article/details/6676802。 
当然,为了让函数名更规范,最常用的方式是:创建dll过程中使用C编译器来编译函数,这样DLL文件中的函数名和原dll工程中的函数名就一致了。

4.更一般的显式调用

为了解决上部分最后的问题,可以使用 extern “C” 为dll工程中的函数建立C连接,简单的示例工程如下。 
在DLL创建的工程中,添加cpp文件

复制代码
/* *作者:侯凯 *说明:创建dll,使用C接口——C编译器生成的dll中函数的"name标示"仍为addfun *日期:2013-6-5*/// cdll.cpp : 定义 DLL 应用程序的导出函数。//#include "stdafx.h"#ifdef __cplusplus         // if used by C++ codeextern "C" {                  // we need to export the C interface#endif__declspec(dllexport) int addfun(int a, int b){        return a+b;}#ifdef __cplusplus}#endif
复制代码

编译即可生成DLL文件。在dll调用工程中,添加cpp文件

复制代码
/* *作者:侯凯 *说明:显式调用dll *日期:2013-6-5*/#include <windows.h>#include <iostream>using namespace std;void main(){    typedef int(*FUNA)(int,int);    HMODULE hMod = LoadLibrary("cdll.dll");//dll路径    if (hMod)    {        FUNA addfun = (FUNA)GetProcAddress(hMod, TEXT("addfun"));//直接使用原工程函数名         if (addfun != NULL)        {            cout<<addfun(5, 4)<<endl;        }        else        {            cout<<"ERROR on GetProcAddress"<<endl;        }        FreeLibrary(hMod);    }    else        cout<<"ERROR on LoadLibrary"<<endl;}
复制代码

运行,这样便可以调用dll的函数了。再进一步,上述dll文件如果通过隐式调用,利用.dll、.lib文件,调用函数应为

复制代码
//隐式链接#include <iostream>#pragma comment(lib,"cdll.lib")using namespace std;extern "C" _declspec(dllimport) int addfun(int a,int b);//载入addfun函数,这里起到了.h文件的作用//dll中使用C编译器 故这里需要extern "C" 如果dll中无extern "C"//此处为:_declspec(dllimport) int addfun(int a,int b);void main(){    cout<<addfun(5,4)<<endl;}
复制代码

原创粉丝点击