最近在看着《windows驱动开发技术详解》这本书,模仿着敲了第七章中的模拟文件读写部分。在Debug过程中,蓝屏了好多次并出现了各种奇葩的问题。在调了快两天之后,问题终于解决了!现在在这里一一再现遇到的问题和解决方法。
【在别人博客的评论中看到这么一句,"能让大家节约时间,就是写博客的目的之一",让我很有很深的感触,希望自己能以这个目的持续将博客进行到底,帮助更多的初学者。】
一 蓝屏问题
1. deviceName,symbolicName显示Memory read error问题
在Debug过程中,发现在调用派遣函数的时候,设备扩展结构体变量中的deviceName,symbolicName以及buffer,显示Memory read error。找了好久,终于发现问题所在。
1 //设备拓展结构体 2 typedef struct { 3 PDEVICE_OBJECT pDeviceObject; //指向自己 4 UNICODE_STRING deviceName; //设备名 5 UNICODE_STRING symbolicName; //符号链接 6 PDEVICE_OBJECT attachDevice; //下层设备对象 7 PWCHAR buffer; //缓冲区指针 8 LONG file_length; //文件长度 9 }DeviceExtension, *PDeviceExtension;
初始化deviceName,symbolicName子域的函数原型是
NTSTATUS CreateDevice(PDRIVER_OBJECT pDriverObject, UNICODE_STRING &linkName, UNICODE_STRING &devName);
CreateDevice的形参linkName和devName都是引用形参,并且该函数在DriverEntry中被调用用于创建新设备。
1 pDevExt->deviceName = devName; 2 pDevExt->symbolicName = linkName;
在之前书中看到过,直接进行初始化,不需要额外的空间也不需要销毁内存,它只是简单地拷贝Buffer指针到另一结构体而已。好了,问题便出现在这里了,当外部的linkName和devName变量被销毁,那么设备扩展中的deviceName和symbolicName引用的Buffer空间也就非法的。
恰好DriverEntry函数被我定义为#pragma INITCODE了,一次调用完空间便被系统自动回收了,定义在函数内部的设备名和符号名变量也自然就没了,自然在派遣函数中使用便会出现蓝屏。
解决的办法就是将DriverEntry函数定义为#pragma PAGECODE就好了。
和我遇到同样问题的还有这位网友:http://www.programlife.net/page_fault_in_nonpaged_area.html,在这篇文章的评论中有这么一句,"能让大家节约时间,就是写博客的目的之一",让我很有很深的感触,希望自己能以这个目的持续将博客进行到底,帮助更多的初学者。
二 字符串显示错误问题
1. 指针字节问题
这个问题出现在应用程序和驱动上面,也是调了好久才解决的。原来问题是如此简单!
我们知道WriteFile和ReadFile函数都是以字节为单位,写入和读取内容的,SetFilePointer函数也是以字节来偏移文件指针的。在驱动程序上,我的IRP_MJ_WRITE派遣函数中的写入缓冲区语句是这样写的
//将数据写入缓冲区 RtlCopyMemory(buffer + writeOffset, pIrp->AssociatedIrp.SystemBuffer, writeLength);
如果buffer类型是CHAR,那么是没问题的,因为它只占一个字节。但在我的程序里buffer的类型是WCHAR,占两个字节,所以当指向它的一个指针加上某个常数N,那么buffer实际上偏移了2N个字节。而传入的writeOffset偏移量是以一个字节为单位,所以它其实多偏移了writeOffset个字节,导致数据写入的位置错误,最后导致乱码出现。
正确的应该为
//将数据写入缓冲区 RtlCopyMemory((char*)buffer + writeOffset, pIrp->AssociatedIrp.SystemBuffer, writeLength);
2. 转义字符0问题
最后在应用程序里,读取到的数据需要在末尾加上转义字符0,才能最终打印出来。
if(ReadFile(hFile, buffer, fileLengthBytes,&fileLengthToRead, NULL)) { buffer[fileLengthBytes/2]= L'