转:DLL导出变量,函数,类

来源:互联网 发布:优雅的女生知乎 编辑:程序博客网 时间:2024/05/20 07:58


测试加载动态库DLL导出
#include <stdio.h>
#include "MyDLL.h"
int func(int a , int b)
{
 
 return(a + b);

    printf("Hello DLL \n");
}



MyDLL.h

#ifndef _MYDLL_
#define _MYDLL_

/*
#ifndef MYDLL_EXPORTS
#define  MYDLLEXPORT __declspec(dllexport)
#else
#define  MYDLLIMPORT __declspec(dllimport)
#endif
*/
#ifdef __cplusplus
extern "C" {
#endif
extern int __declspec(dllexport) func(int a , int b);

#ifdef __cplusplus
}
#endif

#endif

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

#include  <tchar>
#include <iosream>


using namespace std;
//typedef int(*InitSDK)();
/*
typedef int(*Ipfunc)(int,int);


int  _tmain(int argc, _TCHAR* argv[]){

  HINSTANCE hdll;
  hdll = LoadLibrary("C:\\Users\\molys\\Desktop\\TestCode\\TestC\\x64\\Debug\\TESTDLL3.dll");
  
 //if(NULL!=hdll){
Ipfunc funcFun = (Ipfunc)GetProcAddress(hdll,"func");//导出函数获取
if(hdll !=NULL){
  int result=funcFun(3,4);//InitFaceSDKFun(); 
  printf("%s",result);
  
}

   FreeLibrary(hdll);//释放动态链接库

  return 0;
 }
 */






导出变量

  DLL定义的全局变量可以被调用进程访问;DLL也可以访问调用进程的全局数据,我们来看看在应用工程中引用DLL中变量的例子。

 

/* 文件名:lib.h */
#ifndef LIB_H
#define LIB_H
extern int dllGlobalVar;    //导出的变量声明
#endif


/* 文件名:lib.cpp */
#include "lib.h"
#include <windows.h>

int dllGlobalVar;

BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
  switch (ul_reason_for_call)
  {
     case DLL_PROCESS_ATTACH:
       dllGlobalVar = 100;       //在dll被加载时,赋全局变量为100
       break;

     case DLL_THREAD_ATTACH:
     case DLL_THREAD_DETACH:
     case DLL_PROCESS_DETACH:
     break;
  }
      return TRUE;
}


;文件名:lib.def
;在DLL中导出变量

LIBRARY "dllTest"

EXPORTS
dllGlobalVar CONSTANT
;或dllGlobalVar DATA
GetGlobalVar


  从lib.h和lib.cpp中可以看出,全局变量在DLL中的定义和使用方法与一般的程序设计是一样的。若要导出某全局变量,我们需要在.def文件的EXPORTS后添加:

      变量名 CONSTANT   //过时的方法
       或
      变量名 DATA     //VC++提示的新方法


     在主函数中引用DLL中定义的全局变量:
#include <stdio.h>
#pragma comment(lib,"dllTest.lib")

extern int dllGlobalVar;

int main(int argc, char *argv[])
{
       printf("%d ", *(int*)dllGlobalVar);
       *(int*)dllGlobalVar = 1;
       printf("%d ", *(int*)dllGlobalVar);

       return 0;
}
  特别要注意的是用extern int dllGlobalVar声明所导入的并不是DLL中全局变量本身,而是其地址,应用程序必须通过强制指针转换来使用DLL中的全局变量。这一点,从*(int*)dllGlobalVar可以看出。因此在采用这种方式引用DLL全局变量时,千万不要进行这样的赋值操作:

dllGlobalVar = 1;

  其结果是dllGlobalVar指针的内容发生变化,程序中以后再也引用不到DLL中的全局变量了。

  在应用工程中引用DLL中全局变量的一个更好方法是:

#include <stdio.h>
#pragma comment(lib,"dllTest.lib")

extern int _declspec(dllimport) dllGlobalVar; //用_declspec(dllimport)导入
int main(int argc, char *argv[])
{
       printf("%d ", dllGlobalVar);
       dllGlobalVar = 1; //这里就可以直接使用, 无须进行强制指针转换
        printf("%d ", dllGlobalVar);

        return 0;

}


  通过_declspec(dllimport)方式导入的就是DLL中全局变量本身而不再是其地址了,笔者建议在一切可能的情况下都使用这种方式。


导出函数

  DLL中导出函数的声明有两种方式:
一种为给出的在函数声明中加上__declspec(dllexport),这里不再举例说明;
另外一种方式是采用模块定义(.def) 文件声明,.def文件为链接器提供了有关被链接程序的导出、属性及其他方面的信息。

  下面的代码演示了怎样同.def文件将函数add声明为DLL导出函数:

; lib.def : 导出DLL函数

LIBRARY dllTest

EXPORTS
add @1


.def文件的规则为:

  (1)LIBRARY语句说明.def文件相应的DLL;

  (2)EXPORTS语句后列出要导出函数的名称。可以在.def文件中的导出函数名后加@n,表示要导出函数的序号为n(在进行函数调用时,这个序号将发挥其作用);
  注:函数名与@n之间一定要留有一个空格" ",不然编译时会报错,
  而@与n符号之间对此没有限制。

  (3).def 文件中的注释由每个注释行开始处的分号 (;) 指定,且注释不能与语句共享一行。

  由此可以看出,例子中lib.def文件的含义为生成名为“dllTest”的动态链接库,导出其中的add函数,并指定add函数的序号为1。


导出类

  DLL中定义的类可以在应用工程中使用。
  下面的例子里,我们在DLL中定义了point和circle两个类,并在应用工程中引用了它们。

//文件名:point.h,point类的声明
#ifndef POINT_H
#define POINT_H
#ifdef DLL_FILE

class _declspec(dllexport) point //导出类point
#else
class _declspec(dllimport) point //导入类point

#endif
{
public:
float y;
float x;
point();
point(float x_coordinate, float y_coordinate);
};
#endif


//文件名:point.cpp,point类的实现
#ifndef DLL_FILE
#define DLL_FILE
#endif
#include "point.h"
//类point的缺省构造函数
point::point()
{
x = 0.0;
y = 0.0;
}

//类point的构造函数
point::point(float x_coordinate, float y_coordinate)
{
x = x_coordinate;
y = y_coordinate;
}


//文件名:circle.h,circle类的声明
#ifndef CIRCLE_H
#define CIRCLE_H
#include "point.h"

#ifdef DLL_FILE
class _declspec(dllexport)circle //导出类circle
#else
class _declspec(dllimport)circle //导入类circle

#endif
{
public:
void SetCentre(const point &rePoint);
void SetRadius(float r);
float GetGirth();
float GetArea();
circle();

private:
float radius;
point centre;
};
#endif


//文件名:circle.cpp,circle类的实现
#ifndef DLL_FILE
#define DLL_FILE
#endif
#include "circle.h"
#define PI 3.1415926
//circle类的构造函数

circle::circle()
{
centre = point(0, 0);
radius = 0;
}

//得到圆的面积
float circle::GetArea()
{
return PI *radius * radius;
}

//得到圆的周长
float circle::GetGirth()
{
return 2 *PI * radius;
}

//设置圆心坐标
void circle::SetCentre(const point &rePoint)
{
centre = centrePoint;
}

//设置圆的半径
void circle::SetRadius(float r)
{
radius = r;
}


类的引用:
#include "../circle.h"  //包含类声明头文件

#pragma comment(lib,"dllTest.lib");
int main(int argc, char *argv[])
{
circle c;
point p(2.0, 2.0);
c.SetCentre(p);
c.SetRadius(1.0);
printf("area:%f girth:%f", c.GetArea(), c.GetGirth());
return 0;
}
  从上述源代码可以看出,由于在DLL的类实现代码中定义了宏DLL_FILE,故在DLL的实现中所包含的类声明实际上为:

class _declspec(dllexport) point         //导出类point
{

}
  和
class _declspec(dllexport) circle        //导出类circle
{

}
  而在应用工程中没有定义DLL_FILE,故其包含point.h和circle.h后引入的类声明为:
class _declspec(dllimport) point //导入类point
{

}
  和
class _declspec(dllimport) circle //导入类circle
{

}

不错,正是通过DLL中的

class _declspec(dllexport) class_name //导出类circle
{

}
  与应用程序中的
class _declspec(dllimport) class_name //导入类
{

}


  匹对来完成类的导出和导入的!

  我们往往通过在类的声明头文件中用一个宏来决定使其编译为class _declspec(dllexport) class_name还是class _declspec(dllimport) class_name版本,这样就不再需要两个头文件。本程序中使用的是:

#ifdef DLL_FILE
class _declspec(dllexport) class_name //导出类
#else
class _declspec(dllimport) class_name //导入类
#endif


  实际上,在MFC DLL的讲解中,您将看到比这更简便的方法,而此处仅仅是为了说明_declspec(dllexport)与_declspec(dllimport)匹对的问题。

       >>>  End  <<<

0 0