⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 00000004.htm

📁 水木清华关于C++Builder程序的代码
💻 HTM
📖 第 1 页 / 共 2 页
字号:
<HTML><HEAD>  <TITLE>BBS水木清华站∶精华区</TITLE></HEAD><BODY><CENTER><H1>BBS水木清华站∶精华区</H1></CENTER>发信人:&nbsp;life&nbsp;(沙加~重结晶),&nbsp;信区:&nbsp;BCB&nbsp;<BR>标&nbsp;&nbsp;题:&nbsp;DLL动态连结程式库&nbsp;<BR>发信站:&nbsp;BBS&nbsp;水木清华站&nbsp;(Thu&nbsp;Nov&nbsp;19&nbsp;09:03:44&nbsp;1998)&nbsp;<BR>&nbsp;<BR>第十二章&nbsp;动态链结函式库(DLL-Dynamic&nbsp;Linked&nbsp;Library)&nbsp;&nbsp;<BR>&nbsp;<BR>前言&nbsp;<BR>&nbsp;<BR>本章要介绍的是动态链结函式库(Dynamic&nbsp;Linked&nbsp;&nbsp;<BR>Library,简称DLL)的撰写、使用及相关主题。动态链结函式库是Windows&nbsp;<BR>程式设计的一门重要领域,不信的话,你可以看看在Windows系统目录下那&nbsp;<BR>些数量庞大的&nbsp;&nbsp;<BR>.DLL档案,它的重要性及使用频率由此可见一般。&nbsp;<BR>&nbsp;<BR>基本上,如果略去VCL软体元件不谈的话,在C++Builder中撰写及使用DLL&nbsp;<BR>的方法是和传统Windows&nbsp;&nbsp;<BR>SDK是一致的,然而如此一来C++Builder也就失去了它傲人的优势了。因此&nbsp;<BR>在本章中我会为你介绍如何撰写使用VCL元件的&nbsp;&nbsp;<BR>DLL,同时也针对各种不同程式发展平台如Visual&nbsp;C++,&nbsp;VB之间的DLL使用&nbsp;<BR>上应注意的事项,做一个全面的探讨。&nbsp;&nbsp;<BR>&nbsp;<BR>以C++Builder撰写动态链结函式库&nbsp;(DLL)&nbsp;<BR>&nbsp;<BR>图一&nbsp;以C++Builder撰写的About&nbsp;Dialog&nbsp;<BR>&nbsp;<BR>图一所展示的就是我所要撰写的一个以VCL元件组合而成的About&nbsp;Dialog,&nbsp;<BR>如何?看起来是不是颇具商业软体架势呢?&nbsp;<BR>&nbsp;<BR>C++Builder由於其先天上的优势,因此在视觉化的程式设计领域游刃有馀。&nbsp;<BR>然而在现实的工作环境中,也许在你手中的专案并非使用C++Builder来撰&nbsp;<BR>写,而是以其他程式工具如Visual&nbsp;&nbsp;<BR>C++,VB或是Borland&nbsp;&nbsp;<BR>C++完成的,如果要全部改写原来的程式,不仅旷日废时,而且可能老板也&nbsp;<BR>不允许,那麽该怎麽办呢?对了,就是利用撰写DLL的途径来达到程式共享&nbsp;<BR>的目的,为了要让传统的Windows&nbsp;&nbsp;<BR>SDK程式设计人员也可以享受此一优势,因此你可以将部份视觉程式设计部&nbsp;<BR>份以DLL完成,然後提供外部函式供他人呼叫,如此你就可以兼顾两者,『执&nbsp;<BR>其两端,用於其中』,而顺利地解决问题了。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>好了!废话不多说了,现在开始进入正题吧!&nbsp;<BR>&nbsp;<BR>建立DLL专案&nbsp;<BR>&nbsp;<BR>建立DLL专案的方式和一般应用程式大致相同。同样地你可以由【File/New】&nbsp;<BR>来建立一个新的专案,然後选择DLL类型的专案。&nbsp;&nbsp;<BR>如图二所示:&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;<BR>图二&nbsp;选择DLL专案类型&nbsp;<BR>&nbsp;<BR>建选择完专案类型之後,它就自动为你产生了相关档案。和应用程式不同的&nbsp;<BR>是,它只产生了一个Project档,而不包含表格档,而该档案只是一个包含&nbsp;<BR>DLL进入点程式的空壳子,程式大致如下:&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>int&nbsp;WINAPI&nbsp;DllEntryPoint(HINSTANCE&nbsp;hinst,&nbsp;unsigned&nbsp;long&nbsp;reason,&nbsp;void*)&nbsp;&nbsp;<BR>{&nbsp;&nbsp;<BR>return&nbsp;1;&nbsp;&nbsp;<BR>}&nbsp;<BR>&nbsp;<BR>DllEntryPoint是DLL内定的程式进入点,因为本程式中并不做任何处理,&nbsp;<BR>所以就直接return&nbsp;1了。&nbsp;<BR>&nbsp;<BR>加入TForm表格&nbsp;<BR>&nbsp;<BR>为了要撰写如图一的About&nbsp;&nbsp;<BR>Dialog,毫无疑问地,我们必须加入一个TForm表格,因为建立DLL专案时,&nbsp;<BR>并未自动产生相关的TForm表格,所以你必须以手动方式加入。此时你可以&nbsp;<BR>【File/New&nbsp;&nbsp;<BR>Form】来加入一个表格。再来我们就可以用和一般应用程式设计相同的方式,&nbsp;<BR>加入必要的软体元件,如图三所示。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;&nbsp;<BR>图三&nbsp;在设计时期(Design&nbsp;Time)的&nbsp;TForm。&nbsp;<BR>&nbsp;<BR>你可以看到,我在程式中使用了三个TPanel元件(除了标出来的之外,另&nbsp;<BR>外还有一个用来作为放置所有元件的平台)。以及一个TImage元件,图三个&nbsp;<BR>的三个Panel元件的样子都不同,那是利用修改其&nbsp;&nbsp;<BR>BevelInner,BevelOuter,BevelWidth来达成的,你可以试着去修改它,看&nbsp;<BR>看能否做出更好的效果。至於TImage是用来做为显示那张雅典娜图形的元&nbsp;<BR>件。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>在安排好了所有元件的位置之後,我们再设定所有元件的OnClick事件处理&nbsp;<BR>函式,让它可以在使用者按下滑鼠时,关掉该交谈窗。这个事件处理函式很&nbsp;<BR>简单,只有短短的一行。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>void&nbsp;__fastcall&nbsp;TForm1::Image1Click(TObject&nbsp;*Sender)&nbsp;&nbsp;<BR>{&nbsp;&nbsp;<BR>Close();&nbsp;&nbsp;<BR>}&nbsp;<BR>&nbsp;<BR>好了,至此我们已完成加入表格的程序。&nbsp;<BR>&nbsp;<BR>撰写输出函式(Export&nbsp;Function)&nbsp;<BR>&nbsp;<BR>在完成的表格的设计後,再来我们就要撰写输出函式,该外部程式可以利用&nbsp;<BR>呼叫该函式的方式显示这个表格。我们的输出函式定义如下:&nbsp;&nbsp;<BR>extern&nbsp;&quot;C&quot;&nbsp;void&nbsp;_stdcall&nbsp;ShowImage(void);&nbsp;&nbsp;<BR>&nbsp;<BR>其中&nbsp;extern&nbsp;&quot;C&quot;&nbsp;是用来告诉编译器,以C的方式来命名,而不要以C++&nbsp;的&nbsp;<BR>命名法,因为C++&nbsp;&nbsp;<BR>的命名法会在函式名称後加上叁数型态等装饰字,如此会造成其他程式如&nbsp;<BR>VC++,VB等无法使用的困扰。另外&nbsp;&nbsp;<BR>__stdcall是用来表示它使用的叁数传入方法。我们在後续单元会针对以上&nbsp;<BR>两者做更为深入的介绍。&nbsp;&nbsp;<BR>&nbsp;<BR>再来我们来看函式本身,这个函式很简单,只是利用new动态产生一个表格,&nbsp;<BR>然後利用ShowModal来显示该表格,ShowModal会一直等到使用者按Click&nbsp;<BR>之後才关掉表格,此时我们再以delete指令来释放占用的记忆体。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>void&nbsp;_export&nbsp;_stdcall&nbsp;ShowImage(void)&nbsp;&nbsp;<BR>{&nbsp;&nbsp;<BR>Form1&nbsp;=&nbsp;new&nbsp;TForm1(NULL);&nbsp;&nbsp;<BR>Form1-&gt;ShowModal();&nbsp;&nbsp;<BR>delete&nbsp;Form1;&nbsp;&nbsp;<BR>}&nbsp;<BR>&nbsp;<BR>在完成以上程式之後,你就可以编译程式。此时C++Builder会产生一个DLL&nbsp;<BR>档,以本程式而言,它会产生一个DLLSAMP.DLL档案,而这个就是供外部呼&nbsp;<BR>叫的动态链结函式库。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>在C++Builder中使用DLL&nbsp;<BR>&nbsp;<BR>再来我要告诉你如何使用动态链结函式库。我们以前面所产生的DLL为例。&nbsp;<BR>使用DLL有两种方式,分别为明确呼叫及不明确呼叫。&nbsp;&nbsp;<BR>&nbsp;<BR>我先说明不明确呼叫的使用方式。不明确呼叫指的是,在程式中并没有一行&nbsp;<BR>程式是用来载入DLL,而是利用链结一个记载输入函式的函式库档案(LIB),&nbsp;<BR>来进行链结,如此系统会自动将该DLL载入,同时在使用完毕後将其释放,&nbsp;<BR>不必由使用者(也就是呼叫它的函式)来进行载入及释放的动作。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>首先必须产生一个LIB档,你可以利用C++Builder程式目录内的IMPLIB.EXE&nbsp;<BR>来产生该档案,切忌勿使用Visual&nbsp;C++&nbsp;&nbsp;<BR>的IMPLIB.EXE,因为Microsoft所使用的格式是COFF格式的LIB档,而&nbsp;<BR>Borland所使用的格式是OMF格式的LIB档。(同样地,若是你的LIB档是要&nbsp;<BR>给Visual&nbsp;&nbsp;<BR>C++&nbsp;链结用的,那就要使用它所附的IMPLIB.EXE,在使用时不可不察)。因&nbsp;<BR>此我们可用以下指令产生DLLSAMP.LIB档。&nbsp;&nbsp;<BR>&nbsp;<BR>IMPLIB&nbsp;DLLSAMP.LIB&nbsp;DLLSAMP.DLL&nbsp;<BR>&nbsp;<BR>如此你就可以得到供程式链结用DLLSAMP.LIB档了。&nbsp;<BR>&nbsp;<BR>接着我们来撰写使用该DLL的范例程式。这个程式相当简单,我只在表格中&nbsp;<BR>放置一个Button,然後撰写该Button的OnClick事件处理函式,使其呼叫&nbsp;<BR>ShowImage函式即可。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>有一点要注意的是,你必须将先前产生的DLLSAMP.LIB加入此专案中,利用&nbsp;<BR>【Project/Add&nbsp;to&nbsp;Project】选择LIB型态档案,即可将其加入。&nbsp;<BR>&nbsp;<BR>最後我们就可以链结程式,以下为其执行结果。&nbsp;&nbsp;<BR>&nbsp;&nbsp;<BR>图四&nbsp;执行结果。&nbsp;<BR>&nbsp;<BR>动态链结函式库彻底研究&nbsp;<BR>&nbsp;<BR>在前面的范例中,我们已经示范了一个基础dll的撰写方式,然而那只能说&nbsp;<BR>是少部份的Know-How而已,接下来我想针对DLL做一个彻底的探讨,企图&nbsp;<BR>使您对它有一个全面的认知,同时也希望在Know-How之外,可以告诉你一&nbsp;<BR>些关於DLL的Know-Why。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>DLL的生与死&nbsp;<BR>&nbsp;<BR>DLL顾名思义,是一个可以动态链结的函式库。这其中包含两个意义。第一,&nbsp;<BR>它是动态链结的,也就是说它必须具有『招之即来,挥之即去』的基本特性,&nbsp;<BR>它只有在被需要的时候才会被载入系统中,而在不被需要时,即自系统中释&nbsp;<BR>放。第二,它是一个函式库,因此它的行为模式和一般的函式库没什麽不同,&nbsp;<BR>当它载入时,它就视同其他一般的函式般。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>『招之即来,挥之即去』的DLL&nbsp;<BR>&nbsp;<BR>前面我们提到,DLL必须具备『招之即来,挥之即去』的基本特性。那麽要&nbsp;<BR>如何载入及释放DLL呢?关於此点,我们必须分为两方面来探讨;即所谓的&nbsp;<BR>明确呼叫及不明确呼叫。&nbsp;&nbsp;<BR>&nbsp;<BR>明确呼叫(explicited&nbsp;linked):所谓明确呼叫(explicited&nbsp;linked)是&nbsp;<BR>使用LoadLibrary函式来载入&nbsp;&nbsp;<BR>DLL。使用FreeLibrary函式来释放&nbsp;DLL。这种方式是由使用者主动透过&nbsp;<BR>LoadLibrary&nbsp;载入该&nbsp;&nbsp;<BR>DLL,然後以GetProcAddress来取得函式位址,再呼叫该函式。最後在不使&nbsp;<BR>用该DLL之後,再将其释放。使用明确呼叫的优点在於,你可以完全控制该&nbsp;<BR>DLL的载入及释放,最有效地利用系统资源:缺点则是,必须自行利用&nbsp;<BR>GetProcAddress来取得叫使用的函式位址,但也由於使用了GetProcAddress&nbsp;<BR>来取得函式位址,因此在使用上增加许多弹性。由於此种使用方式载入函式&nbsp;<BR>程式是主动且可见的,因此名之为明确呼叫。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>不明确呼叫&nbsp;(&nbsp;implicited&nbsp;linked):所谓不明确呼叫则是利用链结DLL函&nbsp;<BR>式库所相对应的输出函式库&nbsp;(&nbsp;export&nbsp;&nbsp;<BR>library),来达成呼叫函式的目的。因此载入DLL以及释放DLL的程序是不&nbsp;<BR>可见的,当使用该输出函式库的程式载入後,系统即将该DLL载入,当使用&nbsp;<BR>该输出函式的程式结束後,系统即将该DLL释放。使用不明确呼叫的优点在&nbsp;<BR>於,使用者可以完全不必顾虑到函式的载入及释放相关问题,使用时就如同&nbsp;<BR>一般的静态函式般。由於此种使用方式载入函式是非主动且不可见的,因此&nbsp;<BR>名之为不明确呼叫。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>DLL的使用次数&nbsp;(Usage&nbsp;Count)&nbsp;<BR>&nbsp;<BR>前面提到的载入及释放其实在定义上是不明确的。为什麽呢?因为DLL的载&nbsp;<BR>入及释放尚牵涉到多行程使用时的载入及释放。由於DLL是动态链结的,因&nbsp;<BR>此可以同时有许多程式在使用同一个&nbsp;&nbsp;<BR>DLL,举例来说:若一个X.DLL同时被A、B、C三个程式使用着,则X.DLL&nbsp;<BR>会被载入三次。然而系统为了结省资源,当然不会重复载入,因此此时在系&nbsp;<BR>统内会有一个表格来记载X.DLL的使用次数。所以当A程式载入X.DLL後,&nbsp;<BR>B、C程式再次载入X.DLL时,此时X.DLL并没有被重复地载入,系统只是将&nbsp;<BR>X.DLL的使用次数加一,然後将先前载入的X.DLL位址传回给&nbsp;&nbsp;<BR>B、C两个程式使用,如此就可以达到共享函式库的目的了。同样地在释放X.DLL&nbsp;<BR>时,若该DLL同时有多人使用时,系统纯粹只是将该DLL的使用次数减一,&nbsp;<BR>当其使用次数等於0时,系统才会『真正』地将它由系统中释放。否则若是&nbsp;<BR>系统不分青红皂白即将DLL释放,会造成系统的灾难。&nbsp;&nbsp;<BR>&nbsp;<BR>&nbsp;<BR>由以上可知,无论我们使用明确呼叫或是不明确呼叫,DLL的载入及释放都&nbsp;<BR>和它的使用次数有关。所以DLL的生与死其实和它的使用次数有关,当它的&nbsp;<BR>使用次数不为0时,就表示其『阳寿未尽』,系统就会维持其活动状态;反&nbsp;<BR>之,若其使用次数为0时,则表示它该『寿终正寝』了,系统就将其释放,&nbsp;<BR>并回收其使用的资源。然而若使用该DLL的程式当掉,导致该DLL没被释放&nbsp;<BR>时,该DLL就会因为使用次数没有被适时减少,而一直在系统内『阴魂不散』&nbsp;<BR>了。这种利用使用次数来管理共享资源的方法,也同时使用在OLE之中。&nbsp;&nbsp;<BR>&nbsp;<BR>新知识的实践&nbsp;<BR>&nbsp;<BR>现在我们已了解DLL的使用,尚有另一种明确呼叫的方式,我们可以将前面&nbsp;<BR>的范例程式修改为使用明确呼叫的方法来使用&nbsp;DLL。&nbsp;<BR>&nbsp;<BR>void&nbsp;(*ShowImage)(void);&nbsp;&nbsp;<BR>void&nbsp;__fastcall&nbsp;TForm1::ShowButtonClick(TObject&nbsp;*Sender)&nbsp;&nbsp;<BR>{&nbsp;&nbsp;<BR>HINSTANCE&nbsp;hInst;&nbsp;&nbsp;<BR>hInst&nbsp;=&nbsp;LoadLibrary(&quot;DLLSAMP.DLL&quot;);&nbsp;&nbsp;<BR>(FARPROC&nbsp;&amp;)ShowImage=GetProcAddress(hInst,&quot;ShowImage&quot;);&nbsp;&nbsp;<BR>ShowImage();&nbsp;&nbsp;<BR>FreeLibrary(hInst);&nbsp;&nbsp;<BR>}&nbsp;<BR>&nbsp;<BR>以上就是修改後的程式,因为程式已改成明确呼叫的方式,因此不需要使用&nbsp;<BR>DLLSAMP.LIB了,所以关於BCB和VC所使用的LIB档格式不同的问题也不存&nbsp;<BR>在了。在此我简单地说明所使用的几个函式&nbsp;&nbsp;<BR>&nbsp;<BR>

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -