??xml version="1.0" encoding="utf-8" standalone="yes"?>久久亚洲熟女cc98cm,久久强奷乱码老熟女网站,国产69精品久久久久久人妻精品http://www.shnenglu.com/amyvmiwei/category/5910.html 战胜自己是战胜一? zh-cnTue, 20 May 2008 00:05:01 GMTTue, 20 May 2008 00:05:01 GMT60DLL ?.def文g的?/title><link>http://www.shnenglu.com/amyvmiwei/archive/2008/01/02/40203.html</link><dc:creator>不?/dc:creator><author>不?/author><pubDate>Tue, 01 Jan 2008 21:37:00 GMT</pubDate><guid>http://www.shnenglu.com/amyvmiwei/archive/2008/01/02/40203.html</guid><wfw:comment>http://www.shnenglu.com/amyvmiwei/comments/40203.html</wfw:comment><comments>http://www.shnenglu.com/amyvmiwei/archive/2008/01/02/40203.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/amyvmiwei/comments/commentRss/40203.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/amyvmiwei/services/trackbacks/40203.html</trackback:ping><description><![CDATA[<p>           DLL中导出函数的声明有两U方式:一Uؓ在函数声明中加上__declspec(dllexport)Q这里不再D例说明;另外一U方式是采用模块定义(.def) 文g声明Q?def文g为链接器提供了有兌链接E序的导出、属性及其他斚w的信息?br><br><br>            首先创徏 一个DLLE序Q?cpp?br>int __stdcall Add(int numa, int numb)<br>{<br>       return (numa + numb);<br>}</p> <p>int __stdcall Sub(int numa, int numb)<br>{<br>        return (numa - numb);<br>}<br><br>             然后创徏一?def的文Ӟ在里面加?br></p> <p class=code>;DllTestDef.lib : 导出DLL函数<br>;作者:----<br>LIBRARY DllTestDef<br>EXPORTS <br>Add @ 1<br>Sub @ 2<br><br>           最后创Z个测试程序:.cpp文g如下Q?br>#include <iostream><br>#include <windows.h></p> <p class=code>using namespace std;</p> <p class=code>typedef int (__stdcall *FUN)(int, int);<br>HINSTANCE hInstance;<br>FUN   fun;</p> <p class=code>int main()<br>{<br>       hInstance = LoadLibrary("DLLTestDef.dll");<br>       if(!hInstance)<br>           cout << "Not Find this Dll" << endl;<br>       fun = (FUN)GetProcAddress(hInstance, MAKEINTRESOURCE(1));<br>       if (!fun)<br>       {<br>              cout << "not find this fun" << endl;<br>       }<br>       cout << fun(1, 2) << endl;<br>       FreeLibrary(hInstance);<br>       return 0;<br>}</p> <br><br>说明Q?br>.def文g的规则ؓQ?br><br>  (1)LIBRARY语句说明.def文g相应的DLLQ?br><br>  (2)EXPORTS语句后列导出函数的名U。可以在.def文g中的导出函数名后加@nQ表C导出函数的序号ؓnQ在q行函数调用Ӟq个序号发挥其作用Q;<br><br>  (3).def 文g中的注释由每个注释行开始处的分?(;) 指定Q且注释不能与语句共享一行?br><br> <img src ="http://www.shnenglu.com/amyvmiwei/aggbug/40203.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/amyvmiwei/" target="_blank">不?/a> 2008-01-02 05:37 <a href="http://www.shnenglu.com/amyvmiwei/archive/2008/01/02/40203.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DLL 调用方式http://www.shnenglu.com/amyvmiwei/archive/2008/01/02/40202.html不?/dc:creator>不?/author>Tue, 01 Jan 2008 21:24:00 GMThttp://www.shnenglu.com/amyvmiwei/archive/2008/01/02/40202.htmlhttp://www.shnenglu.com/amyvmiwei/comments/40202.htmlhttp://www.shnenglu.com/amyvmiwei/archive/2008/01/02/40202.html#Feedback0http://www.shnenglu.com/amyvmiwei/comments/commentRss/40202.htmlhttp://www.shnenglu.com/amyvmiwei/services/trackbacks/40202.htmlDLL(动态连接库)Q然而可以分为动态调用于静态调用。下面我分别举一个例子说说?br>
1Q动态调用:
首先Q在VC++6.0中创?Win32 Dynamic-link library工程创徏一个动态连接库工程Q?br>            在头文gTestDll.h中写下代?br>            extern "C" int __declspec(dllexport) add(int numa, int numb);
            在源文gTestDll.cpp中实现改函数Q?br>            int __declspec(dllexport) add(int numa, int numb)
            {
                      return numa + numb;
            }
其次Q创Z个测试程序,TestDemoQ创Z?cpp文gQ然后放下代码:
HINSTANCE hinstance;

typedef int (*lpAdd)(int a, int b);


lpAdd lpadd;

int main()
{
          hinstance = LoadLibrary("E:\\vc\\DLL\\TestDll\\Debug\\TestDll.dll");
          lpadd = (lpAdd)GetProcAddress(hinstance, "add");
          cout << "2 + 3 = " << lpadd(2, 3) << endl;
          FreeLibrary(hinstance);
          return 0;
}
而应用程序对本DLL的调用和对第2节静态链接库的调用却有较大差异,下面我们来逐一分析?/p>

  首先Q语句typedef int ( * lpAddFun)(int,int)定义了一个与add函数接受参数cd和返回值均相同的函数指针类型。随后,在main函数中定义了lpAddFun的实例addFunQ?/p>

    其次Q在函数main中定义了一个DLL HINSTANCE句柄实例hDllQ通过Win32 Api函数LoadLibrary动态加载了DLL模块q将DLL模块句柄赋给了hDllQ?/p>

   再次Q在函数main中通过Win32 Api函数GetProcAddress得到了所加蝲DLL模块中函数add的地址q赋l了addFun。经由函数指针addFunq行了对DLL中add函数的调用;

     最后,应用工程使用完DLL后,在函数main中通过Win32 Api函数FreeLibrary释放了已l加载的DLL模块?/p>

    通过q个单的例子Q我们获知DLL定义和调用的一般概念:

      (1)DLL中需以某U特定的方式声明导出函数Q或变量、类Q;

     (2)应用工程需以某U特定的方式调用DLL的导出函敎ͼ或变量、类Q?br>

2Q静态连接:
代码如下Q?br>#include <iostream>
using namespace std;

#pragma comment(lib,"Testlib.lib")

//.lib文g中仅仅是关于其对应DLL文g中函数的重定位信?br>extern "C" __declspec(dllimport) add(int x,int y);

int main()
{
         int result = add(2,3); 
         cout << result <Q endl;
         return 0;
}

         



]]>
(?论函数调用约?/title><link>http://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40169.html</link><dc:creator>不?/dc:creator><author>不?/author><pubDate>Tue, 01 Jan 2008 07:33:00 GMT</pubDate><guid>http://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40169.html</guid><wfw:comment>http://www.shnenglu.com/amyvmiwei/comments/40169.html</wfw:comment><comments>http://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.shnenglu.com/amyvmiwei/comments/commentRss/40169.html</wfw:commentRss><trackback:ping>http://www.shnenglu.com/amyvmiwei/services/trackbacks/40169.html</trackback:ping><description><![CDATA[假设我们有这L一个函敎ͼ <p align=center><strong>int function(int a,int b)</strong></p> <p>调用时只要用result = function(1,2)q样的方式就可以使用q个函数。但是,当高U语a被编译成计算机可以识别的机器码时Q有一个问题就凸现出来Q在CPU中,计算机没有办法知道一个函数调用需要多个、什么样的参敎ͼ也没有硬件可以保存这些参数。也是_计算Z知道怎么l这个函C递参敎ͼ传递参数的工作必须由函数调用者和函数本n来协调。ؓ此,计算机提供了一U被UCؓ栈的数据l构来支持参C递?/p> <p>栈是一U先q后出的数据l构Q栈有一个存储区、一个栈指针。栈指针指向堆栈中W一个可用的数据(被称为栈Ӟ。用户可以在栈顶上方向栈中加入数据,q个操作被称为压?Push)Q压栈以后,栈顶自动变成新加入数据项的位|,栈顶指针也随之修攏V用户也可以从堆栈中取走栈顶Q称为弹出栈(pop)Q弹出栈后,栈顶下的一个元素变成栈Ӟ栈顶指针随之修改?/p> <p>函数调用Ӟ调用者依ơ把参数压栈Q然后调用函敎ͼ函数被调用以后,在堆栈中取得数据Qƈq行计算。函数计结束以后,或者调用者、或者函数本w修改堆栈,使堆栈恢复原装?/p> <p>在参C递中Q有两个很重要的问题必须得到明确说明Q?/p> <ul> <li>当参C数多于一个时Q按照什么顺序把参数压入堆栈 <li>函数调用后,p来把堆栈恢复原装 </li> </ul> <p>在高U语a中,通过函数调用U定来说明这两个问题。常见的调用U定有: <ul> <li>stdcall <li>cdecl <li>fastcall <li>thiscall <li>naked call</li> </ul> <h2>stdcall调用U定</h2> <p>stdcall很多时候被UCؓpascal调用U定Q因为pascal是早期很常见的一U教学用计算机程序设计语aQ其语法严}Q用的函数调用U定是stdcall。在Microsoft C++pd的C/C++~译器中Q常常用PASCAL宏来声明q个调用U定Q类似的宏还有WINAPI和CALLBACK?/p> <p>stdcall调用U定声明的语法ؓ(以前文的那个函数ZQ:</p> <p>int __<font color=#0000ff><strong>stdcall</strong></font> function(int a,int b)</p> <p>stdcall的调用约定意味着Q?Q参C叛_左压入堆栈,2Q函数自w修改堆?3)函数名自动加前导的下划线Q后面紧跟一个@W号Q其后紧跟着参数的尺?/p> <p>以上q这个函Cؓ例,参数b首先被压栈,然后是参数aQ函数调用function(1,2)调用处翻译成汇编语言变成:</p> <pre><blockquote> push 2 W二个参数入? push 1 W一个参数入? call function 调用参数Q注意此时自动把cs:eip入栈 </blockquote> </pre> <p>而对于函数自w,则可以翻译ؓQ? <pre><blockquote> push ebp 保存ebp寄存器,该寄存器用来保存堆栈的栈顶指针Q可以在函数退出时恢复 mov ebp,esp 保存堆栈指针 mov eax,[ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a add eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b mov esp,ebp 恢复esp pop ebp ret 8 </blockquote></pre> <p>而在~译Ӟq个函数的名字被译成_function@8 <p>注意不同~译器会插入自己的汇~代码以提供~译的通用性,但是大体代码如此。其中在函数开始处保留esp到ebp中,在函数结束恢复是~译器常用的Ҏ?/p> <p>从函数调用看Q??依次被pushq堆栈,而在函数中又通过相对于ebp(卛_q函数时的堆栈指针)的偏U量存取参数。函数结束后Qret 8表示清理8个字节的堆栈Q函数自己恢复了堆栈?/p> <h2>cdecl调用U定</h2> <p>cdecl调用U定又称为C调用U定Q是C语言~省的调用约定,它的定义语法是: <pre><blockquote> int function (int a ,int b) //不加修饰是C调用U定 int __cdecl function(int a,int b)//明确指出C调用U定 </blockquote> </pre> <p>在写本文ӞZ我的意料Q发现cdecl调用U定的参数压栈顺序是和stdcall是一LQ参数首先由有向左压入堆栈。所不同的是Q函数本w不清理堆栈Q调用者负责清理堆栈。由于这U变化,C调用U定允许函数的参数的个数是不固定的,q也是C语言的一大特艌Ӏ对于前面的function函数Q用cdecl后的汇编码变成: <pre><blockquote> <strong>调用?/strong> push 1 push 2 call function add esp,8 <strong>注意Q这里调用者在恢复堆栈</strong> <strong>被调用函数_function?/strong> push ebp 保存ebp寄存器,该寄存器用来保存堆栈的栈顶指针Q可以在函数退出时恢复 mov ebp,esp 保存堆栈指针 mov eax,[ebp + 8H] 堆栈中ebp指向位置之前依次保存有ebp,cs:eip,a,b,ebp +8指向a add eax,[ebp + 0CH] 堆栈中ebp + 12处保存了b mov esp,ebp 恢复esp pop ebp ret <strong>注意Q这里没有修改堆?/strong> </blockquote> </pre> <p>MSDN中说Q该修饰自动在函数名前加前导的下划线Q因此函数名在符可中被记录为_functionQ但是我在编译时g没有看到q种变化?/p> <p>׃参数按照从右向左序压栈Q因此最开始的参数在最接近栈顶的位|,因此当采用不定个数参数时Q第一个参数在栈中的位|肯定能知道Q只要不定的参数个数能够ҎW一个后者后l的明确的参数确定下来,可以用不定参敎ͼ例如对于CRT中的sprintf函数Q定义ؓQ? <p><strong>int sprintf(char* buffer,const char* format,...)</strong></p> <p>׃所有的不定参数都可以通过format定Q因此用不定个数的参数是没有问题的?/p> <h2>fastcall</h2> <p>fastcall调用U定和stdcallcMQ它意味着Q? <ul> <li>函数的第一个和W二个DWORD参数Q或者尺寸更的Q通过ecx和edx传递,其他参数通过从右向左的顺序压? <li>被调用函数清理堆? <li>函数名修改规则同stdcall </li> </ul> <p>其声明语法ؓQint fastcall function(int a,int b)</p> <h2>thiscall</h2> <p>thiscall是唯一一个不能明指明的函数修饰Q因为thiscall不是关键字。它是C++cL员函数缺省的调用U定。由于成员函数调用还有一个this指针Q因此必ȝD处理,thiscall意味着Q? <ul> <li>参数从右向左入栈 <li>如果参数个数定Qthis指针通过ecx传递给被调用者;如果参数个数不确定,this指针在所有参数压栈后被压入堆栈? <li>对参CC定的Q调用者清理堆栈,否则函数自己清理堆栈 </li> </ul> <p>Z说明q个调用U定Q定义如下类和用代码: <pre>class A { public:    int function1(int a,int b);    int function2(int a,...); }; int A::function1 (int a,int b) {    return a+b; } #include <stdarg.h> int A::function2(int a,...) {    va_list ap;    va_start(ap,a);    int i;    int result = 0;    for(i = 0 ; i < a ; i ++)    {      result += va_arg(ap,int);    }    return result; } void callee() {    A a;    a.function1 (1,2);    a.function2(3,1,2,3); } </pre> <p>callee函数被翻译成汇编后就变成Q? <pre><blockquote> //函数function1调用 0401C1D push 2 00401C1F push 1 00401C21 lea ecx,[ebp-8] 00401C24 call function1 注意Q这里this没有被入? //函数function2调用 00401C29 push 3 00401C2B push 2 00401C2D push 1 00401C2F push 3 00401C31 lea eax,[ebp-8] q里引入this指针 00401C34 push eax 00401C35 call function2 00401C3A add esp,14h </blockquote></pre> <p>可见Q对于参C数固定情况下Q它cM于stdcallQ不定时则类似cdecl</p> <h2>naked call</h2> <p>q是一个很见的调用约定,一般程序设计者徏议不要用。编译器不会l这U函数增加初始化和清理代码,更特D的是,你不能用returnq回q回|只能用插入汇~返回结果。这一般用于实模式驱动E序设计Q假讑֮义一个求和的加法E序Q可以定义ؓQ? <pre>__declspec(naked) int add(int a,int b) { __asm mov eax,a __asm add eax,b __asm ret } </pre> <p>注意Q这个函数没有显式的returnq回|q回通过修改eax寄存器实玎ͼ而且q退出函数的ret指o都必L式插入。上面代码被译成汇~以后变成: <pre><blockquote> mov eax,[ebp+8] add eax,[ebp+12] ret 8 </blockquote> </pre> <p>注意q个修饰是和__stdcall及cdecll合使用的,前面是它和cdecll合使用的代码,对于和stdcalll合的代码,则变成: <pre>__declspec(naked) int __stdcall function(int a,int b) { __asm mov eax,a __asm add eax,b __asm ret 8 //注意后面? } </pre> <p>至于q种函数被调用,则和普通的cdecl及stdcall调用函数一致?/p> <h2>函数调用U定D的常见问?/h2> <p>如果定义的约定和使用的约定不一_则将D堆栈被破坏,D严重问题Q下面是两种常见的问题: <ol> <li>函数原型声明和函C定义不一? <li>DLL导入函数时声明了不同的函数约?</li> </ol> <p>以后者ؓ例,假设我们在dllU声明了一U函CؓQ? <pre>__declspec(dllexport) int func(int a,int b);//注意Q这里没有stdcallQ用的是cdecl</pre> <p>使用时代码ؓQ? <pre> typedef int (*WINAPI DLLFUNC)func(int a,int b); hLib = LoadLibrary(...); DLLFUNC func = (DLLFUNC)GetProcAddress(...)//q里修改了调用约? result = func(1,2);//D错误 </pre> <p>׃调用者没有理解WINAPI的含义错误的增加了这个修饎ͼ上述代码必然D堆栈被破坏,MFC在编译时插入的checkesp函数告诉你Q堆栈被破坏了?br><br><br><br><br> <table height=849 cellSpacing=0 cellPadding=0 width=780 align=center border=0> <tbody> <tr> <td class=text width=739 height=64> <div align=left> <p>DLL中调用约定和名称修饰- -<br><br>调用U定QCalling ConventionQ是指在E序设计语言中ؓ了实现函数调用而徏立的一U协议。这U协议规定了该语a的函C的参C送方式、参数是否可变和p来处理堆栈等问题。不同的语言定义了不同的调用U定?/p> <p>在C++中,Z允许操作W重载和函数重蝲QC++~译器往往按照某种规则改写每一个入口点的符号名Q以便允许同一个名字(h不同的参数类型或者是不同的作用域Q有多个用法Q而不会打破现有的ZC的链接器。这Ҏ术通常被称为名U改~(Name ManglingQ或者名UC饎ͼName DecorationQ。许多C++~译器厂商选择了自q名称修饰Ҏ?/p> <p>因此Qؓ了其它语言~写的模块(如Visual Basic应用E序、Pascal或Fortran的应用程序等Q可以调用C/C++~写的DLL的函敎ͼ必须使用正确的调用约定来导出函数Qƈ且不要让~译器对要导出的函数q行M名称修饰?br>1Q调用约定(Calling ConventionQ?br>调用U定用来处理军_函数参数传送时入栈和出栈的序Q由调用者还是被调用者把参数弹出栈)Q以及编译器用来识别函数名称的名UC饰约定等问题。在Microsoft VC++ 6.0中定义了下面几种调用U定Q我们将l合汇编语言来一一分析它们Q?br>1、__cdecl<br>__cdecl是C/C++和MFCE序默认使用的调用约定,也可以在函数声明时加上__cdecl关键字来手工指定。采用__cdeclU定Ӟ函数参数按照从右到左的顺序入栈,q且p用函数者把参数弹出栈以清理堆栈。因此,实现可变参数的函数只能用该调用U定。由于每一个用__cdeclU定的函数都要包含清理堆栈的代码Q所以生的可执行文件大会比较大。__cdecl可以写成_cdecl?/p> <p>下面通过一个具体实例来分析__cdeclU定Q?/p> <p>在VC++中新Z个Win32 Console工程Q命名ؓcdecl。其代码如下Q?/p> <p>int __cdecl Add(int a, int b); //函数声明</p> <p>void main()<br>{<br>Add(1,2); //函数调用<br>}</p> <p>int __cdecl Add(int a, int b) //函数实现<br>{<br>return (a + b);<br>}</p> <p>函数调用处反汇编代码如下Q?/p> <p>;Add(1,2);<br>push 2 ;参数从右到左入栈Q先压入2<br>push 1 ;压入1<br>call @ILT+0(Add) (00401005) ;调用函数实现<br>add esp,8 ;由函数调用清?br>2、__stdcall<br>__stdcall调用U定用于调用Win32 API函数。采用__stdcalU定Ӟ函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,函数参数个数固定。由于函C本n知道传进来的参数个数Q因此被调用的函数可以在q回前用一条ret n指o直接清理传递参数的堆栈。__stdcall可以写成_stdcall?/p> <p>q是那个例子Q将__cdeclU定换成__stdcallQ?/p> <p>int __stdcall Add(int a, int b)<br>{<br>return (a + b);<br>}</p> <p>函数调用处反汇编代码Q?br><br>; Add(1,2);<br>push 2 ;参数从右到左入栈Q先压入2<br>push 1 ;压入1<br>call @ILT+10(Add) (0040100f) ;调用函数实现</p> <p>函数实现部分的反汇编代码Q?/p> <p>;int __stdcall Add(int a, int b)<br>push ebp<br>mov ebp,esp<br>sub esp,40h<br>push ebx<br>push esi<br>push edi<br>lea edi,[ebp-40h]<br>mov ecx,10h<br>mov eax,0CCCCCCCCh<br>rep stos dword ptr [edi]<br>;return (a + b);<br>mov eax,dword ptr [ebp+8]<br>add eax,dword ptr [ebp+0Ch]<br>pop edi<br>pop esi<br>pop ebx<br>mov esp,ebp<br>pop ebp<br>ret 8 ;清栈<br>3、__fastcall<br>__fastcallU定用于Ҏ能要求非常高的场合。__fastcallU定函数的从左边开始的两个大小不大?个字节(DWORDQ的参数分别攑֜ECX和EDX寄存器,其余的参C旧自叛_左压栈传送,被调用的函数在返回前清理传送参数的堆栈。__fastcall可以写成_fastcall?/p> <p>依旧是相cM的例子,此时函数调用U定为__fastcallQ函数参C数增?个:</p> <p>int __fastcall Add(int a, double b, int c, int d)<br>{<br>return (a + b + c + d);<br>}</p> <p>函数调用部分的汇~代码:</p> <p>;Add(1, 2, 3, 4);<br>push 4 ;后两个参C叛_左入栈,先压?<br>mov edx,3 ;intcd?攑օedx<br>push 40000000h ;压入doublecd?<br>push 0<br>mov ecx,1 ;intcd?攑օecx<br>call @ILT+0(Add) (00401005) ;调用函数实现</p> <p>函数实现部分的反汇编代码Q?br><br>; int __fastcall Add(int a, double b, int c, int d)<br>push ebp<br>mov ebp,esp<br>sub esp,48h<br>push ebx<br>push esi<br>push edi<br>push ecx<br>lea edi,[ebp-48h]<br>mov ecx,12h<br>mov eax,0CCCCCCCCh<br>rep stos dword ptr [edi]<br>pop ecx<br>mov dword ptr [ebp-8],edx<br>mov dword ptr [ebp-4],ecx<br>;return (a + b + c + d);<br>fild dword ptr [ebp-4]<br>fadd qword ptr [ebp+8]<br>fiadd dword ptr [ebp-8]<br>fiadd dword ptr [ebp+10h]<br>call __ftol (004011b8)<br>pop edi<br>pop esi<br>pop ebx<br>mov esp,ebp<br>pop ebp<br>ret 0Ch ;清栈</p> <p>关键字__cdecl、__stdcall和__fastcall可以直接加在要输出的函数前,也可以在~译环境的Setting...->C/C++->Code Generationw择。它们对应的命o行参数分别ؓ/Gd?Gz?Gr。缺省状态ؓ/GdQ即__cdecl。当加在输出函数前的关键字与~译环境中的选择不同Ӟ直接加在输出函数前的关键字有效?br>4、thiscall<br>thiscall调用U定是C++中的非静态类成员函数的默认调用约定。thiscall只能被编译器使用Q没有相应的关键字,因此不能被程序员指定。采用thiscallU定Ӟ函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,只是另外通过ECX寄存器传送一个额外的参数Qthis指针?/p> <p>q次的例子中定义一个类Qƈ在类中定义一个成员函敎ͼ代码如下Q?/p> <p>class CSum<br>{<br>public:<br>int Add(int a, int b)<br>{<br>return (a + b);<br>}<br>};</p> <p>void main()<br>{ <br>CSum sum;<br>sum.Add(1, 2);<br>}</p> <p>函数调用部分汇编代码Q?/p> <p>;CSum sum;<br>;sum.Add(1, 2);<br>push 2 ;参数从右到左入栈Q先压入2<br>push 1 ;压入1<br>lea ecx,[ebp-4] ;ecx存放了this指针<br>call @ILT+5(CSum::Add) (0040100a) ;调用函数实现</p> <p>函数实现部分汇编代码Q?/p> <p>;int Add(int a, int b)<br>push ebp<br>mov ebp,esp<br>sub esp,44h ;多用了一?bytes的空间用于存放this指针<br>push ebx<br>push esi<br>push edi<br>push ecx<br>lea edi,[ebp-44h]<br>mov ecx,11h<br>mov eax,0CCCCCCCCh<br>rep stos dword ptr [edi]<br>pop ecx<br>mov dword ptr [ebp-4],ecx<br>;return (a + b);<br>mov eax,dword ptr [ebp+8]<br>add eax,dword ptr [ebp+0Ch]<br>pop edi<br>pop esi<br>pop ebx<br>mov esp,ebp<br>pop ebp<br>ret 8 ;清栈<br>5、naked属?br>采用上面所q的四种调用U定的函数在q入函数Ӟ~译器会产生代码来保存ESI、EDI、EBX、EBP寄存器中的|退出函数时则生代码恢复这些寄存器的内宏V对于定义了naked属性的函数Q编译器不会自动产生q样的代码,需要你手工使用内嵌汇编来控制函数实C的堆栈管理。由于naked属性ƈ不是cd修饰W,故必d__declspec共同使用。下面的q段代码定义了一个用了naked属性的函数及其实现Q?/p> <p>__declspec ( naked ) func()<br>{<br>int i;<br>int j;<br><br>_asm<br>{<br>push ebp<br>mov ebp, esp<br>sub esp, __LOCAL_SIZE<br>}<br><br>_asm<br>{<br>mov esp, ebp<br>pop ebp<br>ret<br>}<br>}</p> <p>naked属性与本节关系不大Q具体请参考MSDN?br>6、WINAPI<br>q有一个值得一提的是WINAPI宏,它可以被译成适当的调用约定以供函C用。该宏定义于windef.h之中。下面是在windef.h中的部分内容Q?/p> <p>#define CDECL _cdecl<br>#define WINAPI CDECL<br>#define CALLBACK __stdcall<br>#define WINAPI __stdcall<br>#define APIENTRY WINAPI</p> <p>由此可见QWINAPI、CALLBACK、APIENTRY{宏的作用?br></p> 2Q名UC饎ͼName DecorationQ?br>C或C++函数在内部(~译和链接)通过修饰名(Decoration NameQ识别。函数的修饰名是~译器在~译函数定义或者原型时生成的字W串。编译器在创?obj文g时对函数名称q行修饰。有些情况下使用函数的修饰名是必要的Q如在模块定义文仉头指定输出C++重蝲函数、构造函数、析构函敎ͼ又如在汇~代码里调用C或C++函数{? <p>在VC++中,函数修饰名由~译cdQC或C++Q、函数名、类名、调用约定、返回类型、参数等多种因素共同军_。下面分C~译、C++~译Q非cL员函敎ͼ和C++cd其成员函数编译三U情况说明:<br>1、C~译时函数名UC?br>当函C用__cdecl调用U定Ӟ~译器仅在原函数名前加上一个下划线前缀Q格式ؓ_functionname。例如:函数int __cdecl Add(int a, int b)Q输出后为:_Add?/p> <p>当函C用__stdcall调用U定Ӟ~译器在原函数名前加上一个下划线前缀Q后面加上一个@W号和函数参数的字节敎ͼ格式为_functionname@number。例如:函数int __stdcall Add(int a, int b)Q输出后为:_Add@8?/p> <p>当函数是用__fastcall调用U定Ӟ~译器在原函数名前加上一个@W号Q后面是加一个@W号和函数参数的字节敎ͼ格式为@functionname@number。例如:函数int __fastcall Add(int a, int b)Q输出后为:@Add@8?/p> <p>以上改变均不会改变原函数名中的字W大写?br>2、C++~译时函敎ͼ非类成员函数Q名UC?br>当函C用__cdecl调用U定Ӟ~译器进行以下工作:</p> <p>1Q以?标识函数名的开始,后跟函数名;<br>2Q函数名后面以@@YA标识开始,后跟q回值和参数表;<br>3Q当函数的返回值或者参CC++cL关的时候,q回值和参数表以下列代号表示Q?br>BQconst<br>DQchar<br>EQunsigned char<br>FQshort<br>GQunsigned short<br>HQint<br>IQunsigned int<br>JQlong<br>KQunsigned long<br>MQfloat<br>NQdouble<br>_NQbool<br>PAQ指针(*Q后面的代号表明指针cdQ如果相同类型的指针q箋出现Q以0<br>代替Q一?代表一ơ重复)<br>PBQconst指针<br>AAQ引用(&Q?br>ABQconst引用<br>UQ类或结构体<br>VQInterfaceQ接口)<br>W4Qenum<br>XQvoid<br>4、@@YA标识之后紧跟的是该函数的q回值类型,其后依次为参数的数据cdQ指针标识在其所指数据类型前。当函数的返回值或者参CC++cL关的时候,其处理符合本条规则,否则按照5?规则处理Q?br>5、当函数q回gؓ某个cL带有const性质的类的时候,q回值的命名为:?A/?B+V+cd+@@Q不带加P。当函数q回gؓ某个cȝ指针/引用或者带有const性质的类的指?引用的时候,q回值的命名为:PA/AA或者PB/AB+V+cd+@@Q不带加PQ?br>6、函数参Cؓ某个cȝ时候,q且该参数所使用的类曄出现q的话(也就是与函数q回值所使用的类相同或者与前一个参C用的cȝ同)Q则该参数类型格式ؓQV+1+@Q不带加P。如果该参数所使用的类没有出现q的话,则该参数cd格式为:V+cd+@@Q不带加P。函数参Cؓ某个cȝ指针/引用或者带有const性质指针/引用的时候,则该参数cd格式是在上述格式的基上在V前面加上代表指针/引用cd或者带有const性质指针/引用cd的标识符QPA/AA或PB/ABQ;<br>7、参数表后以@Z标识整个名字的结束,如果该函数无参数Q则以Z标识l束?/p> <p>当函C用__stdcall调用U定Ӟ~译器所做工作的规则同上面的__cdecl调用U定Q只是参数表的开始标识由上面的@@YA变ؓ@@YG?/p> <p>当函C用__fastcall调用U定Ӟ~译器所做工作的规则同上面的__cdecl调用U定Q只是参数表的开始标识由上面的@@YA变ؓ@@YI?br>3、C++~译cd其成员函数时名称修饰<br>对于导出的C++c,仅能使用__cdecl调用U定。在~译器编译过E中Q编译器会对C++c进行处理。如Qclass __declspec(dllexport) MyClass会被处理为class MyClass & MyClass::operator=(class MyClass const &)。在C++~译器对C++c进行名UC饰的时候,~译器进行以下工作:</p> <p>1Q以?标识函数名的开始,后跟?4+cdQ?br>2Q类名后面跟@@QAE标识Q对于导出类来说q是固定的;<br>3Q@@QAE后面跟AAV0@ABV0@Q即引用cd标识WAA+V+0Q重复的cȝ标识W)+@Q不带加P和const性质的引用AB+V+ 0Q重复的cȝ标识W)+@Q不带加PQ?br>4Q最后以@Z标识整个名字的结束?/p> <p>对于导出的C++cM的成员函敎ͼ非构造函数和析构函数Q,可以使用不同的调用约定。当导出的C++cM的成员函C用__cdecl调用U定Ӟ~译器进行以下工作:</p> <p>1Q以?标识函数名的开始,后跟函数?@+cdQ不带加PQ?br>2Q之后以@@QAE标识开始,后跟q回值和参数表;<br>3Q当函数的返回值或者参CC++cL关的时候,q回值和参数表以下列代号表示Q?br>BQconst<br>DQchar<br>EQunsigned char<br>FQshort<br>GQunsigned short<br>HQint<br>IQunsigned int<br>JQlong<br>KQunsigned long<br>MQfloat<br>NQdouble<br>_NQbool<br>PAQ指针(*Q后面的代号表明指针cdQ如果相同类型的指针q箋出现Q以0<br>代替Q一?代表一ơ重复)<br>PBQconst指针<br>AAQ引用(&Q?br>ABQconst引用<br>UQ类或结构体<br>VQInterfaceQ接口)<br>W4Qenum<br>XQvoid<br>4、@@QAE标识之后紧跟的是该函数的q回值类型,其后依次为参数的数据cdQ指针标识在其所指数据类型前。当函数的返回值或者参CC++cL关的时候,其处理符合本条规则,否则按照5?规则处理Q?br>5、当函数q回gؓ当前cL带有const性质的当前类的时候,q回值的命名为:?A?B+V+1+@@Q不带加P。当函数q回gؓ当前cȝ指针/引用或者带有const性质的当前类的指?引用的时候,q回值的命名为:PA/AA或PB/AB+V+1+@@Q不带加PQ?br>6、当函数q回gؓ某个cL带有const性质的类的时候,q回值的命名为:?A/?B+V+cd+@@Q不带加P。当函数q回gؓ某个cȝ指针/引用或者带有const性质的类的指?引用的时候,q回值的命名为:PA/AA或者PB/AB+V+cd+@@Q不带加PQ?br>7、函数参Cؓ某个cȝ时候,q且该参数所使用的类曄出现q的话(也就是当前要导出的类、与函数q回值所使用的类相同或者与前一个参C用的cȝ同的c)Q则该参数类型格式ؓQV+1+@Q不带加P。如果该参数所使用的类不是当前要导出的cȝ话,则该参数cd格式为:V+cd+@@Q不带加P。函数参Cؓ某个cȝ指针/引用或者带有const性质指针/引用的时候,则该参数cd格式是在上述格式的基上在V前面加上代表指针/引用cd或者带有const性质指针/引用cd的标识符QPA/AA或PB/ABQ;<br>8、参数表后以@Z标识整个名字的结束,如果该函数无参数Q则以Z标识l束?/p> <p>当函C用__stdcall调用U定Ӟ~译器所做工作的规则同上面的__cdecl调用U定Q只是参数表的开始标识由上面的@@YA变ؓ@@YG?/p> <p>当函C用__fastcall调用U定Ӟ~译器所做工作的规则同上面的__cdecl调用U定Q只是参数表的开始标识由上面的@@YA变ؓ@@YI?br>4、C++~译导出数据时名UC?br>对于导出的数据,仅用__cdecl调用U定。在C++~译器对C++c进行名UC饰的时候,~译器进行以下工作:</p> <p>1Q以?标识数据的开始,后跟数据名;<br>2Q数据名后面以@@3标识开始,后跟数据cdQ?br>3Q当数据cd与C++cL关的时候,数据cd以下列代可C:<br>BQconst<br>DQchar<br>EQunsigned char<br>FQshort<br>GQunsigned short<br>HQint<br>IQunsigned int<br>JQlong<br>KQunsigned long<br>MQfloat<br>NQdouble<br>_NQbool<br>PAQ指针(*Q后面的代号表明指针cdQ如果相同类型的指针q箋出现Q以0<br>代替Q一?代表一ơ重复)<br>PBQconst指针<br>AAQ引用(&Q?br>ABQconst引用<br>UQ类或结构体<br>VQInterfaceQ接口)<br>W4Qenum<br>XQvoid<br>4Q如果数据类型是某个cȝ时候,数据cd的命名ؓQV+cd+@@Q不带加P。当数据cd为当前类的指?引用或者带有const性质的当前类的指?引用的时候,数据cd的命名ؓQPA/AA或PB/AB+V+cd+@@Q不带加PQ?br>5Q最后,如果数据cd是const性质Q则修饰名以Bl尾。如果数据类型是非const性质Q则修饰名以Al尾?br></p> </div> </td> </tr> </tbody> </table> </p> <img src ="http://www.shnenglu.com/amyvmiwei/aggbug/40169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.shnenglu.com/amyvmiwei/" target="_blank">不?/a> 2008-01-01 15:33 <a href="http://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Lib 静态链接库http://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40164.html不?/dc:creator>不?/author>Tue, 01 Jan 2008 07:02:00 GMThttp://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40164.htmlhttp://www.shnenglu.com/amyvmiwei/comments/40164.htmlhttp://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40164.html#Feedback0http://www.shnenglu.com/amyvmiwei/comments/commentRss/40164.htmlhttp://www.shnenglu.com/amyvmiwei/services/trackbacks/40164.html
// TestLib01.h
#ifndef TESTLIB_H
#define TESTLIB_H
//声明函数为C~译Q连接方式的外部函数
extern "C" int Add(int numa, int numb);
#endif


//TestLib01.cpp
#incldue "TestLib01.h"
int Add(int numa, int numb)
{
      return (numa + numb);
}

~译得到一?lib的静态库Q把.lib文g以及头文件TestLib01.h拯到用户工E目录下面(两个文g都放在工E目录下面,不用吧lib文g攑֜debug下面Q?br>
下面来一个用L序来试一?br>//TestLibProject
//Test.cpp

#include <iostream>
#include <"TestLib01.h">

using namespace std;

#pragma comment(lib,"TestLib01.lib")
int main()
{
      cout << Add(1, 4) << endl; 
      return 0;
}

代码?pragma comment( lib , TestLib01.lib" )的意思是指本文g生成?obj文g应与TestLib01.lib一赯接?/strong>



]]>
DLLQ动态链接库Q编E?Q概论)http://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40157.html不?/dc:creator>不?/author>Tue, 01 Jan 2008 06:35:00 GMThttp://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40157.htmlhttp://www.shnenglu.com/amyvmiwei/comments/40157.htmlhttp://www.shnenglu.com/amyvmiwei/archive/2008/01/01/40157.html#Feedback0http://www.shnenglu.com/amyvmiwei/comments/commentRss/40157.htmlhttp://www.shnenglu.com/amyvmiwei/services/trackbacks/40157.html

dll是现在常见的文gQ它集成了程序的很多功能在里面。一般情况下Q它不能直接被执行,常见的用方法是用其他的*.exe调用其执行,以其内部功能表现出来。还?.ocx文g也与之类|也就是h们常说的com
1.?br>
         Windows API中所有的函数都包含在dll中,其中?/span>3个最重要?/span>DLL?br>        (1)   Kernel32.dll
        它包含那些用于管理内存、进E和U程的函敎ͼ例如CreateThread函数Q?br>        (2)   User32.dll
       它包含那些用于执行用L面Q?/span>(如窗口的创徏和消息的传?/span>)的函敎ͼ例如CreateWindow函数Q?br>        (3)   GDI32.dll
       它包含那些用于画囑֒昄文本的函数?br>2.      静态库和动态库
(1)   静态库
           函数和数据被~译q一个二q制文g(通常扩展名ؓ.LIB)。在使用静态库的情况下Q在~译链接可执行文件时Q链接器从库中复制这些函数和数据q把它们和应用程序的其他模块l合h创徏最l的可执行文?/span>(.Exe文g).当发布品时Q只需要发布这个可执行文gQƈ不需要发布被使用的静态库?br>(2)   动态库
        在用动态库的时候,往往提供两个文gQ一个引入库(.lib)文g和一?/span>DLL(.dll)文g。虽然引入库的后~名也?/span>”lib”Q但是动态库的引入库文g和静态库文g有着本质上的区别Q对一?/span>DLL来说Q其引入库文?/span>(.lib)包含?/span>DLL导出的函数和变量的符号名Q?/span>.dll文g包含?/span>DLL实际的函数和数据。在使用动态库的情况下Q在~译链接可执行文件时Q只需要链接该DLL的引入库文gQ该DLL中的函数代码和数据ƈ不复制到可执行文件中Q直到可执行E序q行Ӟ才去加蝲所需?/span>DLLQ将?/span>DLL映射到进E的地址I间外,然后讉KDLL中导出的函数。这Ӟ发布产品Ӟ除了发布可执行文件以外,同时q要发布该程序将要调用的动态链接库?br>3.      在导出库头文件中的标准写法:
#ifdef LIBDAQ_EXPORTS
#define LIBDAQ_API __declspec(dllexport)
#else
#define LIBDAQ_API __declspec(dllimport)
#endif
     该头文件添加到某客户代码中Ӟ会自动展开。如果客户代码没有定?/span>LIBDAQ_EXPORTSQ那?/span>LIBDAQ_EXPORTS会被定义?/span>__declspec(dllimport)表示?/span>LIBDAQ_EXPORTS头的函数都是从该DLL中导入的?br>4.      名字改编?/span>”extern “C””
           C++~译器在生成DLLӞ会对导出的函数进行名字改~,q且不同的编译器使用的改变规则不一P因此改编后的名字会不一栗这P如果利用不同的编译器分别生成DLL和访问该DLL的客L代码E序的话Q后者在讉K?/span>DLL的导出函数时会出现问题。ؓ了实现通用性,需要加上限定符Q?/span>extern “C”?br>           但是利用限定W?/span>extern “C”可以解决C++?/span>C之间怺调用时函数命名的问题Q但是这U方法有一个缺P是不能用于导出一个类的成员函敎ͼ只能用于导出全局函数?br>5.      昄加蝲方式加蝲DLL
           使用动态方式来加蝲动态链接库Ӟ需要用?/span>LoadLibrary函数。该函数的作用就是将指定的可执行模块映射到调用进E的地址I间。调用原型ؓQ?br>HMODULE LoadLibrary(LPCTSTR lpFileName);
           LoadLibrary函数不仅可以加蝲DLLQ还可以加蝲可执行模?/span>(Exe)。当加蝲可执行模块时Q主要是Z讉K该模块内的一些资源,例如对话框资源、位图资源或图标资源{?/span>LoadLibrary函数有一个字W串cd(LPCTSTR)的参敎ͼ该参数指定了可执行模块的名称Q既可以是一?/span>dll文gQ也可以是一?/span>exe文g。如果调用成功,LoadLibrary函数返回所加蝲的那个模块的句柄。返回类?/span>HMODULE?/span>HINSTANCE可以通用?br>           当加载到动态链接库模块的句柄后Q接下来p惛_法获取该动态链接库中导出函数的地址Q这可以通过调用GetProcAddress函数来实现。该函数用来获取DLL导出函数的地址Q其原型声明如下所C:
FARPROC GetProcAddress(HMODULE hModule, LPCSTR lpProcName);
参数hModuleQ指定动态链接库模块的句柄,?/span>LoadLibrary函数的返回倹{?br>参数lpProcNameQ一个指向常量的字符指针Q指?/span>DLL导出函数的名字或函数的序受如果是序号Q则序号必须在低位字节中Q高位字节必L0?br>如果调用成功Q?/span>GetProcAddress函数返回指定导出函数的地址Q否则返?/span>NULL?br>例如Q?br>HINSTANCE hInst;
hInst = LoadLibrary(“DllTest.dll”);
typedef int (*ADDPROC)(int a, int b);
ADDPROC add = (ADDPROC)GetProcAddress(hInst, “add”);
if (!add)
print(“Failure”);
else
process next events
FreeLibrary(hInst);
调用语法Q?br>BOOL FreeLibrary(HMODULE hModule);
6.      加蝲DLL的两U方式优~点Q?br>       采用动态加载方式,那么可以在需要时才加?/span>DLLQ而隐式链接方式实现v来比较简单,在编写客L代码时就可以把链接工作做好,在程序中可以随时调用DLL导出的函数。但是如果程序需要访问十多个DLLӞ如果都采用隐式链接方式加载它们的话,那么在该E序启动Ӟq些DLL都需要被加蝲到内存中Qƈ映射到调用进E的地址I间Q这样将加大E序的启动时间。而且一般来_在程序运行过E中只是在某个条件满x才需要访问某?/span>DLL中的某个函数Q其它情况下都不需要访问这?/span>DLL中的函数。但是这时所有的DLL都已l被加蝲到内存中Q资源浪Ҏ比较严重的。这个时候就需要采用显C加载的方式来访?/span>DLLQ在需要时才加载所需?/span>DLL。也是说在需要时才被加蝲到内存中Qƈ被映到调用q程的地址控g中。需要说明的是,隐式链接方式讉KDLLӞ在程序启动时也是通过LoadLibrary函数加蝲该进E需要的动态链接库的?br>7.      DllMain函数
           如果提供?/span>DllMain函数(该函数是可以选择存在?/span>)Q那么在此函C不要q行太复杂的调用。因为在加蝲该动态链接库Ӟ可能q有一些核心动态链接库没有被加载。例?/span>Use32.dll?/span>GDI32.dll。我们自q写的DLL会比较靠前地被加载?br>

8.     。def的应?br>         在不同语a间调用约定的不同Q甚x的编译器对名字的修饰也不是完全相同,所以,要DLL能广泛应用加上一?def文g?br> 


]]>
97ȾþƵƷ99| Ʒþþ99| þ޾Ʒ˳ۺ| þ99Ʒ鶹| ŷþһ| þþþ޾Ʒվ| ˾þþƷ鶹| պƷþĻ| ݹƷþ| ޾Ʒþþþþ| AVպƷþþþþ| ƷȾþëƬ| ҹƷþþþþ99| þü¶| ŷ츾BBBþþ| þǿdŮ| ƷþþƷ| ľþþþר| ˺ݺۺϾþ| ŷ777Ʒþþþ| ޵һƷƷþ| þþþĻɫ | 뾫Ʒþþ| þĻ| ҹƷƬþ| þZYZԴվĶ| þþþAVվ | һaƬþëƬ| 69Ʒþþþ9999APGF| Ʒþþþþþö| Ʒþþþþһ| ֻƬþøպ| 99þˬ޾ƷŮ| Ʒþþþ| ٸþĻһ | ݺݺɫۺϾþ| ձvaĻþ| Ʒþþþþù| ŷ龫Ʒþþþþþþžž| ޺ݺۺϾþ| ѾþþƷѾѾ|