dll入门-类的导出和数据共享

来源:互联网 发布:c 语言中cost 编辑:程序博客网 时间:2024/06/04 00:44

dll入门-类的导出和数据共享  

2012-02-29 12:36:32|  分类: 动态链接库知识收|举报|字号 订阅

前面介绍了怎么从DLL中导出函数和变量,实际上导出类的方法也是大同小异,废话就不多说了,下面给个简单例子示范一下,也就不多做解释了。

DLL头文件:
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#ifndef _DLL_SAMPLE_H
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#define _DLL_SAMPLE_H
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
// 通过宏来控制是导入还是导出
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#ifdef _DLL_SAMPLE
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#define DLL_SAMPLE_API __declspec(dllexport)
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#else
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#define DLL_SAMPLE_API __declspec(dllimport)
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#endif
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
// 导出/导入变量声明
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
DLL_SAMPLE_API class DLLClass
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
{
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽  
public:
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽    
void Show();
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽}
;
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#undef DLL_SAMPLE_API
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#endif
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽

DLL实现文件:
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#include "stdafx.h"
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#define _DLL_SAMPLE
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#ifndef _DLL_SAMPLE_H
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#include 
"DLLSample.h"
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#endif
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#include 
"stdio.h"
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
//APIENTRY声明DLL函数入口点
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
{
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽 
switch (ul_reason_for_call)
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽 
{
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽  
case DLL_PROCESS_ATTACH:
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽  
case DLL_THREAD_ATTACH:
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽  
case DLL_THREAD_DETACH:
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽  
case DLL_PROCESS_DETACH:
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽   
break;
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽 }

dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽 
return TRUE;
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽}

dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
void DLLClass::Show()
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
{
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽  printf(
"DLLClass show!");
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽}

应用程序调用DLL
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#include "DLLSample.h"
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#pragma comment(lib,
"DLLSample.lib")
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
int main(int argc, char *argv[])
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
{
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽 DLLClass dc;
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽  dc.Show();
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽 
return 0;
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽}

dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽


大家可能发现了,上面我没有使用模块定义文件(.def)声明导出类也没有用显式链接导入DLL。 
用Depends查看前面编译出来的DLL文件,会发现里面导出了很奇怪的symbol,这是因为C++编译器在编译时会对symbol进行修饰。
这是我从别人那儿转来的截图。



网上找了下,发现了C++编译时函数名的修饰约定规则

__stdcall调用约定:

1、以"?"标识函数名的开始,后跟函数名;
2、函数名后面以"@@YG"标识参数表的开始,后跟参数表;
3、参数表以代号表示:

X——void,
D——char,
E——unsigned char,
F——short,
H——int,
I——unsigned int,
J——long,
K——unsigned long,
M——float,
N——double,
_N——bool,
....

  PA——表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
4、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前; 
5、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。
  其格式为"?functionname@@YG*****@Z"或?functionname@@YG*XZ,

    int Test1(char *var1,unsigned long)-----“?Test1@@YGHPADK@Z”       void Test2()                          -----“?Test2@@YGXXZ”  

__cdecl调用约定:
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。

__fastcall调用约定:
  规则同上面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YI"。

VC++对函数的省缺声明是"__cedcl",将只能被C/C++调用。

虽然因为C++编译器对symbol进行修饰的原因不能直接用def文件声明导出类和显式链接,但是可以用另外一种取巧的方式。

在头文件中类的声明中添加一个友元函数:
friend DLLClass* CreatDLLClass();
然后声明CreatDLLClass()为导出函数,通过调用该函数返回一个DLLClass类的对象,同样达到了导出类的目的。
这样,就可以用显式链接来调用CreatDLLClass(),从而得到类对象了。

DLL入门浅析(5)——使用DLL在进程间共享数据

         在Win16环境中,DLL的全局数据对每个载入它的进程来说都是相同的,因为所有的进程用的都收同一块地址空间;而在Win32环境中,情况却发生了变化,每个进程都有了它自己的地址空间,DLL函数中的代码所创建的任何对象(包括变量)都归调用它的进程所有。当进程在载入DLL时,操作系统自动把DLL地址映射到该进程的私有空间,也就是进程的虚拟地址空间,而且也复制该DLL的全局数据的一份拷贝到该进程空间。(在物理内存中,多进程载入DLL时,DLL的代码段实际上是只加载了一次,只是将物理地址映射到了各个调用它的进程的虚拟地址空间中,而全局数据会在每个进程都分别加载)。也就是说每个进程所拥有的相同的DLL的全局数据,它们的名称相同,但其值却并不一定是相同的,而且是互不干涉的。
因此,在Win32环境下要想在多个进程中共享数据,就必须进行必要的设置。在访问同一个Dll的各进程之间共享存储器是通过存储器映射文件技术实现的。也可以把这些需要共享的数据分离出来,放置在一个独立的数据段里,并把该段的属性设置为共享。必须给这些变量赋初值,否则编译器会把没有赋初始值的变量放在一个叫未被初始化的数据段中。

在DLL的实现文件中添加下列代码:

dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽#pragma data_seg("DLLSharedSection")      // 声明共享数据段,并命名该数据段
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
   int SharedData = 123;       // 必须在定义的同时进行初始化!!!!
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽
#pragma data_seg()

 

 在#pragma data_seg("DLLSharedSection")和#pragma data_seg()之间的所有变量将被访问该Dll的所有进程看到和共享。仅定义一个数据段还不能达到共享数据的目的,还要告诉编译器该段的属性,有三种方法可以实现该目的(其效果是相同的),一种方法是在.DEF文件中加入如下语句:

dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽SETCTIONS
dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽    DLLSharedSection READ WRITE SHARED

 

另一种方法是在项目设置的链接选项(Project Setting --〉Link)中加入如下语句:

dll入门-类的导出和数据共享 - 雪灵 - 雪灵之紫羽/SECTION:DLLSharedSection,rws

 

还有一种就是使用指令:

#pragma comment(linker,"/section:.DLLSharedSection,rws")


那么这个数据节中的数据可以在所有DLL的实例之间共享了。所有对这些数据的操作都针对同一个实例的,而不是在每个进程的地址空间中都有一份。
 
当进程隐式或显式调用一个动态库里的函数时,系统都要把这个动态库映射到这个进程的虚拟地址空间里。这使得DLL成为进程的一部分,以这个进程的身份执行,使用这个进程的堆栈。

下面来谈一下在具体使用共享数据段时需要注意的一些问题:

·         所有在共享数据段中的变量,只有在数据段中经过了初始化之后,才会是进程间共享的。如果没有初始化,那么进程间访问该变量则是未定义的。
·         所有的共享变量都要放置在共享数据段中。如何定义很大的数组,那么也会导致很大的DLL。
·         不要在共享数据段中存放进程相关的信息。Win32中大多数的数据结构和值(比如HANDLE)只在特定的进程上下文中才是有效地。
·         每个进程都有它自己的地址空间。因此不要在共享数据段中共享指针,指针指向的地址在不同的地址空间中是不一样的。
·         DLL在每个进程中是被映射在不同的虚拟地址空间中的,因此函数指针也是不安全的。

当然还有其它的方法来进行进程间的数据共享,比如文件内存映射等,这就涉及到通用的进程间通信了,这里就不多讲了。

文章来源:http://www.cppblog.com/suiaiguo/archive/2009/07/21/90734.aspx

0 0
原创粉丝点击