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

📄 chap12.htm

📁 该书是c++builder编程者的重要指导手册
💻 HTM
📖 第 1 页 / 共 2 页
字号:
</FONT><P><FONT SIZE=2>#ifndef _SHOWIMG_H_</FONT><P><FONT SIZE=2>#define _SHOWIMG_H_<BR></FONT><P><FONT SIZE=2>#ifndef IMGDLL</FONT><P><FONT SIZE=2>#define EXTERN __declspec(dllimport)</FONT><P><FONT SIZE=2>#else</FONT><P><FONT SIZE=2>#define EXTERN __declspec(dllexport)</FONT><P><FONT SIZE=2>#endif</FONT><P><FONT SIZE=2>void EXTERN ShowImage(void);</FONT><P><FONT SIZE=2>#endif<BR></FONT><P><FONT SIZE=2>如此一来,当你在撰写DLL时撰写可以撰写如下函式:<BR></FONT><P><FONT SIZE=2>#define IMGDLL</FONT><P><FONT SIZE=2>#include  &quot;image.h&quot;<BR></FONT><P><FONT SIZE=2>当使用者在使用DLL时,则只要直接含入image.h即可。如此一来算是解决了利用__declspec(dllimport) 和 __declspec(dllexport) 的不便了。<BR></FONT><UL><LI><FONT SIZE=2>必也正名乎的DLL函式命名</FONT></UL><P><FONT SIZE=2></FONT><UL><LI><FONT SIZE=2>谈完了标准写法,再来我们要谈谈一个更容易搞混的函式命名原则。本来在正常情况下,我们是不需要理会编译器的函式命名规则的,因为在使用同一样编译器的情况下,不会有什麽太大的问题。然而问题来了,由於DLL是动态连结函式库,因此它的目标就是希望可以让多个程式共享程式及资源。所以若是DLL只能为同一种编译器所使用,那麽它的用途就大打折扣了。因此我们还是必须了解函式的命名方法。同时由於函式命名方式在各种不同的编译器各不相同,因此我们也必须了解其相异处,最重要的是,我们必须找出其沟通的方式。</FONT></UL><P><FONT SIZE=2></FONT><P><FONT SIZE=2>C++ Builder的命名规则<BR></FONT><UL><LI><FONT SIZE=2>除了前面提到的 __declspec编译指令之外,在C++Builder尚有几种修饰字会影响到函数的命名, 它们就是 __cdecl,__stdcall,__pascal,__fastcall四个修饰字。为了了解该修饰字对於函式命名的影响,我们可以用以下的程式来测试之:</FONT></UL><P><FONT SIZE=2></FONT><P><FONT SIZE=2>#ifndef _DLLNAME01_H_</FONT><P><FONT SIZE=2>#define _DLLNAME01_H_</FONT><P><FONT SIZE=2>#ifndef DLLNAME</FONT><P><FONT SIZE=2>#define EXTERN __declspec(dllimport)</FONT><P><FONT SIZE=2>#else</FONT><P><FONT SIZE=2>#define EXTERN __declspec(dllexport)</FONT><P><FONT SIZE=2>#endif</FONT><P><FONT SIZE=2>EXTERN void DllName01(void);</FONT><P><FONT SIZE=2>EXTERN void _stdcall   DllName02(void);</FONT><P><FONT SIZE=2>EXTERN void _cdecl     DllName03(void);</FONT><P><FONT SIZE=2>EXTERN void _pascal    DllName04(void);</FONT><P><FONT SIZE=2>EXTERN void _fastcall  DllName05(void);</FONT><P><FONT SIZE=2>};</FONT><P><FONT SIZE=2>#endif<BR></FONT><P><FONT SIZE=2>以上为程式的定义,同时我们可以在 .CPP档中撰写相对应的空函式,然後将其编译成DLL档,再利用TDUMP.EXE或是VC++内的DUMPBIN.EXE来观察其内容,由於TDUMP会将函式命名解码,反而会使混淆原来的名称,因此以下的输出是由DUMPBIN.EXE得来。<BR><BR><BR><BR><BR></FONT><P><FONT SIZE=2>函式定义      DLL内的函式名  摘要说明</FONT><P><FONT SIZE=2>void DllName01(void)    @DllName01$qv   因为是CPP程式码</FONT><P><FONT SIZE=2>void _stdcall   DllName02(void)  @DllName02$qqsv 所以函式名都被修</FONT><P><FONT SIZE=2>void _cdecl     DllName03(void)  @DllName03$qv  饰过。</FONT><P><FONT SIZE=2>void _pascal    DllName04(void)  @DLLNAME04$QV </FONT><P><FONT SIZE=2>void _fastcall   DllName05(void)  @DllName05$qqrv<BR></FONT><UL><LI><FONT SIZE=2>以上结果是否令你丈二金钢、摸不着头绪。这是因为我们的程式名称若以CPP为延伸名,C++Builder会以C++特有的命名方式来为函式命名,这种命名方式会在函式名称後加上其使用参数的性质,如参数类别等。这在C++中有一个特别的名称,叫做mangled name,这是一种为了要实作出多载函式所发出的命名规则。(注:在C++中Add(int) 和Add(double) 可以同时存在,因此必须在object code区分之)。同时这种命名方式由於各个编译器厂商使用的方式各不相同,因此在撰写DLL时要避免使用之。为了要避开以上问题,我们改以下列的宣告方式:</FONT></UL><P><FONT SIZE=2></FONT><P><FONT SIZE=2>#define _DLLNAME01_H_</FONT><P><FONT SIZE=2>#ifndef DLLNAME</FONT><P><FONT SIZE=2>#define EXTERN __declspec(dllimport)</FONT><P><FONT SIZE=2>#else</FONT><P><FONT SIZE=2>#define EXTERN __declspec(dllexport)</FONT><P><FONT SIZE=2>#endif</FONT><P><FONT SIZE=2>extern &quot;C&quot; {</FONT><P><FONT SIZE=2>EXTERN void DllName011(void);</FONT><P><FONT SIZE=2>EXTERN void _stdcall   DllName022(void);</FONT><P><FONT SIZE=2>EXTERN void _cdecl     DllName033(void);</FONT><P><FONT SIZE=2>EXTERN void _pascal    DllName044(void);</FONT><P><FONT SIZE=2>EXTERN void _fastcall  DllName055(void);</FONT><P><FONT SIZE=2>};</FONT><P><FONT SIZE=2>#endif<BR></FONT><P><FONT SIZE=2>其中extern &quot;C&quot; {琕琕.}; 是用来告诉编译器使用C的命名方式,不要使用C++的mangled name。若是其中只有一个函式时,你可以直接以下列方式宣告之:</FONT><P><FONT SIZE=2>extern &quot;C&quot; void __stdcall ShowImage();<BR></FONT><P><FONT SIZE=2>现在我们可以检视除去mangled name後的函式名称:<BR></FONT><P><FONT SIZE=2>函式定义      DLL内的函式名  摘要说明</FONT><P><FONT SIZE=2>void DllName01(void)    _DllName01    名称加底线</FONT><P><FONT SIZE=2>void _stdcall   DllName02(void)  DllName02    名称未变</FONT><P><FONT SIZE=2>void _cdecl     DllName03(void)  _DllName03    名称加底线</FONT><P><FONT SIZE=2>void _pascal    DllName04(void)  DLLNAME04  名称大写</FONT><P><FONT SIZE=2>void _fastcall   DllName05(void)  @DllName05   名称加@<BR></FONT><P><FONT SIZE=2>以上我们可得知,在未加修饰字时和使用_cdecl修饰字时的名称是一样的。而_pascal修饰字所产生的函式名则和16位元的标准DLL 函式名相同(这在VC++是不被接受的),__fastcall的函式名称则加上 @。<BR></FONT><P><FONT SIZE=2>其中在WIN32中使用最多的是 _stdcall修饰字,这也是你要撰写一个可以和其他语言共同使用时所使用的修饰字,其次则为__cdecl修饰字,这是用来传送不定参数型别的函式如printf、sprintf等使用的。其馀两者几乎在DLL没有机会使用。<BR></FONT><P><FONT SIZE=2>结论:由上可知,在C++Builder中撰写DLL时必须注意以下事项:</FONT><OL><LI><FONT SIZE=2>使用 __declspec(dllimport)及 __declspec(dllexport)的标准型式。</FONT><LI><FONT SIZE=2>注意C++ 的函式名称编码(mangled name)。</FONT><LI><FONT SIZE=2>注意修饰字的使用。除非使用不定参数的函式,否则必使用__stdcall修</FONT></OL><P><FONT SIZE=2>饰字。</FONT><P><FONT SIZE=2>(4) 不要把 __declspec的使用和 __stdcall混淆了。此二者并没有绝对的相关性。即使是程式老手都可能栽在此处,切记,切记!<BR></FONT><P><FONT SIZE=2>怎麽样,在看完了以上的介绍後,是否有晃然大悟的感觉。在了解以上的规则後,今後不论在撰写或是使用DLL时遭遇连结的问题时,应该难不倒你吧!</FONT><P><FONT SIZE=2>最後,我们将标准的DLL宣告方式列於後,以加深你的印象:<BR></FONT><P><FONT SIZE=2>#ifndef _SHOWIMG_H_</FONT><P><FONT SIZE=2>#define _SHOWIMG_H_</FONT><P><FONT SIZE=2>#ifndef IMGDLL</FONT><P><FONT SIZE=2>#define EXTERN __declspec(dllimport)</FONT><P><FONT SIZE=2>#else</FONT><P><FONT SIZE=2>#define EXTERN __declspec(dllexport)</FONT><P><FONT SIZE=2>#endif</FONT><P><FONT SIZE=2>extern &quot;C&quot; EXTERN void __stdcall ShowImage(void);</FONT><P><FONT SIZE=2>#endif<BR></FONT><P><FONT SIZE=2>语言双雄' C++Builder 和Visual C++ 连结<BR></FONT><P><FONT SIZE=2>前面我们已经把关於C++Builder撰写DLL所应注意到的事项介绍完了,现在我们来谈另一个重点- C++Builder和Visual C++ 的连结。若是你没有使用过Visual C++ 的话,可以将此部份略去。若是你在程式设计时必须使用到VisualC++ 的DLL或是必须提供DLL给VC++ 或是VB使用时,也许会带给你意想不到的收获。<BR></FONT><P><FONT SIZE=2>VC++ 使用C++Builder的DLL函式<BR></FONT><P><FONT SIZE=2>在Visual C++ 中使用C++Builder的DLL的函式方法和在C++Builder中使用大同小异,唯有几件事情必须要注意。<BR></FONT><P><FONT SIZE=2>(一)Visual C++ 的LIB档格式和C++Builder的LIB格式不同,因此你必须重新产生一个LIB。不过,可惜的是VC++ 在32位元的版本中并未提供IMPLIB.EXE函式(这点一直令许多人百思不解),因此你无法很方便地产生LIB档。解决方法有二:其一是在VC++内撰写一个同名称的空的DLL函式,令其产生LIB档,其二则是使用 LoadLibrary、GetProcAddress式的明确呼叫方式。</FONT><P><FONT SIZE=2>(二)使用前面提到的标准写法。<BR></FONT><P><FONT SIZE=2>C++Builder中使用VC++ 的DLL函式<BR></FONT><P><FONT SIZE=2>在C++Builder中使用VC++ 的DLL函式时要注意的是Microsoft在VisualC++ 中使用的特殊命名规则。在VC++ 中命名规则除了前面谈到的几项之外,它还使用了一个特殊的参数命名法,简言之,就是在函数名称後面加上参数的大小,这种命名方法会造成C++Builder,VB,Delphi使用的上的困扰。举例来说<BR></FONT><P><FONT SIZE=2>extern &quot;C&quot; _declspec(dllexport) void __stdcallShowImage(void);<BR></FONT><P><FONT SIZE=2>在VC++ 中产生的函式名称为ShowImage@0(其中0表示参数大小),而不是如在C++Builder中产生的ShowImage,这是VC++已知的问题,这个问题也造成了很多使用non-VC++ 的使用者的问题,解决之道是在该DLL的DEF档中加上以下的叙述</FONT><P><FONT SIZE=2>EXPORTS</FONT><P><FONT SIZE=2> ShowImage=ShowImage@0</FONT><P><FONT SIZE=2>如此便可以产生正确的函式名了,若是你不想修改DEF档,你也可以在程式中加入以下的连结指引</FONT><P><FONT SIZE=2>#pragma comment(linker,&quot;/exports:ShowImage=ShowImage@0&quot;)</FONT><P><FONT SIZE=2>假设你不确定其正确的名称,可以利用DumpBin或是TDump观察之。<BR></FONT><P><FONT SIZE=2>以上是针对VC++ 的程式设计的所作的额外说明。最後我们以一个VC++程式呼叫本单元的About Dialog DLL做为结束。<BR></FONT><P><IMG SRC="IMG00005.GIF"><BR><P><FONT SIZE=2>此程式的关键程式码如下:</FONT><P><FONT SIZE=2>void CVcusedllApp::OnAppAbout()</FONT><P><FONT SIZE=2>{</FONT><P><FONT SIZE=2>    void (*ShowImage)(void);</FONT><P><FONT SIZE=2>    HINSTANCE hInst;</FONT><P><FONT SIZE=2>    hInst = LoadLibrary(&quot;DLLSAMP2.DLL&quot;);</FONT><P><FONT SIZE=2>    (FARPROC &amp;)ShowImage=GetProcAddress(hInst,&quot;ShowImage&quot;);</FONT><P><FONT SIZE=2>    ShowImage();</FONT><P><FONT SIZE=2>    FreeLibrary(hInst);</FONT><P><FONT SIZE=2>}<BR></FONT></BODY></HTML>

⌨️ 快捷键说明

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