当你运行程式得到了一个AV(Access Violation)错误的时候,这意味着你的程式正在试图访问一块不再有效的内存,请注意我所提到的“不再 ”有效。大多数的情况下,出现这个错误要么是因为你试图访问一块已经被释放的内存,要么是想使用一个还未创建对象的指针。
幸运的是:Win32的内存体系在不同的进程(Process)间使用了独立的地址空间。所以我们可以不必担心会访问到其他的进程中的地址空间而造成破坏,(在Win 16下就有这种潜在的危险).这也就意味着我们能够正确的利用错误对话框中的信息。
当我们得到一个AV错误对话框的时候,将会出现例如:Av at ddress ????的字样。这个时候把这个地址写下来(如图为:0X4006A620),回到程序中并且打开调试用的CPU窗口,右键选择"Goto Address",你将会发现出错的信息结构。
当然CPU窗口都是以汇编语言(Assembly)出现的.你可能对此不大熟悉。于是你就可以在窗口中滚动看看到底是哪个函数(funtion)调用了它。这样你就可以在这个地方设置断点了(breakpoint).
不幸的是,不是每个错误都是这么容易的捕捉到的。相对而言。指针问题是很难调试的。这里有个常规的法则就是:在删除指针指向的对象以后。请将它置为NULL 。因此在调用的时候你可以先看看这个指针是否为NULL,如果是NULL,你可以在这个地方输出一些调试信息以方便你在发生AV错误的时候能精确的找到这个地方。
在VC中调试程序时,Output窗口有时会出现“First-chance exception in xxx.exe...”这样的信息。一般来说,这是由于程序中发生了异常,被VC捕获而产生的输出。
在调试器中运行程序时,如果程序产生异常,调试器会首先获得通知(即First-chance exception),而后此异常由产生它的程序负责捕获。如果程序没有捕获此异常,那么调试器会再次被通知(即Second-chance exception,Last-chance exception),并结束程序。
通常见到的“First-chance exception”一般是“0xC0000005: Access Violation”,“0xC00000FD: Stack Overflow”等,这些都说明程序中有缺陷,需要修正。
但是也有一些属于正常的情况,例如“First-chance exception in xxx.exe (KERNEL32.DLL): 0xE06D7363: Microsoft C++ Exception”。Windows 操作系统中广泛使用了结构化异常(SEH)来处理特殊情况,许多和底层打交道的API都靠SEH来处理可能发生的意外。并且,这些API中都有捕获SEH 的代码,产生的异常不会对程序造成影响。但是由于上面提到的“First-chance exception,Second-chance exception”机制,VC仍然会有输出,但是我们完全可以忽略。如果你实在不喜欢这些输出信息,那你就必须禁用对特定异常的“First-
chance exception”捕获。
在调试状态下,打开debug菜单下的exception对话框,把access violation异常的action改成stop always,这时再出现异常,调试器就会弹出一个对话框报告这个异常,并在异常发生处停下来,而不仅仅是在output窗口中报告first- chance exception,这有助于你定位异常位置。
在内部进行调试的时候发现,是调用了new操作符的缘故。
很不理解,在dll中调用new操作符会出现这么一个问题,网络上搜索,在微软msdn论坛(具体地址:http://forums.msdn.microsoft.com/zh-CN/vclanguage/thread/951a04be-6b52-4617-b396-9c35e3755759/)上搜索到相关问题。
于是按照说明,将dll的生成方式修改为"共享mfc方式"
project properties -> general -> Project Defaults->Use of MFC
To set the properties "Use MFC In A Shared DLL "
另外,在网上也发现另外一篇,console方面的, 解决办法:使用c runtime library的动态链接版本(在你的console工程中设置忽略所有的默认链接库,然后链接到msvcrt.lib就可以了)(具体地址:http://www.poptool.net/software/p606/A60674501.shtml)
调试,发现问题解决
在使用C++ Builder的调用VC的DLL的时候遇到了'Access violation at address xxx'的错误,以下是测试程序:
C++ Builder代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
//--------------------------------------------------------------------------- #include
<stdio.h> #include
<windows.h> #pragma
hdrstop //--------------------------------------------------------------------------- int __stdcall
(*add)( int ,
int ); int __stdcall
(*sub)( int ,
int ); #pragma
argsused int main( int argc,
char *
argv[]) { HMODULE
handle = LoadLibrary( "AddFunc.dll" ); add
= GetProcAddress(handle, "Add" ); sub
= GetProcAddress(handle, "Sub" ); printf( "2+3=%d\n" ,
add(2, 3)); printf( "100-30=%d\n" ,
sub(100, 30)); FreeLibrary(handle); getchar(); return 0; } //--------------------------------------------------------------------------- |
1
|
|
1
|
|
1
|
DLL代码: |
1
|
.h: |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//
The following ifdef block is the standard way of creating macros which make exporting
//
from a DLL simpler. All files within this DLL are compiled with the ADDFUNC_EXPORTS //
symbol defined on the command line. this symbol should not be defined on any project //
that uses this DLL. This way any other project whose source files include this file see
//
ADDFUNC_API functions as being imported from a DLL, whereas this DLL sees symbols //
defined with this macro as being exported. #ifdef
ADDFUNC_EXPORTS #define
ADDFUNC_API extern "C" __declspec(dllexport) #else #define
ADDFUNC_API extern "C" __declspec(dllimport) #endif ADDFUNC_API
int Add( int a,
int b); ADDFUNC_API
int Sub( int a,
int b); |
.c:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//
AddFunc.cpp : Defines the exported functions for the DLL application. // #include
"stdafx.h" #include
"AddFunc.h" ADDFUNC_API
int Add( int a,
int b) { return (a
+ b); } ADDFUNC_API
int Sub( int a,
int b) { return (a
- b); } |
1
|
|
于是调用就出现了该异常,经过查阅资料得知VC的DLL在export的时候要加上__stdcall的修饰,强制使用WINAPI的导出方式,对两个函数修改为(.h, .c):
1
2
3
|
ADDFUNC_API
int __stdcall
Add( int a,
int b); ADDFUNC_API
int __stdcall
Sub( int a,
int b); |
结果发现导出的函数发生了变化_Add@4, __Sub@4,解决方法是在VC工程中加入一个AddFunc.def,内容如下:
1
2
3
4
5
|
LIBRARY
"AddFunc" EXPORTS Add
@ 1 Sub
@ 2 |
至此,解决问题。