免費論壇 繁體 | 簡體
Sclub交友聊天~加入聊天室當版主
分享
返回列表 发帖

驱动下的异常处理

返回状态值
  检查内存的可用性
  异常处理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;                                //这句执行了
这就是宏的结果,因为宏只是进行了简单的替换,所以加上括号的执行结果就正确了.

最后老师说这些知识以后可能会用到,这里只是进行一下了解.

返回列表