代码改变世界

一发不可收拾的学习

2004-09-09 22:12  FantasySoft  阅读(972)  评论(4编辑  收藏  举报

        昨天还在感叹自己面对Windows开发就像一个傻瓜,今天随便找了个突破口,期待能够将自己掌握的知识联系起来,结果就是一发不可收拾,越看越迷糊,越想越凌乱。不管怎么样,先将今天看到的新面孔记个流水帐吧。
        首先,突破口就是WinMain函数的修饰符WINAPI。从WINDEF.H这个头文件中,我得知WINAPI实质上就是__stdcall
。那么什么是__stdcall呢?
        __stdcall属于Microsoft定义的函数修饰符,除了她之外,还有__fastcall、__cdecl和thiscall定义了函数被调用的方式,其中,thiscall不是关键字。以前关注的都是函数如何去定义,而函数调用都是很平常的事情,从来都没有去思考在函数调用的过程中,发生了什么事情。以下文字摘自MSDN,对于函数调用的过程中发生的事情做了描述:   
        All arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair. Larger structures are returned in the EAX register as pointers to hidden return structures. Parameters are pushed onto the stack from right to left.
        The compiler generates prolog and epilog code to save and restore the ESI, EDI, EBX, and EBP registers, if they are used in the function.
        译文:所有的实参在传递的时候都会被扩展为32位,返回值也是如此,并且会存储至EAX寄存器中(天啊,寄存器都来了)。如果返回的是8字节的结构体,那么她就会被存在EDX:EAX寄存器对中。如果是体积更大的结构体返回的话,那么EAX存放的将是一个指向结构体所在地址的指针。型参按照从右至左的顺序被推入栈中。
        在ESI、EDI、EBX和EBP寄存器当中,如果她们在程序中被使用到的话,编译器会生成序言(prolog)和结语(epilog)代码来存储和恢复这些被使用的寄存器的内容。

        再次回到刚刚提到的几种定义函数调用的修饰符上来。大家先看如下的函数定义和调用(以下代码和图片均来自MSDN):

void    calltype MyFunc( char c, short s, int i, double f );

void    MyFunc( char c, short s, int i, double
 f )
{
    
}


MyFunc (
'x'1281922.7183);

        对于以上的函数定义,calltype用__fastcall、__cdecl等替代。那么,在函数调用的过程中,不同的修饰符下的内存结构如下图:
       
         calltype = __cdecl

       
        calltype = __stdcall 和 thiscall
        
        
        calltype = __fastcall

        最后,由prolog和epilog又引出了Naked Function Calls的概念。所谓Nake Function Calls就是由自己来写prolog和epilog,而不是由编译器来生成。而这里面还有很多的学问,譬如Nake Function Calls的限制,prolog和epilog应该如何写等等。以下就先给出一个Naked Function的例子吧(来自MSDN):

__declspec(naked) int __fastcall  power(int i, int j) 
{
    
/* calculates i^j, assumes that j >= 0 */

    
/* prolog */
    __asm 
{
        push   ebp
        mov      ebp, esp
        sub      esp, __LOCAL_SIZE
       
// store ECX and EDX into stack locations allocated for i and j
        mov   i, ecx
        mov   j, edx
    }

      
    
{
        
int k=1// return value
        while (j-- > 0) k *= i;
        __asm 
{ mov eax, k };
    }


    
/* epilog */
    __asm  
    
{
        mov      esp, ebp
        pop      ebp
        ret
    }

}


int main()
{
}

        噢,我已经有点眩晕了。一天吃不成胖子,脑袋也装不下那么多的东西,就到这里吧。还是不要冷落WinMain,毕竟这才是主体,__stdcall只是一个修饰符而已啊。Oh, My God.