★卢培培★ ── 欢迎光临卢培培(goodname008)的BLOG

人生真正的快乐,多在贫家茅舍,少在富室红楼。

用户操作
[即时聊天] [发私信] [加为好友]
卢培培ID:goodname008
79389次访问,排名1256(1)好友0人,关注者0
goodname008的文章
原创 22 篇
翻译 0 篇
转载 5 篇
评论 286 篇
卢培培的公告


只有干不成事的人
没有干不成的事


代码下载说明:请将代码下载地址的链接复制到浏览器的地址栏,按下回车即可正常下载。

最近评论
jingang123gz:在C# 中怎么屏蔽WIN键啊?????
kennylyj:我在做类似的东西的时候遇到一个问题:
由于需要将一些16位的DOS程序的输出导出,因此使用了管道,但直接CreateProcess这些程序是不行的,必须通过ComSpec这个环境变量得到命令行程序的路径并把要运行的DOS程序的路径及相关参数作为命令行程序的参数才行,或者是先运行cmd/command,然后通过标准写端口写入要启动的程序的参数
而这样做却导致系统的虚拟机程序……
mycaibo:原来可以下载呀,我还自己写了一遍,不过说KBDLLHOOKSTRUCT没有定义,也不知道是为什么,谢谢哈
jarcyju:大哥下载不了呀,麻烦发一份给我,谢谢啦
zhuyc0808@gmail.com
wuzhongyi:我认为以上的方案不好,看样子你也是做开发的。先不考虑硬盘本身的分区特点及如何得到更快的访问速度,

现在如果c:\为系统盘,而也是你常用的。那样XP安装完以后要多大?2G左右吧,那么虚拟的内存又会占用1G多,你再装点软件什么的。VS2005+msdn即使你不装在C盘,照样撑死你。
文章分类
收藏
    相册
    其它图片
    文章用图
    A.我的软件
    1.注册表大师 v2.0
    2.窗口间谍
    3.图易贴 v1.1
    B.强烈推荐
    1.VB打造超酷个性化菜单[一]
    2.VB打造超酷个性化菜单[二]
    3.VB打造超酷个性化菜单[三]
    4.剖析VC++函数调用约定
    C.有脚印的地方
    CSDN--中国软件网
    微软中国社区
    微软中文新闻组
    D.开放源代码
    1.鼠标感应器
    2.VB自绘菜单类
    3.你想要钱吗?
    E.友情链接
    1. 凝尘
    2. 羽毛羽毛
    3. 泗水寻芳
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 剖析VC++函数调用约定收藏

    新一篇: 利用LineDDA函数做简单动画 | 旧一篇: Win32 函数原型

    Visual C/C++ 的编译器提供了几种函数调用约定,了解这些函数调用约定的含义及它们之间的区别可以帮助我们更好地调试程序。在这篇文章里,我就和大家共同探讨一些关于函数调用约定的内容。

         Visual C/C++ 的编译器支持如下的函数调用约定:

     

    关键字

    清理堆栈

    参数入栈顺序

    函数名称修饰(C)

    __cdecl

    调用函数

    à

    _函数名

    __stdcall

    被调用函数

    à

    _函数名@数字

    __fastcall

    被调用函数

    à

    @函数名@数字

    thiscall(非关键字)

    被调用函数

    à

    /

     

         上面这张表只简单地列出了每种函数调用约定的特点,既然这篇文章题目的前两个字是剖析,哪能这么容易就完事!?下面就对上面这四种函数调用约定逐个剖析

         一、__cdecl函数调用约定

         这是CC++ 程序默认的函数调用约定,参数按从右到左的顺序压入堆栈,由调用函数负责清理堆栈,把参数弹出栈。也正是因为用来传送参数的堆栈是由调用函数维护的,所以实现可变参数的函数只能使用这种函数调用约定。因为每一个调用它的函数都要包含清理堆栈的代码,所以编译后的可执行文件的大小要比调用__stdcall函数的大。使用这种函数调用约定时,修饰后的函数名只是在原函数名前加上了一个_(下划线),并且不改变函数的大小写。对于__cdecl,我们一般不特别指出,因为它是CC++ 程序默认的函数调用约定,所以只有将编译选项设置成/Gzstdcall)或/Grfastcall)时,我们才有必要在函数名前显式地指出采用这种函数调用约定。下面举一个例子:

     

    int __cdecl Sumcdecl(int a, int b, int c)

    {

    int i = 1000;

    short j = 2000;

    int k = 3000;

    int rEBP = 0;

    int value = 0;

     

    // ...

     

    return (a + b + c);

     

    }

     

    调用:Sumcdecl(10, 20, 30);

     

     

    函数体及调用语句如上所示,修饰后的函数名为_Sumcdecl,堆栈和寄存器状态如下(一行表示4个字节):

     

    0

     value

    0

     rEBP

    3000

     k

    2000

     j

    1000

     i

     

     <---------EBP

     

     

    10

     a

    20

     b

    30

     c

     

     

    [未使用]

     ECX

    [未使用]

     EDX

     

         口说无凭,代码能说明一切,下面的程序乃Win32 console application.exe)是也:

     

    #include "iostream.h"

    #include "stdio.h"

     

    extern "C" __declspec(dllexport) int __cdecl Sumcdecl(int a, int b, int c)

    {

    // 声明局部变量

         int i = 1000;

         short j = 2000;

         int k = 3000;

         int rEBP = 0;

         int value = 0;

     

         // 显示局部变量的地址

         cout << "局部变量的地址:" << endl;

         cout << &value << "    <-----------value" << endl;

         cout << &rEBP << "    <-----------rEBP" << endl;

         cout << &k << "    <-----------k" << endl;

         cout << &j << "    <-----------j" << endl;

         cout << &i << "    <-----------i" << endl;

     

         // 显示寄存器的值

         cout << "寄存器:" << endl;

         __asm mov rEBP, ebp;

         printf("0x%08X    <-----------EBP\n", rEBP);

     

         // 显示函数参数的地址

         cout << "函数参数的地址:" << endl;

         cout << &a << "    <-----------a" << endl;

         cout << &b << "    <-----------b" << endl;

         cout << &c << "    <-----------c" << endl;

     

         // 通过 EBP 寄存器获得堆栈中的数据并显示

         cout << "通过EBP获取堆栈中的数据:" << endl;

         __asm mov eax, [ebp - 4];

         __asm mov value, eax;

         cout << "i: " << value << endl;

     

         __asm mov eax, [ebp - 8];

         __asm mov value, eax;

         cout << "j: " << (short)value << endl;

     

         __asm mov eax, [ebp - 12];

         __asm mov value, eax;

         cout << "k: " << value << endl;

     

         __asm mov eax, [ebp + 8];

         __asm mov value, eax;

         cout << "a: " << value << endl;

     

         __asm mov eax, [ebp + 12];

         __asm mov value, eax;

         cout << "b: " << value << endl;

     

         __asm mov eax, [ebp + 16];

         __asm mov value, eax;

         cout << "c: " << value << endl;

     

         // 返回

         return (a + b + c);

     

    }

     

    // 主函数

    int main(int argc, char* argv[])

    {

     

         Sumcdecl(10, 20, 30);

     

         return 0;

     

    }

     

     

         在我的机器上,运行结果如下:

     

    局部变量的地址:

    0x0012FF0C    <-----------value

    0x0012FF10    <-----------rEBP

    0x0012FF14    <-----------k

    0x0012FF18    <-----------j

    0x0012FF1C    <-----------i

    寄存器:

    0x0012FF20    <-----------EBP

    函数参数的地址:

    0x0012FF28    <-----------a

    0x0012FF2C    <-----------b

    0x0012FF30    <-----------c

    通过EBP获取堆栈中的数据:

    i: 1000

    j: 2000

    k: 3000

    a: 10

    b: 20

    c: 30

     

         函数声明部分的extern “C”表示连接规范(Linkage Specification)采用C,而不是C++,不加extern “C”的情况我会在后面统一讨论。__declspec(dllexport)表示将该函数导出,将生成.lib文件,以便我们验证函数名是怎样修饰的。关于修饰后的函数名,我们可以使用VC98\bin目录下的dumpbin工具来验证:

     

         dumpbin /exports 文件名>

     

         输出结果如下:

     

    File Type: LIBRARY

     

         Exports

     

           ordinal    name

     

                      _Sumcdecl

     

      Summary

     

              C9 .debug$S

              14 .idata$2

              14 .idata$3

               4 .idata$4

               4 .idata$5

               E .idata$6

     

         二、__stdcall函数调用约定

         __stdcall函数调用约定通常用于Win32 API函数,参数按从右到左的顺序压入堆栈,由被调用函数负责清理堆栈,把参数弹出栈。在windows.h中包含了windef.h,而windef.h中定义了一个WINAPI宏:#define WINAPI __stdcall,呵呵,应该心知肚明了。使用这种函数调用约定时,修饰后的函数名在原函数名前加上了一个_(下划线),并且在原函数名后加上“@数字,当然也不改变函数的大小写,@ 后面的数字表示参数所占的字节数,这里有一点要注意的,不足32位(4字节)的参数将在参数传递时被扩充到32位。下面举一个例子:

     

    int __stdcall Sumstdcall(int a, int b, int c)

    {

    int i = 1000;

    short j = 2000;

    int k = 3000;

    int rEBP = 0;

    int value = 0;

     

    // ...

     

    return (a + b + c);

     

    }

     

    调用:Sumstdcall(10, 20, 30);

     

     

         函数体及调用语句如上所示,修饰后的函数名为_Sumstdcall@12int32位的,占4个字节,332位的变量,共12个字节。堆栈和寄存器状态如下(一行表示4个字节):

     

    0

     value

    0

     rEBP

    3000

     k

    2000

     j

    1000

     i

     

     <---------EBP

     

     

    10

     a

    20

     b

    30

     c

     

     

    [未使用]

     ECX

    [未使用]

     EDX

     

     

         仍然以代码说明: