现在的位置: 首页 > 综合 > 正文

delete this,dll导出类及如何写plugins

2013年10月28日 ⁄ 综合 ⁄ 共 4923字 ⁄ 字号 评论关闭

 delete this,dll导出类及如何写plugins

1.为什么要使用delete this,以及如何使用。
(1)考查这样的情况:
有两个对象A,B需要访问同一个多态对象C。
因为C一般是通过new 操作构造的,一定要自己释放,但是对象A,B都需要使用它,并且B不知道A什么时候使用完成C,A也不知道B什么时候使用完成C(当然可以用函数通信的方法通知了,不过是一个比较丑陋的实现方法),所以不能在A/B中间来delete,
一个折中的办法是在程序退出的时候来delete,但是这样做不到资源的立即释放,假如有多个A/B/C会有比较大的运行开销。
解决的办法就是在C中增加引用计数,并且自己决定什么时候来释放自己:

下面是代码

struct c{
C()
{nRef=1;}
;
int AddRef()
{
return ++
nRef;
}

int Release(){
if(!--
nRef)
{
 delete 
this
;
 
return 0
;
}

return nRef;
}

protected:
virtual~c()//donot allow delete from outsides

{
}

int nRef;
}
;

就是通过C内部的计数来判断什么时候释放自己的资源。当然需要A/B在适当时候调用AddRef/Release了。
下面是一个简单的实现。

struct a{
a(c
*
 pc)
{
m_pc
=
pc;
pc
->
AddRef();
}

void do_Sth(){
;
//...使用完成m_pc了

m_pc->Release();
m_pv
=
NULL;
}

protected:
c
*
 m_pc;
}

class b的结构和a也差不多,就不写了
下面是main函数:
void
 main()
{
c
* pc=new
 c;
a clsA(pc);
b clsB(pc);
pc
->Release();//ok pc was hold by both clsA and clsB,so we don't need it.release.

pc=NULL;
a.do_sth();
b.do_sth();
;
//should no memory leak 

}

注意事项:
因为是在类成员函数里面delete this的,所以在此语句以后,不能访问任何的成员变量及虚函数,否则一定非法。

2.dll导出类的几种方法。
其实我就知道两种啦,还请大家补充。
先看M$使用的方法。
struct exports{
__declspec(dllexport)exports();
__declspec(dllexport)~exports();
};
exports::exports(){}
exports::~exports(){}
优点:完全和本地的类一样使用,可以直接new/delete.
缺点:只能使用自动连接(就是连lib的),自由度不高。
不能通过选择dll的方式来转换插件(可以通过替换dll文件实现,但是不能保证对所有的修改后的类通用)
继承exports后没有办法通过同一个接口调用子类函数。(因为你永远new的都是父类,除非重新编译原来的main程序)

另外一个方法:
这样声明exports类:
;//exports.h
struct exports{
friend exports* CreateExports(LPVOID lpparm);
virtual int AddRef();
virtual int Release();//前面讲过的方法,如果不清楚请看第一篇delete this
virtual do_sth()=0;//所有成员函数均为虚函数,基类可以纯虚。
protected:
exports(){}
virtual ~exports(){}
}
extern "C" CreateExports函数是dll的输出函数。
编译exports.cpp成exports.dll
然后在main.cpp:

#include "windows.h"
#include 
"exports.h"
void main()
{
LoadLibrary(
"exports"
);....
PCreateExports
=GetProcAddress("CreateExports"
....);...
exports
* pex=
pCreateExports(NULL);
pex
->
do_sth();
pex
->
Release();
}

 

Q:为什么要使用protected的构造函数。
A:因为我不希望exports类被直接使用(这样就没有办法实现多态了,因为new不到子类),所以protected它的构造函数。
这样在程序里面你只能通过CreateExports来得到Exports类的指针。而CreateExports返回的类是通过lpparm来确定的,这样就实现了多态性。

Q:为什么要使用delete this 的方法(AddRef/Release)。
A:因为exports类是从dll里面new出来的,所以一定需要在dll里面delete,所以使用这个方法,否则会内存泄漏。

Q:为什么所有成员函数均为虚函数。
A:如果不是虚函数,其实再连接的时候成员函数是会连接代码到.exe里面去的,这样就根本call不到dll里面的函数,也就没有实现我们的原意。
而虚函数是通过函数指针调用的,exports类是再dll里面构造,所以可以保证虚函数是call到dll里面,而且.exe只需要dll的头文件即可编译连接通过。

3.实现plugins。
有了前两章的基础,这个就比较好办了。
先了解一点点基本概念:
Q:what is plugins
A:就是通过加载不同的dll实现不同的功能,或者附加的功能,典型的如模拟器的各种插件(显示,input...)。
Q:why we use plugins
A:主要为了使程序模块化,并且容易扩充。升级组件时不需要重新发布整个产品。
Q:how to do it?
A:使用LoadLibrary来加载具有相同接口(一般是函数,不过我们这里会使用C++类实现)的不同dll,实现/(扩充)功能。
例子:
D:/ePsxe/plugins>dumpbin spuseal.dll /exports
Microsoft (R) COFF Binary File Dumper Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.

Dump of file spuseal.dll
File Type: DLL
 Section contains the following exports for spuSeal.dll
      0 characteristics
  36A22A22 time date stamp Sun Jan 17 10:21:22 1999
    0.00 version
      2 ordinal base
     22 number of functions
     22 number of names
  ordinal hint RVA    name
      3  0 00001700 PSEgetLibName
      2  1 00001710 PSEgetLibType
      4  2 00001720 PSEgetLibVersion
      6  3 00001800 SPUabout
     11  4 00001EE0 SPUclose
      5  5 00001730 SPUconfigure
     18  6 00002090 SPUgetOne
      7  7 00001910 SPUinit
     10  8 00001B90 SPUopen
     23  9 00002270 SPUplayADPCMchannel
     12  A 00002250 SPUplaySample
     17  B 00002050 SPUputOne
     19  C 000020B0 SPUsetAddr
     20  D 000020E0 SPUsetPitch
     21  E 00002110 SPUsetVolumeL
     22  F 00002150 SPUsetVolumeR
      8  10 00001B80 SPUshutdown
     13  11 00002190 SPUstartChannels1
     14  12 000021C0 SPUstartChannels2
     15  13 000021F0 SPUstopChannels1
     16  14 00002220 SPUstopChannels2
      9  15 00001880 SPUtest
 Summary
    1000 .rsrc
    96000 UPX0
    5000 UPX1
D:/ePsxe/plugins>

这个是epsxe的一个sound插件的输出表。可以看到epsxe就是通过这些函数与dll通信的。
进入正题:
从一个dll里面输出这么多函数实在是一个很烦的事情,而且一个个的去GetProcAddr也很麻烦,
能不能用简单一些的方法呢?
当然是可以的了,使用我们前面介绍的dll输出类技术2就可以了。
还是看代码,我们做一个input的plugins类。

;//input.h
class input{
virtual int  Addref()=0
;
virtual int  Release()=0
;
virtual DWORD  QueryClsID()=0;//identify this is a input plugins;

virtual DWORD  IsSupport(DWORD flags)=0;
virtual bool  GetXY(int&x,int&y)=0
;
virtual DWORD  GetButtonStat()=0
;
virtual bool AddEffect(DWORD id,DWORD level)=0
;
virtual bool QueryInterface(DWORD clsid,LPVOID* ppVoid);//留待扩充。越来越像COM了。

virtual bool DoConfig();
protected
:
input();
virtual ~
input();
}
;
--------------------------------------------------------

;
//mouseinput.h
class MouseInput
 :
public
 input
{
protected
:
friend input
*
 CreateInput(LPVOID pVoid);
;
//...............

}
;
--------------------------------------------------------

;
//mouseinput.cpp
input* CreateInput(LPVOID pVoid){return new mouseinput;};
 
--------------------------------------------------------

;
//keyinput.h
class KeyInput
 :
public
 input
{
protected
:
friend input
*
 CreateInput(LPVOID pVoid);
;
//...............

}
;
--------------------------------------------------------

;
//keyinput.cpp
input* CreateInput(LPVOID pVoid){return new keyinput;};

 
编译后就得到了mouseinput.dll和keyinput.dll
他们分别输出mouseinput类和keyinput类,而外部程序是通过input的接口来调用他们,从而实现了plugins的功能。
 
 
不知道还有没有什么遗漏啊,请大家指教了......
 
多提意见:)
 

抱歉!评论已关闭.