VC (_CRT_DEBUGGER_HOOK(_CRT_DEBUGGER_GSFAILURE) 问题记录

VC内存溢出一例 –- 调用约定不一致 (_CRT_DEBUGGER_HOOK(_CRT_DEBUGGER_GSFAILURE)
最近在写一个程序,调用了多个DLL,每个DLL代码都支持多线程,Debug的模式下基本调通了,但是在Release模式下,程序因为内存溢出而崩溃,中断在gs_report.c文件的298行位置(_CRT_DEBUGGER_HOOK(_CRT_DEBUGGER_GSFAILURE),
由于问题是出自某个DLL模块中,并且是多线程的,并且出现中断的断点无法回溯,很难直接定位到是哪个DLL模块的问题。在将一个一个模块被调用的代码注释后,大概确定了可能出问题的模块,以及可能的函数。由于是Release版本的问题,无法设置断点,只好用输出到Output窗口的方式调试,想进一步确定出现内存溢出的代码段。折腾了很长时间,一直没找到问题根源。

网上搜索了一下,有人是Debug版本有这样的内存溢出错误,Release版本没有,是因为字符串填充的时候超过的申请的长度,和本例的情况不一样。开始我也觉得可能是哪个DLL库字符串操作问题,仔细检查了一些,没有发现问题。

后来在测试一个作为参数传输到某个DLL中函数的字符串的时候,发现此字符串在被调用函数之后内容被改变了,而理论上被调用的函数是不改变此字符串的内容。调试了一下,发现此字符串在被调用的函数最后返回之前还是正常的,函数返回之后就不对了,也就是说函数退出之后的出栈操作有问题,不是按入栈完全相反的动作操作的。产生这个问题的原因有可能是函数的声明不对,也就是在DLL中的声明和在主程序中的声明不一致。检查了一下参数和返回值类型,是完全一致的。那会是哪不一样呢?

DLL中的声明: EXPORTDLL unsigned int MyFunction(char * appname);
     主函数中的声明: typedef unsigned int (WINAPI *lpMyFunction)(char* appname);
     在VC中,EXPORTDLL定义为: #define EXPORTDLL extern “C” __declspec (dlexport); WINAPI定义为:#define WINAPI __stdcall ;回想起以前解决过的一些问题,在给回调函数传函数指针时,就为是用__stdcall还是__cdcall折腾过,所以估计这里可能是函数约定调用不一致导致的,正好和前面函数参数值被非法改变有关系。WINAPI这个调用约定是没法改的,那就修改DLL的函数。将DLL项目 Configuration Properties --> C/C++ --> Advanced --> Calling Convention设置从默认的__cdecl(/Gd)改为 __stdcall(/Gz),重新编译,重新运行,一切正常。

C++函数调用方式(_stdcall, _pascal, _cdecl...)总结
http://blog.sina.com.cn/s/blog_57065b99010006mo.html
__stdcall:
_stdcall 调用约定相当于16位动态库中经常使用的PASCAL调用约定。在32位的VC++5.0中PASCAL调用约定不再被支持(实际上它已被定义为__stdcall。除了__pascal外,__fortran和__syscall也不被支持),取而代之的是__stdcall调用约定。两者实质上是一致的,即函数的参数自右向左通过栈传递,被调用的函数在返回前清理传送参数的内存栈,但不同的是函数名的修饰部分(关于函数名的修饰部分在后面将详细说明)。
_stdcall是Pascal程序的缺省调用方式,通常用于Win32 Api中,函数采用从右到左的压栈方式,自己在退出时清空堆栈。VC将函数编译后会在函数名前面加上下划线前缀,在函数名后加上"@"和参数的字节数。

_cdecl:
_cdecl c调用约定, 按从右至左的顺序压参数入栈,由调用者把参数弹出栈。对于传送参数的内存栈是由调用者来维护的(正因为如此,实现可变参数的函数只能使用该调用约定)。另外,在函数名修饰约定方面也有所不同。
_cdecl是C和C++程序的缺省调用方式。每一个调用它的函数都包含清空堆栈的代码,所以产生的可执行文件大小会比调用_stdcall函数的大。函数采用从右到左的压栈方式。VC将函数编译后会在函数名前面加上下划线前缀。是MFC缺省调用约定。

__fastcall:
__fastcall调用约定是"人"如其名,它的主要特点就是快,因为它是通过寄存器来传送参数的(实际上,它用ECX和EDX传送前两个双字(DWORD)或更小的参数,剩下的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的内存栈),在函数名修饰约定方面,它和前两者均不同。
_fastcall方式的函数采用寄存器传递参数,VC将函数编译后会在函数名前面加上"@"前缀,在函数名后加上"@"和参数的字节数。

thiscall:
thiscall仅仅应用于"C++"成员函数。this指针存放于CX寄存器,参数从右到左压。thiscall不是关键词,因此不能被程序员指定。

naked call:
采用1-4的调用约定时,如果必要的话,进入函数时编译器会产生代码来保存ESI,EDI,EBX,EBP寄存器,退出函数时则产生代码恢复这些寄存器的内容。
naked call不产生这样的代码。naked call不是类型修饰符,故必须和_declspec共同使用。
另附:
关键字 __stdcall、__cdecl和__fastcall可以直接加在要输出的函数前,也可以在编译环境的Setting...\C/C++ \Code Generation项选择。当加在输出函数前的关键字与编译环境中的选择不同时,直接加在输出函数前的关键字有效。它们对应的命令行参数分别为/Gz、/Gd和/Gr。缺省状态为/Gd,即__cdecl。
要完全模仿PASCAL调用约定首先必须使用__stdcall调用约定,至于函数名修饰约定,可以通过其它方法模仿。还有一个值得一提的是WINAPI宏,Windows.h支持该宏,它可以将出函数翻译成适当的调用约定,在WIN32中,它被定义为__stdcall。使用WINAPI宏可以创建自己的APIs。

http://blog.csdn.net/jiangxinyu/article/details/7844414

VC内存溢出一例 –- 调用约定不一致 (_CRT_DEBUGGER_HOOK(_CRT_DEBUGGER_GSFAILURE)的更多相关文章

  1. VC内存溢出一例 –- 调用约定不一致

    这个是网查的跟我在做图像分割realse相反的情况: 最近在写一个程序,调用了多个DLL,每个DLL代码都支持多线程,Debug的模式下基本调通了,但是在Release模式下,程序因为内存溢出而崩溃, ...

  2. JSP页面导致tomcat内存溢出一例

    今天发现一个奇怪的问题,一个tomcat应用,里面只有一个单纯的jsp页面,而且这个jsp页面没有任何java代码——想用这个jsp页面配合tomcat完成一个性能验证.但是用jmeter压测了几分钟 ...

  3. Visual Studio (VC) Win32 程序由于数据大,内存溢出怎么办?

    Visual Studio (VC) 内编写的Win32 程序由于数据大,内存溢出,即使转移到64位系统也不行.在国外网站上找到了答案. 原来,只需在project->property中的Lin ...

  4. Hibernate内存溢出分析一例

    公司业务系统在进行压力测试时,压测24小时后系统发生内存溢出.经过分析读dump文件,发现org.hibernate.stat.StatisticsImpl类的hashmap类型的变量存储了大量数据( ...

  5. X86调用约定 calling convention

    http://zh.wikipedia.org/wiki/X86%E8%B0%83%E7%94%A8%E7%BA%A6%E5%AE%9A 这里描述了在x86芯片架构上的调用约定(calling con ...

  6. CPP-基础:有关调用约定

    在C语言中,假设咱们有这样的一个函数:int function(int a,int b) 调历时只有用result = function(1,2)的方法就能利用这个函数.然而,当高档语言被编译成计算机 ...

  7. OutOfMemory相关问题(内存溢出异常OOM)

    OutOfMemory(内存溢出异常OOM) java.lang.OutOfMemoryError :Thrown when the Java Virtual Machine cannot alloc ...

  8. C++调用约定和名字约定

    C++调用约定和名字约定 转自http://www.cppblog.com/mzty/archive/2007/04/20/22349.html 调用约定:__cdecl __fastcall与 __ ...

  9. PHP内存溢出解决方案

    一.内存溢出解决方案 在做数据统计分析时,经常会遇到大数组,可能会发生内存溢出,这里分享一下我的解决方案.还是用例子来说明这个问题,如下: 假定日志中存放的记录数为500000条,那么解决方案如下: ...

随机推荐

  1. C++----练习

    1.完成一个C++实现的加法程序: #include<iostream> int main() { std::cout<<"可以输入两个加数,本程序完成加法...&q ...

  2. wordpress教程之WP_Query()类

    WP_Query的使用方法 在讲WP_Query之前我们要先区分一下两个名词: WP_Query是WordPress自带的的一个用于处理复杂请求的类(这里的请求的内容不仅包括文章,还可能是页面,用户, ...

  3. css案例学习之div a实现立体菜单

    效果 代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w ...

  4. web前端设计:JQuery MINI UI

    JQuery MINIUI 个人感觉用起来很爽,所以在此记录之,以后开发过程可能作为备选项.它能缩短开发时间,减少代码量,使开发者更专注于业务和服务端,轻松实现界面开发,带来绝佳的用户体验.在线下载地 ...

  5. Bring Your Charts to Life with HTML5 Canvas and JavaScript

    Bring Your Charts to Life with HTML5 Canvas and JavaScript Bring Your Charts to Life with HTML5 Canv ...

  6. poj 2411 新写法

    别以为我在刷水题.... 今天做了场srm,500pt想到了是dp但是无从下手,但是看了rng_58的神代码后顿觉海阔天空啊(盯着看了一个下午),相比于一年前的写法,真的是不忍直视啊, TC真是个好地 ...

  7. hdu 5441 Travel(并查集)

    Problem Description Jack likes to travel around the world, but he doesn’t like to wait. Now, he is t ...

  8. 使用Comparable接口的小例子

    代码: public class Student implements Comparable<Student> { private int id; private String name; ...

  9. AngularJS 的一些坑

    UI的闪烁 Angular的自动数据绑定功能是亮点,然而,他的另一面是:在Angular初始化之前,页面中可能会给用户呈现出没有解析的表达式.当DOM准备就绪,Angular计算并替换相应的值.这样就 ...

  10. linux查看用户登录信息-w命令

    NAME w - Show who is logged on and what they are doing. SYNOPSIS w - [husfV] [user] DESCRIPTION w di ...