返回状态值
检查内存的可用性
异常处理try-except
异常处理try-finally
断言
课时:45
NTSTATUS
typedef LONG NTSTATUS;
NT_SUCCESS
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#define STATUS_SUCCESS ((NTSTATUS)0x00000000L) // ntsubauth
//
// Values are 32 bit values layed out as follows:
//
// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---+-+-+-----------------------+-------------------------------+
// |Sev|C|R| Facility | Code |
// +---+-+-+-----------------------+-------------------------------+
//
// where
//
// Sev - is the severity code
//
// 00 - Success
// 01 - Informational
// 10 - Warning
// 11 - Error
//
// C - is the Customer code flag
//
// R - is a reserved bit
//
// Facility - is the facility code
//
// Code - is the facility's status code
//
//
// Define the facility codes
R(Reserved)保留位
C (Customer) 客户位
Sev(Severity) 重要位 共2个二进制位 00表示成功 01表示信息 10表示警告 11表示错误
检测内存可用性
ProbeForRead
VOID ProbeForRead( __in PVOID Address, __in SIZE_T Length, __in ULONG Alignment);
ProbeForWrite
VOID ProbeForWrite( __in PVOID Address, __in SIZE_T Length, __in ULONG Alignment);
void Memaccess_Test()
{
KdPrint(("测试内存可用否\n"));
int i, *pi=NULL;
__try
{
i=*pi;
}
__except(1)
{
KdPrint(("测试内存 不可用\n"));
return;
}
}
void ProbeForRead_Test()
{
KdPrint(("测试内存可用否\n"));
int *pi=NULL;
__try
{ ProbeForRead(pi,4,1);
//i=*pi;
}
__except(1)
{
KdPrint(("ProbeForRead 测试内存 不可用\n"));
return;
}
}
结构化异常处理try except
__try
{
//这里如果出错 发出异常
}
__except(filter_Value)
{
}
filter_Value是以下三种值之一
EXCEPTION_CONTINUE_SEARCH 0 转向上一层异常处理
EXCEPTION_CONTINUE_EXECUTION -1 重复执行错误指令
EXCEPTION_EXECUTE_HANDLER 1 忽略该错误 转到 __except 块处理
结构化异常处理try finally
__try
{
}
__finally
{
}
断言
ASSERT
#if DBG
#define ASSERT( exp ) \
((!(exp)) ? \
(RtlAssert( #exp, __FILE__, __LINE__, NULL ),FALSE) : \
TRUE)
#else
#define ASSERT( exp ) ((void) 0)
RtlAssert
侧效(宏使用问题)
#define add(a,b) a=a+b; b=a+b;
if (??) add(a,b)
==================================
笔记:
以36课代码为例讲解代码中的异常处理,首先说一下NTSTATUS类型就是一个整型,但在驱动中我们都这样定义, 这是一个关于返回值的判断.看一下此类型在DDK中的定义,这个32位的长度一共分为四个区段(教案中),那么在高两位中代表着成功/失败/警告/错误,所以判断NT_SUCCESS这个宏就知道否成功,简单的判断就是看其是否>0.第三位用来自定义的位,中间这些位可能是保存了,右边16位是错误信息.
ProbeForRead/ProbeForWrite函数用来检测内存读/写,参数1是起始地址,参数2是检查大小,对齐方式一般选1,这个函数并不是通过返回值来判断,而是会发出一个异常.当然我们也可以直接写代码用来判断内存的读写权限.
结构化异常处理一共有两种方式,一种是try…except的,这也是C++的处理方式,分为三种异常处理代码.
将刚才讲到的异常处理写入mini_ddk.h头文件里,在代码中建立一个空指针,这样就没有读写权限,然后下边代码进行读写就会出错,此时会出现异常,然后我们进行处理,当然老师这里写了两种访问方式.在37.h文件头里.
void Memaccess_Test()
{
KdPrint(("\n测试内存可用否\n"));
int i=3, *pi=NULL;
__try
{
*pi=i;
}
__except(1)
{
KdPrint(("测试内存不可用\n"));
return;
}
}
void ProbeForRead_Test()
{
KdPrint(("nProbeForRead 测试内存可用否\n"));
int *pi=NULL;
__try
{ ProbeForRead(pi,14,4);
//ProbeForWrite(pi,4,1);
// KdPrint(("\n ProbeForRead end *pi=%d \n",*pi));
//i=*pi;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("\n ProbeForRead 测试内存不可用\n"));
return;
}
return;
}
在虚拟机里测试一下可读/可写.可是只执行了前面的代码.修改一下,但是程序并未出现异常.感觉ProbeForRead这个函数不太准确,再试一下ProbeFowWrite,
另外一种异常处理是try…finally,这个无论什么情况都执行代码
void finally_test()
{ int *p=NULL;
int i=1;
__try
{
*p=i;
}
__finally
{
KdPrint(("\n __finally 写入出错\n"));
}
__try
{
i=3;
}
__finally
{
KdPrint(("\n __finally i=3; 写入出错\n"));
}
return;
}
结果蓝屏了,因为它没有处理异常.我们一般都会忽略异常.先说一下断言RtlAssert,它也是一个调试技巧.
void ASSERT_test()
{ int *p=NULL;
int i=1;
ASSERT(p!=NULL); //也可以手动强制抛出断言ASSERT(FALSE);
return;
}
在WINDBG里看一下信息,断言的效果就是会提示一句话,按I忽略继续执行,也就是说可以用断言做一个判断,方便我们定位出错的地点.
最后看一下侧效,比如我们定义了一个宏
#define add(a,b) { a=a+b; b=a+b;}
void test1() //建立侧效函数
{int a=1,b=1; //定义变量
if (false) {add(a,b);} //此句为假就变成将此句注释掉不执行了
KdPrint(("\n a=%d,b=%d \n",a,b));//输出信息
return;
}
结果A的值为1,B的值均为2,那么第一句的a=a+b没有执行,而b=a+b执行了,那么实际的编译情况是
If (false) a=a+b; //这句未执行
B=a+b; //这句执行了
这就是宏的结果,因为宏只是进行了简单的替换,所以加上括号的执行结果就正确了.
最后老师说这些知识以后可能会用到,这里只是进行一下了解. |