DLL学习之BCB篇

  DLL从本质上来说是一个包含有函数或者数据的程序模块,它可以被可执行文件或者是其他的DLL所调用,为应用程序提供函数、类或各种资源。DLL是软件工程模块化思想的具体实现,也是更为抽象的COM技术的实现基础,具有很高的学习和使用价值。

  一、动态链接库的创建

  1,创建不使用库资源的DLL

  在C++ Builder 6.0集成环境中,可以通过应用程序模板来生成DLL。选择File|New|Other...,在弹出的New Items对话框中选择DLL Wizard,根据向导提示选择C++;而Use VCL和Use CLX分别决定了是使用VCL类库还是跨平台的组件库CLX。它们两个是互斥的,只能选择一个,也可以全不选。选项Multi Threaded决定是否利用多线程技术;选项VC++ Style DLL决定是否采用VC++ Style风格,其主要区别在于DLL的入口函数是否采用DLLMain。在本节中,将创建一个Windows下的一般DLL,不使用VCL或其他类库。

  确定选择后向导会自动创建一个DLL的框架。

  我们看一下系统自动生成的代码,可以看到主体部分除了注释之外只有一个DLL入口函数DllEntryPoint()。这个函数将在DLL加载和卸载的时候被调用,而调用的具体情况则取决于它的reason参数。通常如果开发者编写的DLL不涉及到函数外的内存管理,就没必要编写该函数里的内容。

  代码中那一大段的注释大意是说最好不要使用string类型来作为DLL函数的参数,否则在使用DLL库的时候还需要包含内存管理单元的代码(包含MEMMGR.LIB文件,通过BORLNDMM.DLL来进行内存管理)。

  本节将在DLL中实现一个求解N的阶乘的功能函数。在上面生成的DllEntryPoint函数的前、后分别添加如下代码:

  extern "C" __declspec(dllexport) int fac(int n);

  int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)

  {

  return 1;

  }

  int fac(int n)

  {

  if(n==0)

  return 1;

  else

  return(n*fac(n-1));

  }

  在语句extern "C" __declspec(dllexport) int fac(int n)中,申明该DLL中定义了一个fac函数,该函数将列入DLL的导出库函数列表中,可以被其他程序调用。extern "C"申明C++ Builder将采用C方式来命名函数,而不采用C++方式,这样更方便函数的调用。另外,在DLL接口中除了可以声明函数外,还可以声明类和变量,例如:

  class __declspec(dllexport) DllClass1; //申明DLL中定义的类DllClass1可以被其他程序调用

  int __declspec(dllexport) DllNumber1; //同上,变量可以被调用

  代码添加完毕后,保存工程,然后编译,此时会提示无法调试程序,因为没有主程序调用它。不管它,看一下程序目录,已经生成了一个Dll.dll文件和一个Dll.lib文件(如果没有生成lib文件,请检查菜单Porject|Options|linker中的Generate import library项是否被选中)。这两个文件就是动态链接库的主体文件,*.dll中包含了函数或类的实现内容等信息,*.lib中则包含了函数在动态链接库中的位置引用等信息。

  2,创建使用VCL的DLL

  在C++ Builder 6.0中,可视化类库VCL为其提供了强大的支持。熟练使用VCL可以充分利用它设计出界面友好、功能强大的程序。在本节中将创建一个使用VCL来实现简易计算器功能的DLL。

  同上,在向导中选择Use VCL项,单击“OK”后向导将自动创建一个DLL框架。可以看到代码与上次的相比多了一句#include <vcl.h>,这意味着在本DLL工程中可以使用VCL了。接下来我们保存工程为VclDll.bpr,保存程序单元为VclDll.cpp,然后通过菜单File|New|Form新建一个窗体,保存单元名称为Unit1,主窗体命名为UnitForm。然后按下图来制作窗体:

  然后编写相应的事件代码,完整代码如下:

  //---------------------------------------------------------------------------

  #include <vcl.h>

  #pragma hdrstop

  #include "Unit1.h"

  //---------------------------------------------------------------------------

  #pragma package(smart_init)

  #pragma resource "*.dfm"

  TUnitForm *UnitForm;

  String s="";

  float opn,opn1,opn2;

  int opr;

  extern "C" __declspec(dllexport) _stdcall void UseCalu();

  //---------------------------------------------------------------------------

  __fastcall TUnitForm::TUnitForm(TComponent* Owner)

  : TForm(Owner)

  {

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button10Click(TObject *Sender)

  { //“1”按钮事件

  String c="1";

  s=s+c;

  Edit1->Text=s;

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button11Click(TObject *Sender)

  { //“2”按钮事件,其余3到0均一样,省略

  String c="2";

  s=s+c;

  Edit1->Text=s;

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button15Click(TObject *Sender)

  { //点号按钮事件

  String c=".";

  s=s+c;

  Edit1->Text=s;

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button5Click(TObject *Sender)

  { //加号按钮事件

  opn1=StrToFloat(s);

  s="";

  opr=1;

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button9Click(TObject *Sender)

  { //减号按钮事件

  opn1=StrToFloat(s);

  s="";

  opr=2;

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button13Click(TObject *Sender)

  { //乘号按钮事件

  opn1=StrToFloat(s);

  s="";

  opr=3;

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button17Click(TObject *Sender)

  { //除号按钮事件

  opn1=StrToFloat(s);

  s="";

  opr=4;

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button16Click(TObject *Sender)

  { //等号按钮事件

  opn2=StrToFloat(Edit1->Text);

  Edit1->Text="";

  s="";

  switch(opr)

  {

  case 1:

  opn=opn1+opn2;

  break;

  case 2:

  opn=opn1-opn2;

  break;

  case 3:

  opn=opn1*opn2;

  break;

  case 4:

  opn=opn1/opn2;

  break;

  }

  Edit1->Text=FloatToStr(opn);

  }

  //---------------------------------------------------------------------------

  void __fastcall TUnitForm::Button1Click(TObject *Sender)

  { //清空按钮事件

  s="";

  Edit1->Text="";

  opn=0;

  opn1=0;

  opn2=0;

  }

  //---------------------------------------------------------------------------

  void __stdcall UseCalu()

  {  //DLL输出函数

  TUnitForm *TestForm=new TUnitForm(NULL);

  TestForm->ShowModal();

  delete TestForm;

  }

  //---------------------------------------------------------------------------

  编写完毕后编译此工程,得到VclDll.dll和VclDll.lib文件。

  二、动态链接库的调用

  动态链接库只有装入内存后,才能被调用。根据DLL是在应用程序运行时自动装入还是在需要的时候主动装入,可分为两种调用方式:静态调用和动态调用。

  1,静态调用

  静态调用也成为隐含调用,这种调用方法在装入DLL时采用非主动和不可见方式。它需要应用程序在运行前将与DLL实现文件(*.dll)相对应的库文件(*.lib)添加到工程中。当应用程序运行时,系统将DLL装入内存,应用程序结束后,系统将该DLL释放。DLL静态调用的好处在于可以让调用者不用考虑DLL的载入和释放等问题。

  本节将新建一个应用工程TestDll,采用静态调用方法来调用第一个实例中生成的DLL。选择File|New|Application,新建一个应用工程,保存为TestDll.bpr和TestUnit.cpp。添加一个Button,一个Label,一个Edit控件,将Button的Caption属性改为“计算N的阶乘”,Label的Caption属性改为“请输入N值:”;然后选择Project|Add to Project...,将Dll.lib添加到工程中,编写代码如下:

  #include <vcl.h>

  #pragma hdrstop

  #include "TestUnit.h"

  //---------------------------------------------------------------------------

  #pragma package(smart_init)

  #pragma resource "*.dfm"

  extern "C" __declspec(dllimport) int fac(int n);

  TForm1 *Form1;

  //---------------------------------------------------------------------------

  __fastcall TForm1::TForm1(TComponent* Owner)

  : TForm(Owner)

  {

  }

  //---------------------------------------------------------------------------

  void __fastcall TForm1::Button1Click(TObject *Sender)

  {

  int i;

  i=fac(StrToInt(Edit1->Text));

  ShowMessage("结果是:"+AnsiString(i));

  }

  使用前需要将第一个实例中生成的Dll.dll和Dll.lib复制到此应用程序的目录中。最后按F9运行此程序,结果如图:

  从此例可以看出,为了使用DLL中的fac函数,调用时必须采用相应的函数说明语句:extern "C" __declspec(dllimport),正好与DLL中的__declspec(dllexport)相对应。

  2,动态调用

  动态调用是当需要时才将DLL装入内存,使用完毕就立即释放。它的有点是装入速度较快而且可以节约内存,但它要求开发者需要做较多的工作。动态调用需要用到以下几个重要的API函数:

  LoadLibrary();  //装入DLL并获得其句柄

  GetProcAddress(); //获得调用函数的指针

  FreeLibrary();  //卸载DLL

  本节将新建一个工程TestVclDll,采用动态调用的方法来实现第二个DLL中的计算器功能。

  使用前请先将第二个实例中生成的VclDll.dll和VclDll.lib复制到当前程序目录下。此工程中只添加一个Button控件,事件函数如下:

  void __fastcall TForm1::Button1Click(TObject *Sender)

  {

  HINSTANCE hInst;

  hInst = LoadLibrary("VclDll.dll");

  (FARPROC &)userca = GetProcAddress(hInst,"UseCalu");

  userca();

  FreeLibrary(hInst);

  }

  然后在该文件的开始部分加入函数UseCalu的说明语句:void (* userca) ();

  编译运行结果如图:

  三,资源型动态链接库的创建与实现

  资源是指附加于程序的数据。资源不能直接包含代码,但是它们能够包含应用程序代码所需要的信息。资源包括字符串、声音、位图等,这些信息可以由可执行文件携带或存储于动态链接库中。可以利用动态链接库的特点,将相关资源放到DLL中来实现资源的统一存储与管理,进而实现其他功能。例如,可将应用于应用程序的不同语言版本的字符串资源放到DLL资源文件中,并在不同的环境下加载合适的资源。

  1,资源文件的编写方法

  资源文件的后缀为.rc,在资源文件中可以编辑字符串资源,定义声音文件和位图文件。每种资源文件的编写需遵循相应的规则。

  (1)字符串资源的编写规则如下:

  STRINGTABLE(DISCARDABLE)

  {

  标识符 字符串

  }

  STRINGTABLE为字符串资源标识的关键字,DISCARDABLE为可选项,表示不必常驻内存。数据定义区中的标识符为整型数,与所定义的字符串一一对应。需要引用字符串资源时,将使用到该标识符整数。例如:

  STRINGTABLE(DISCARDABLE)

  {

  101 "File"

  }

  (2)声音文件资源的编写规则如下:

  标识符 WAVE (DISCARDABLE) "实际文件名"

  WAVE为声音文件资源的标识符关键字,声音文件资源中的标识符与字符串资源中的标识符定义方法相同,取整数类型,代表所定义的声音文件。实际文件名中往往需要包含文件的具体存储路径。例如:

  1001 WAVE DISCARDABLE "c:\windows\ding.wav"

  (3)位图文件资源的编写规则如下:

  标识符 BITMAP (DISCARDABLE) "实际文件名"

  位图文件资源的定义方法与声音文件类似,BITMAP为其标识符关键字。例如:

  2001 BITMAP DISCARDABLE "c:\windows\paintbrush.bmp"

  2,资源型动态链接库的创建

  在C++ Builder 6.0中,通过DLL Wizard,生成一个工程,命名为Ch07.bpr,程序单元保存为Ch07Unit1.cpp。然后新建一个文本文件,保存单元名称为Mylanguage.rc,将该文件添加到工程中。在Mylanguage.rc资源文件中编辑代码如下:

  STRINGTABLE DISCARDABLE

  {

  101 "File"

  102 "Edit"

  103 "About"

  1011 "New"

  1012 "Open"

  1013 "Close"

  1014 "Exit"

  201 "文件"

  202 "编辑"

  203 "关于"

  2011 "新建"

  2012 "打开"

  2013 "关闭"

  2014 "退出"

  301 "ぶんしょう"

  302 "編集する"

  303 "にっいて"

  3011 " 新しく"

  3012 "開 (ひら)く"

  3013 "閉める"

  3014 "たいしゅつ"

  }

  编译连接成功后会生成动态链接库的主体文件Ch07.dll,就创建好了一个包含字符串资源的DLL。

  3,资源性动态链接库的调用

  新建一个工程,命名为Ch07_06.bpr,保存程序单元为Ch07_06Unit1.cpp,然后新建一个表单Form2,保存为Ch07_06Unit2.cpp。

  在Form2中,添加一个MainMenu组件,设计好菜单,然后在Form1中添加三个RadioButton和一个Button组件。编辑Ch07_06Unit1.cpp文件代码如下:

  #include <vcl.h>

  #pragma hdrstop

  #include "Ch07_6Unit1.h"

  #include "Ch07_6Unit2.h"

  //---------------------------------------------------------------------------

  #pragma package(smart_init)

  #pragma resource "*.dfm"

  TForm1 *Form1;

  HINSTANCE hInst;

  char StrBuff[100];

  //---------------------------------------------------------------------------

  __fastcall TForm1::TForm1(TComponent* Owner)

  : TForm(Owner)

  {

  hInst=LoadLibrary("Ch07_5.dll");

  }

  //---------------------------------------------------------------------------

  void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

  {

  if(hInst != NULL)

  FreeLibrary(hInst);

  }

  //---------------------------------------------------------------------------

  void __fastcall TForm1::Button1Click(TObject *Sender)

  {

  TForm2 *form=new TForm2(this);

  if(RadioButton1->Checked)

  {

  LoadString(hInst,101,StrBuff,100);

  form->File1->Caption=StrBuff;

  LoadString(hInst,1011,StrBuff,100);

  form->New1->Caption=StrBuff;

  LoadString(hInst,1012,StrBuff,100);

  form->Open1->Caption=StrBuff;

  LoadString(hInst,1013,StrBuff,100);

  form->Close1->Caption=StrBuff;

  LoadString(hInst,1014,StrBuff,100);

  LoadString(hInst,102,StrBuff,100);

  form->Edit1->Caption=StrBuff;

  LoadString(hInst,103,StrBuff,100);

  form->About1->Caption=StrBuff;

  }

  else if(RadioButton2->Checked)

  {

  LoadString(hInst,201,StrBuff,100);

  form->File1->Caption=StrBuff;

  LoadString(hInst,2011,StrBuff,100);

  form->New1->Caption=StrBuff;

  LoadString(hInst,2012,StrBuff,100);

  form->Open1->Caption=StrBuff;

  LoadString(hInst,2013,StrBuff,100);

  form->Close1->Caption=StrBuff;

  LoadString(hInst,2014,StrBuff,100);

  LoadString(hInst,202,StrBuff,100);

  form->Edit1->Caption=StrBuff;

  LoadString(hInst,203,StrBuff,100);

  form->About1->Caption=StrBuff;

  }

  else

  {

  LoadString(hInst,301,StrBuff,100);

  form->File1->Caption=StrBuff;

  LoadString(hInst,3011,StrBuff,100);

  form->New1->Caption=StrBuff;

  LoadString(hInst,3012,StrBuff,100);

  form->Open1->Caption=StrBuff;

  LoadString(hInst,3013,StrBuff,100);

  form->Close1->Caption=StrBuff;

  LoadString(hInst,3014,StrBuff,100);

  LoadString(hInst,302,StrBuff,100);

  form->Edit1->Caption=StrBuff;

  LoadString(hInst,303,StrBuff,100);

  form->About1->Caption=StrBuff;

  }

  form->ShowModal();

  }

  编译连接成功后就可以实现程序的多语言版本了。
 



[本日志由 admin 于 2009-08-13 09:32 PM 编辑]
文章来自: 网络
引用通告地址: http://www.cn-sohu.com/bolg/trackback.asp?tbID=220
Tags:
评论: 0 | 引用: 124 | 查看次数: 359
发表评论
你没有权限发表留言!
分享到: