阅读:1518回复:2
斑竹,高手们,帮帮小弟阿~~~~~一个问题,弄了好几天了~~~~~~
小弟偶第一次做驱动,重启蓝屏无数次了,装系统也有N次了。要不是实在没办法(身边的参考资料几乎只从网上下,我们学校的图书馆放假了,书店也放假了),偶是不会随便来bbs哭的:)。
各位高手,你们先不要骂我,等调通了偶竖着耳朵让你们骂,OK?? 是这样的,偶在一个NDIS中间层驱动下面想要访问磁盘文件,这里就是想从磁盘文件里面读数据,该文件名是c:\\passthru.txt,里面的数据是一些字符,偶随便写了些,比如,gggggghhhhh。 我是这样来编码的: 在DriverEntry里面: ... MyDriverCreateFile(NULL,&m_ghFileHandle,m_gFileName); ... 这是个自定义函数,用于打开一个文件,参数m_ghFileHandle是个全局变量,m_gFileName是"\\\\??\\\\c:\\\\passthru.txt",这个函数返回的文件句柄是176,偶把它输出来看了的。 在PtReiceive里面:(这个函数工作在IRQL=DISPATCH_LEVEL) ... char readbuffer[50];//这个是用来存放读出数据的缓冲区 char * piChar; ... MyDriverReadFile(readbuffer,20,m_ghFileHandle);//自定义函数 //这个函数就是读取数据的函数,其中m_ghFileHandle的值,经察看是176, //readbuffer即用于存放数据的缓冲区,20表示读取20字节。 piChar=readbuffer; //指向缓冲区的头部 for(i=0;i<=19;i++) //用于输出缓冲区的数据,显示为乱码,不是文件中的数据gggggghhhhh. { DbgPrint("%c",*piChar);//输出到DbgView piChar++; } ... 下面是偶的自定义函数,主要是3个函数:一个用于打开文件,两个用于读取文件数据。 1.MyDriverCreateFile();//打开文件 这个函数用于打开文件,当然,也是在workitem里面打开了。其中对IRQL进行判断,若在IRQL=DISPATCH则用workitem来创建一个系统进程,再在workitem里面打开文件,这个函数返回值正确,就不列出细节,只给出最主要的ZwCreateFile部分。而且偶用它返回的文件句柄来写文件是正确的,就是说可以用这个文件句柄来写,就是读不出来,缓冲区始终为原来的值,不是读出的文件数据。 ... ntStatus=NT::ZwCreateFile( &FileHandle, SYNCHRONIZE | GENERIC_READ, &ObjectAttributes, &IoStatusBlock, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0 ); ... 2.MyDriverReadFile(); 这个是用来读文件数据的函数: VOID MyDriverReadFile( IN PVOID Buffer, //存放数据的缓冲区 IN ULONG Length, //读出的字节数 IN OUT HANDLE FileHandle) //文件句柄 { NTSTATUS ntStatus; NT::OBJECT_ATTRIBUTES ObjectAttributes;//NT是个namespace,里面包含了ntddk.h,ntdef.h,及自定义的struct_FILE_WORK_ITEM结构 NT::IO_STATUS_BLOCK IoStatusBlock; NT::UNICODE_STRING UniFileName; NT::PFILE_WORK_ITEM workitem; //FILE_WORK_ITEM结构是在NT这个namespace里面定义的 NT::LARGE_INTEGER ByteOffset; if(NT::KeGetCurrentIrql() < DISPATCH_LEVEL) //若IRQL==PASSIVE_LEVEL { ntStatus=NT::ZwReadFile(FileHandle, 0, 0, 0, &IoStatusBlock, Buffer, Length, NULL, NULL); if(NT_SUCCESS(ntStatus) && FileHandle != NULL) { } } else //经输出调试,发现IRQL不在 //PASSIVE_LEVEL,因此,执行的是这个分支 { ntStatus = STATUS_PENDING; workitem = (NT::PFILE_WORK_ITEM)NT::ExAllocatePool(NT::NonPagedPool,sizeof(NT::FILE_WORK_ITEM)); if (workitem) { ExInitializeWorkItem(&workitem->WorkItem, MyDriverReadFileWorkItem,//自定义的系统线程,用于读文件 workitem); workitem->FileContext = Buffer; workitem->FileHandle = FileHandle; workitem->Length = Length; NT::ExQueueWorkItem(&workitem->WorkItem,NT::DelayedWorkQueue); } else { ntStatus = STATUS_INSUFFICIENT_RESOURCES; } } return; } 3.MyDriverReadFileWorkItem(); 这个是自定义的读数据的系统进程函数,把其中的"Read"换成"Write"就是写文件的函数,而且经使用,写是可以正确写的(这时ZwCreateFile函数的参数要相应改为GENERIC_WRITE)。 VOID MyDriverReadFileWorkItem( PVOID Context) { HANDLE FileHandle; NTSTATUS ntStatus; NT::IO_STATUS_BLOCK IoStatusBlock; NT::LARGE_INTEGER ByteOffset; PVOID Buffer; ULONG Length; NT::PFILE_WORK_ITEM workitem = (NT::PFILE_WORK_ITEM) Context; FileHandle = workitem->FileHandle; Buffer = workitem->FileContext; Length = workitem->Length; ntStatus=NT::ZwReadFile(FileHandle, 0, 0, 0, &IoStatusBlock, Buffer, Length, NULL, NULL); if(NT_SUCCESS(ntStatus) && FileHandle != NULL) { } NT::ExFreePool(workitem); return; } 就是这3个函数了,打开文件,然后读文件数据到缓冲区。 再强调一下几个数据: 文件句柄:m_ghFileandle是个全局变量 缓冲区:char readbuffer[50]是在PtReceive里面定义的 用这个句柄和缓冲区是可以正确的写文件到数据的,已经实现过了,可就是不能正确的读文件数据。 偶第一次做驱动,重启蓝屏无数次了,装系统也有N次了。要不是实在没办法(身边的参考资料几乎只从 网上下,我们学校的图书馆放假了,书店也放假了),偶也不会来这哭了:)。 各位高手,就这些了,你们先不要骂我,等调通了偶竖着耳朵让你们骂,OK?? Thank you for all your help~~~! [编辑 - 8/19/04 by Camus1981] |
|
沙发#
发布于:2004-08-26 12:19
读文件和写文件的代码应该差不多,写没有问题,读应该也不会有问题,你确定是在读文件的时候蓝屏的吗??读和写操作的代码是否基本一致??
还有如果irql != PASSIVE_LEVEL,你的MyDriverReadFile将是异步的,你在该函数返回后立即读取缓冲区中的内容,当然可能是乱码,因为那时候系统还没有往里面写数据,另外因为MyDriverReadFile可能是异步工作,所以,你如果过早释放了缓冲区,还有可能导致蓝屏,不知道你的蓝屏是不是这个原因??? |
|
板凳#
发布于:2004-08-26 14:42
看看这段文字对你有没有帮助。
访问文件 有时,WDM驱动程序需要读写常规的磁盘文件。你可能需要向硬件下载一大段微代码,或者创建自己的全面的硬件操作记录。有一组ZwXxx函数可以做这些事情。 访问磁盘文件的第一步是调用ZwCreateFile打开一个文件句柄。DDK中详细地描述了该函数。我在这里仅描述该函数的两个方面,即读/写一个已知名的文件。 打开已存在文件然后读 下面例子打开一个已存在的文件然后读该文件: NTSTATUS status; OBJECT_ATTRIBUTES oa; IO_STATUS_BLOCK iostatus; HANDLE hfile; // the output from this process PUNICODE_STRING pathname; // you've been given this InitializeObjectAttributes(&oa, pathname, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwCreateFile(&hfile, GENERIC_READ, &oa, &iostatus, NULL, 0, FILE_SHARE_READ, FILE_OPEN, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); 创建或重写文件 为了创建一个新文件,或打开一个已存在文件并截为0长度,用下面方式调用ZwCreateFile函数: status = ZwCreateFile(&hfile, GENERIC_WRITE, &oa, &iostatus, NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0); 在上面这些例子中,我们建立了一个OBJECT_ATTRIBUTES结构,其目的是用它指向被打开文件的完整路径。我们指定了OBJ_CASE_INSENSITIVE属性,因为Win32文件系统模型对路径名的大小写不区分。然后我们调用ZwCreateFile打开文件句柄。 第一个参数(&hfile),是HANDLE类型变量的地址,ZwCreateFile将向该地址返回它创建的句柄。 第二个参数(GENERIC_READ或GENERIC_WRITE),指定文件访问方式,读或写。 第三个参数(&oa),是OBJECT_ATTRIBUTES结构的地址,该结构包含要打开的文件名。 第四个参数(&iostatus),指向一个IO_STATUS_BLOCK结构,该结构接收ZwCreateFile操作的结果状态。 第五个参数(NULL),是一个指针,指向一个64位整数,该数指定文件的初始分配大小。该参数仅关系到创建或重写文件操作,如果忽略它(如我在这里所做的),那么文件长度将从0开始,并随着写入而增长。 第六个参数(0或FILE_ATTRIBUTE_NORMAL),指定新创建文件的属性。 第七个参数(FILE_SHARE_READ或0),指定文件的共享方式。如果仅为读数据而打开文件,则可以与其它线程同时读取该文件。如果为写数据而打开文件,你可能不希望其它线程访问该文件。 第八个参数(FILE_OPEN或FILE_OVERWRITE_IF),表明当指定文件存在或不存在时应如何处理。在只读情况中,我指定FILE_OPEN,因为我希望打开一个已存在的文件,当文件不存在时返回错误。在只写情况中,我指定FILE_OVERWRITE_IF,因为我希望改写任何已存在的文件或创建一个新文件。 第九个参数(FILE_SYNCHRONOUS_IO_NONALERT),指定控制打开操作和句柄使用的附加标志位。在这里,我指明要做同步I/O操作(即我希望读写函数在I/O操作完成后才返回,此时调用者处于等待状态)。 第十个参数(NULL),一个指针,指向可选的扩展属性区。 第十一个参数(0),扩展属性区的长度。 我们希望ZwCreateFile返回STATUS_SUCCESS状态并设置句柄变量。然后我们用ZwReadFile和ZwWriteFile函数执行读写操作,最后调用ZwClose函数关闭文件句柄。 ZwClose(hfile); 你可以执行同步或异步读写操作,这取决于你在ZwCreateFile中指定的标志。我在上面例子中指定了同步操作,读写函数在完成后才返回。如下例: PVOID buffer; ULONG bufsize; status = ZwReadFile(hfile, NULL, NULL, NULL, &iostatus, buffer, bufsize, NULL, NULL); -or- status = ZwWriteFile(hfile, NULL, NULL, NULL, &iostatus, buffer, bufsize, NULL, NULL); 这些调用与用户模式中的非重叠ReadFile或WriteFile调用类似。当这些函数返回时,你可能对iostatus.Information域感兴趣,该域携带了操作所传输的字节数量。 如果你想把整个文件读入内存缓冲区,应该先调用ZwQueryInformationFile获得文件的总长度: FILE_STANDARD_INFORMATION si; ZwQueryInformationFile(hfile, &iostatus, &si, sizeof(si), FileStandardInformation); ULONG length = si.EndOfFile.LowPart; 文件操作的时间选择 当驱动程序为响应IRP_MN_START_DEVICE请求而初始化设备时,可能需要读磁盘文件。由于设备初始化可能出现在系统初始化各种设备的不同阶段,所以使用普通的路径名如\??\C:\dir\file.ext有时不能访问到文件。为了安全起见,你应该把数据文件放到系统根目录下的某个目录中,如\SystemRoot\dir\file.ext,而名称空间中的SystemRoot分支总是可访问的,因为操作系统在启动时也需要读磁盘文件。 浮点运算 有时候整数运算并不能满足要求,我们需要浮点运算。在Intel处理器上,浮点协处理器还可以执行MMX指令。在历史上,驱动程序在执行浮点运算上有两个问题。对于没有浮点协处理器的计算机,操作系统将用软件仿真一个,但是仿真的浮点协处理器会消耗很大的CPU处理能力,并且需要一个处理器异常来捕捉浮点指令。在内核模式中处理异常,尤其是在提升的IRQL级上,是困难的。另外,在有浮点协处理器的计算机上,由于CPU结构上的原因,当线程上下文切换时,需要一个耗时的操作来保存和恢复浮点协处理器的状态。所以,通常的做法是禁止在内核模式驱动程序中使用浮点运算。 Windows 2000和Windows 98提供了一种方法来绕过这个问题。首先,有一个运行在低于或等于DISPATCH_LEVEL级上系统线程,该线程可以自由地使用浮点协处理器。另外,运行在低于或等于DISPATCH_LEVEL级上的任意线程背景的驱动程序可以调用两个系统函数,用这两个函数调用可以把浮点运算程序括起来: ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL); KFLOATING_SAVE FloatSave; NTSTATUS status = KeSaveFloatingPointState(&FloatSave); if (NT_SUCCESS(status)) { ... KeRestoreFloatingPointState(&FloatSave); } 这两个调用必须成对出现,它们为当前CPU保存和恢复浮点协处理器状态,即浮点协处理器的状态可以维持到任意多个指令周期。这个状态信息包括寄存器、控制字,等等。而在某些其它种类的CPU上,这两个调用不做任何事,因为这种CPU在线程切换时可以自动保存和恢复浮点协处理器的状态。出于这种原因,Microsoft建议,除非必要,应避免在内核模式驱动程序中使用浮点运算。 调用KeSaveFloatingPointState时发生了什么,不同的CPU结构会有不同的答案。例如,在Intel处理器上,该函数通过执行FSAVE指令把整个浮点处理器状态保存起来。它既可以把状态信息保存到当前线程的上下文块中,也可以保存到一块动态分配的内存区中。它还用一个不透明的FloatSave区记录关于被保存状态的中间信息,这可以使KeRestoreFloatingPointState正确地恢复浮点协处理器的状态。 如果系统没有浮点协处理器,KeSaveFloatingPointState将失败并返回STATUS_ILLEGAL_FLOAT_CONTEXT代码。(顺便提一下,多CPU计算机的每个CPU必须同时拥有或同时没有协处理器) 因此你的驱动程序必须以其它方式实现浮点计算,或者干脆拒绝把驱动程序装入无浮点处理器的计算机上(由于DriverEntry的失败)。 |
|
|