浅谈win32的dll中导出类的方法

来源:互联网 发布:lol我的数据查询 编辑:程序博客网 时间:2024/05/02 04:50
  

昨天,朋友再写一个控件给客户使用,问我一个问题: 怎么在动态链接库中导出类?(不使用MFC,因为感觉那个东西实在很烦人!)呵呵,说实在,我还没有这方面的经验。以前给客户用的控件一般都是做一堆函数在控件之中,然后进行“封装”起来,再扔给客户使用。当然很顺利,只要加一个.def文件,任何语言皆可以调用你的动态库,这样有什么不好吗?当然,我并不认为它有什么不好之处,而且,也能绕过“class”这个机制,让自己编译出来的代码量会更小,更精致。是的,这一切看起来都很好,但是,如果在你控件之中有很多函数供用户使用(想一想DirectX)——也就是说你的一个控件有很多个功能块,并且,这些功能块可以按着一定形式划分,每一个功能块都有一些函式供外界使用,而外部调用的时候只需要用 功能块->函数 调用,好的,来点例子:比如你有两个功能块分别是飞机(Airplane)和 汽车(Car),这两个功能块都要向外界提供一个启动引擎的方法(Startup),但是,这个方法在Airplane Car中所作的动作肯定是不一样的,但是为了让你的调用更加自然,呵呵,面向对象!是的,这一点我们不用再讨论了,有时,我们的确需要那样做。

让我们回到主题,怎样将控件中类的方法供给别人,呵呵,我很惯性思维,首先想到将类的方法加到.def 之中。是的,没有问题:看看下面的代码:

Airplane.h

 

Class Airplane

{

Public:

    int Startup(void *list);

    int Stop ();

   

Private:

   //……

};

 

然后,我们开始实现类在我们另一个Airplane.cpp

Airplane.cpp

。。

Int Airplane:: Startup(void *list){//};

Int Airplane:: Sop//(){};

。。。

是的,我们不得不给外部增加一个方法,也就是说要导出我们的类

于是,我们在Airplane.h 中增加一个方法声明

Airplane.h

extern "C" __declspec(dllexport)  HANDLE CreateClass ();

extern "C" __declspec(dllexport) int ReleaseClass(HANDLE h_class);

 

实现它:

Airplane.cpp

 

HANDLE CreateClass ()

{

     Airplane *tmp =NULL;

     tmp = new Airplane;

     return HANDLE(tmp)

}

 

int ReleaseClass(HANDLE h_class)

{

    If(!h_class) return 0;

    delete h_class;

return 1;

}

 

别忘了,在.def 文件中加入导出的方法名如下:

Exports

    CreateClass   @ 1

    ReleaseClass  @ 2

    Startup       @ 3

    Stop         @ 4

…..

 

是的,好像没有问题,一切okay! 且慢,如果,我还打算在上述控件中再加入另一个类Car ,呵呵,而且,在Car 之中还有一个Startup Stop 的方法,糟糕!我该怎样定义我的.def文件?事情看起来陷入了僵局。Com, 我突然想起来了com,(其实我对com知之甚少,只是略有了解),directx之类的东西提供给外部通常是接口,也就是说我们通常获得的是接口,然后,再通过接口去调用,是的,没错!比如,我们总是这样:

 

DirectDrawCreateEx(NULL,(void**)&lpdd,IID_IDirectDraw7,NULL)

 

获取到lpdd 接口,然后,再调用这个接口的一些方法。看来,事情有了转机,让我们看看下面的代码:

Engine.h

 

 

__interface IAirplaneEng

{

  Virtual int _stdcall  Startup(void *list) =0;

virtual int _stdcall  Stop () =0;

virtual int _stdcall  Realse ()=0;

};

 

__interface ICarEng

{

  Virtual int _stdcall  Startup(void *list) =0;

virtual int _stdcall  Stop () =0;

virtual int _stdcall  Realse ()=0;

};

 

Class CAirplane

{

private:

    virtual int _stdcall Startup(void *list);

    virtual int _stdcall  Stop ();

    virtual int _stdcall  Realse ();

   

Private:

   //……

};

 

Class CCar

{

private:

    virtual  int _stdcall  Startup(void *list);

    virtual  int _stdcall  Stop ();

    virtual  int _stdcall  Realse ();

   

Private:

   //……

};

 

  我们可以定义一个统一的方法,来创建和释放我们的接口,Engine.h 中加入如下代码:

  #define IID_ IAirplaneEng  1

#define IID_ ICarEng      2  

 

extern "C" __declspec(dllexport) HRESULT CreateInstance(DWORD iid_num,void **iface);

 

让我们在Engine.cpp 中加入如下代码,主要是实现CreateInstance,和类的Realse()

HRESULT CreateInstance(DWORD iid_num,void **iface)

{

     Switch(iid_num)

     {

         Case IID_ IAirplaneEng:

              *iface = (IAirplaneEng *)new CAirplane;

               Break;

         Case IID_ ICarEng:

*iface = (ICarEng *)new CCar;

               Break;

         Default: break;

}

 Return S_OK;

 

}

 

Void CAirplane::Release()

{

   // Release resource….

   Delete this;

}

 

Void CCar::Release()

{

   // Release resource….

   Delete this;

}

 

那么外部怎么样调用你的东西呢?其实很简单了,一段简短的代码如下:

#include “Engine.h”

   

Int main()

{

   IAirplaneEng *p_IAirplane =NULL;

   ICarEng     * p_ICar =NULL;

 

   If(!CreateInstance(IID_ IAirplaneEng,(void **) &p_IAirplane))

   {//error information…..}

   p_IAirplane->Starup();

p_IAirplane->Sop();

p_IAirplane->Release();

 

If(!CreateInstance(IID_ ICarEng,(void **)& p_ICar))

{//error information…..}

   p_ICar ->Starup();

p_ICar ->Sop();

p_ICar ->Release();

 

   Return 0;

}

 

好了,搞定!然后,你就可以将你的动态库和头文件提供给别人使用了,在头文件类不会暴露你类的任何细节,实现了有效的封装。当然上述也有不完美之处,最好在自己的类中提供类似于com AddRef QueryInterface 的方法,也可以为每一个接口生成唯一的guid号,天哪,那不就是com 了吗!呵呵。。