我有一个 NT 式驱动 HelloDDK,“在之前的调试中没出错”。
我想为这个驱动增加与应用程序通信的功能。
我在 HelloDDK.h 中增加了函数声名:NTSTATUS DeviceIoControl( IN PDEVICE_OBJECT, IN PIRP );        // IRP_MJ_DEVICE_CONTROL 的派遣函数,与设备交互
在 DriverEntry() 中注册了该函数:pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIoControl;
在 DeviceIoControl.h 中增加了函数实现:
#define MY_DEVICE_IOCTL \
CTL_CODE( FILE_DEVICE_UNKNOWN, \
0x800, \
METHOD_BUFFERED, \
FILE_ANY_ACCESS)
NTSTATUS
DeviceIoControl( IN PDEVICE_OBJECT pDeviceObject, IN PIRP pIrp )
{
NTSTATUS status = STATUS_SUCCESS; PIO_STACK_LOCATION pIoStack = 0; // I/O 堆栈 PCHAR pInputBuffer = 0; // 输入缓冲区指针 PCHAR pOutputBuffer = 0; // 输出缓冲区指针 ULONG InputBufferSize = 0; // 输入缓冲区的大小 ULONG OutputBufferSize = 0; // 输出缓冲区的大小 ULONG IoCode = 0; // IOCODE 码 PCHAR DeviceOutput = "the string from DeviceIoControl() in the Driver";        // 输出到应用程序 ULONG OutDataLen = strlen( DeviceOutput ) + 1; //信息长度含结尾一个NULL   
KdPrint(( "in the DeviceIoControl() for the HelloDDK project \n" ));
pIoStack = IoGetCurrentIrpStackLocation( pIrp ); // 获取当前 I/O 堆栈 InputBufferSize = pIoStack->Parameters.DeviceIoControl.InputBufferLength; OutputBufferSize = pIoStack->Parameters.DeviceIoControl.OutputBufferLength; IoCode = pIoStack->Parameters.DeviceIoControl.IoControlCode;
if ( MY_DEVICE_IOCTL == IoCode ) // IOCTL 码编号 0x800。
{
KdPrint(( "控制码 MY_DEVICE_IOCTL,编号 0x800。\n" ));
pInputBuffer = pIrp->AssociatedIrp.SystemBuffer; // 操作输入缓冲区

KdPrint(( "the string from Appliction %s\n", pInputBuffer )); //打印出应用层传入的内容
pOutputBuffer = pIrp->AssociatedIrp.SystemBuffer; // 操作输出缓冲区 RtlCopyBytes( pOutputBuffer, DeviceOutput, OutputBufferSize ); // 复制要传出的字符串到输出缓冲区
pIrp->IoStatus.Status = status; pIrp->IoStatus.Information = OutputBufferSize;
} else
{
pIrp->IoStatus.Status = STATUS_INVALID_VARIANT; pIrp->IoStatus.Information = 0;
}
#if DBG // 调试状态
_asm int 3 // 断点
#endif
IoCompleteRequest( pIrp, IO_NO_INCREMENT ); // 完成此次 IRP
return pIrp->IoStatus.Status;
}
卸载函数:#pragma PAGEDCODE
void
HelloDDKUnload( IN PDRIVER_OBJECT pDriverObject ) // 驱动卸载
{
PDEVICE_OBJECT pDeviceObject; // 设备对象 PDEVICE_EXTENSION pDeviceExtension;          // 设备扩展,HelloDDK.h 中 UNICODE_STRING symLinkName; // 符号链接名
pDeviceObject = pDriverObject->DeviceObject; // 由驱动对象得到设备对象
while ( NULL != pDeviceObject )
{
pDeviceExtension = pDeviceObject->DeviceExtension; symLinkName = pDeviceExtension->sSymlinkName; IoDeleteSymbolicLink( &symLinkName );          //删除符号链接
pDeviceObject = pDeviceObject->NextDevice;          // 指向下一个设备对象 IoDeleteDevice( pDeviceExtension->pDevice ); // 删除当前设备对象
} #if DBG // 调试状态
_asm int 3 // 断点
#endif KdPrint(( "in the HelloDDKUnload() for the HelloWDM project \n" ));
}我在虚拟机中调试时,先在虚拟机中的控制台中输入: net start HelloDDK提示:服务启动成功。
紧接者,在不执行任何其它步骤的情况下,在虚拟机中的控制台中输入: net stop HelloDDK回车,此时,在 WinDbg 中,出现以下提示:*** Fatal System Error: 0x00000050
                       (0xFCA260E0,0x00000000,0x80564772,0x00000000)Break instruction exception - code 80000003 (first chance)A fatal system error has occurred.
Debugger entered on first try; Bugcheck callbacks have not been invoked.A fatal system error has occurred.Connected to Windows XP 2600 x86 compatible target, ptr64 FALSE
Loading Kernel Symbols
............................................................................................................Loading User SymbolsLoading unloaded module list
.........
*******************************************************************************
*                                                                             *
*                        Bugcheck Analysis                                    *
*                                                                             *
*******************************************************************************Use !analyze -v to get detailed debugging information.BugCheck 50, {fca260e0, 0, 80564772, 0}*** ERROR: Module load completed but symbols could not be loaded for mssmbios.sys
Probably caused by : HelloDDK.sys ( HelloDDK!HelloDDKUnload+39 )Followup: MachineOwner
---------nt!RtlpBreakWithStatusInstruction:
804e4592 cc              int     3
我只要将新增的与 DeviceIoControl() 有关的代码都去掉,就能正常停止该驱动。不知错在哪里?请高人指点!先谢了!

解决方案 »

  1.   

    在HelloDDKUnload上设置断点调试一下,看看执行到哪一行代码出问题,再查看相关变量的值是否正常。
      

  2.   

    我在 HelloDDKUnload() 上设置断点调试了一下。执行到 HelloDDKUnload() 中的下面这行代码后,WinDbg 才出现错误提式:IoDeleteSymbolicLink( &symLinkName ); //删除符号链接下面是 HelloDDKUnload() 完整代码:#pragma PAGEDCODE 
    void 
    HelloDDKUnload( IN PDRIVER_OBJECT pDriverObject ) // 驱动卸载 

    PDEVICE_OBJECT pDeviceObject; // 设备对象 PDEVICE_EXTENSION pDeviceExtension;         // 设备扩展,HelloDDK.h 中 UNICODE_STRING symLinkName; // 符号链接名 
    pDeviceObject = pDriverObject->DeviceObject; // 由驱动对象得到设备对象 
    while ( NULL != pDeviceObject ) 

    pDeviceExtension = pDeviceObject->DeviceExtension; symLinkName = pDeviceExtension->sSymlinkName; 
    KdPrint(( "断点 2   in the HelloDDKUnload()\n" )); #if DBG // 调试状态
        _asm int 3 // 断点
    #endifIoDeleteSymbolicLink( &symLinkName );         //删除符号链接 
    KdPrint(( "断点 3   in the HelloDDKUnload()\n" )); #if DBG // 调试状态
        _asm int 3 // 断点
    #endif
    pDeviceObject = pDeviceObject->NextDevice;         // 指向下一个设备对象 IoDeleteDevice( pDeviceExtension->pDevice ); // 删除当前设备对象 

    #if DBG // 调试状态 
    _asm int 3 // 断点 
    #endif KdPrint(( "in the HelloDDKUnload() for the HelloWDM project \n" )); 

    从中可看出,symLinkName 是从设备扩展中获取的:pDeviceExtension = pDeviceObject->DeviceExtension; symLinkName = pDeviceExtension->sSymlinkName; 
    在不增加与 DeviceIoControl() 有关的代码时,服务能正常停止。那就说明,下面这行应该没问题:IoDeleteSymbolicLink( &symLinkName );         //删除符号链接 
    那为什么增加与 DeviceIoControl() 有关的代码后,服务不能正常停止呢?
    我只启动和停止服务,中间无任何其它操作。DeviceIoControl() 中的代码并未被执行。不知为何错在:
    IoDeleteSymbolicLink( &symLinkName );         //删除符号链接 请指点!!
      

  3.   

    #if DBG // 调试状态 
    _asm int 3 // 断点 
    #endif 去掉
      

  4.   

    执行到IoDeleteSymbolicLink时,看一下symLinkName内的数据是否正常。
      

  5.   


    我在 WinDbg 中设置了符号表的路径,符号表的路径与本机上 HelloDDK 驱动的 .sys 文件在同一目录下。但我在 WinDbg 中的 Watch 窗口中看不到任何变量显示?
      

  6.   

    应该是symLinkName的问题,做个有效性判断吧。
      

  7.   


    下面是初始化 symLinkName 的代码:RtlInitUnicodeString( &symLinkName, L"\\??\\HelloDDK");
    我在 HelloDDKUnload() 添加了打印 symLinkName 的代码:KdPrint(( "symLinkName 的值:   %wZ\n", &symLinkName )); // 打印变量的内容 #if DBG // 调试状态
    _asm int 3 // 断点
    #endif
    然后,我先把与 DeviceIoControl() 有关的代码注释掉,然后调试。执行到:KdPrint(( "symLinkName 的值:   %wZ\n", &symLinkName )); // 打印变量的内容
    这行时,在 WinDbg 中显示:symLinkName 的值:   \??\HelloDDK说明没问题。而且,直到最后都没出错,服务也能正常停止。
    然后,我再把与 DeviceIoControl() 有关的代码加上,然后调试。执行到:KdPrint(( "symLinkName 的值:   %wZ\n", &symLinkName )); // 打印变量的内容
    这行时,在 WinDbg 中显示:symLinkName 的值:   
    说明 symLinkName 的值不对。但也说明问题出在与 DeviceIoControl() 有关的代码加上。与 DeviceIoControl() 有关的代码具体代码都在本帖第一楼中,我看不出问题所在。
    请高人们帮忙指点指点了!!!!
      

  8.   

    依我看不是DeviceIoControl的问题,而是你把symLinkName保存到设备对象中的具体做法有问题,建议你把初始化代码贴全。你可以在DeviceIoControl上设置断点调试看看该函数有没有执行,如果执行到断点上,就单步调试并监视symLinkName的变化;如果没有执行,自然就不是这个函数的问题了。
      

  9.   

    我在 DeviceIoControl() 上设置了断点,调试时该函数没有执行。我没运行应用程序,即没有向驱动发送 IRP_MJ_DEVICE_CONTROL ,所以 DeviceIoControl() 函数没有执行是合理的。下面是我的初始化代码:// HelloDDK.h#ifndef NTDDK_H
    #include <ntddk.h>
    #define NTDDK_H
    #endif// 内存模式#define PAGEDCODE code_seg("PAGE")
    #define LOCKEDCODE code_seg()
    #define INITCODE code_seg("INIT")#define PAGEDDATA data_seg("PAGE")
    #define LOCKEDDATA data_seg()
    #define INITDATA data_seg("INIT")
    // 自定义宏,计算数组元素个数,用于默认派遣函数
    #define MyArraySize( p ) ( sizeof( p ) / sizeof( ( p )[0] ) )typedef struct DEVICE_EXTENSION // 自定义设备扩展
    {
    PDEVICE_OBJECT pDevice; // 设备对象指针 UNICODE_STRING sDeviceName; // 设备名,由内核使用 UNICODE_STRING sSymlinkName; // 符号链接名,由应用程序使用} DEVICE_EXTENSION, *PDEVICE_EXTENSION;NTSTATUS DriverEntry( IN PDRIVER_OBJECT, IN PUNICODE_STRING ); // 入口void HelloDDKUnload( IN PDRIVER_OBJECT ); // 驱动卸载NTSTATUS CreateDevice( IN PDRIVER_OBJECT ); // 添加新设备
    NTSTATUS  ReadDevice( IN PDEVICE_OBJECT, IN PIRP );   // IRP_MJ_READ 的派遣函数
    //IRP_MJ_DEVICE_CONTROL 的派遣函数,与设备交互
    NTSTATUS  DeviceIoControl( IN PDEVICE_OBJECT, IN PIRP );NTSTATUS DispatchRoutine( IN PDEVICE_OBJECT, IN PIRP ); // 默认派遣函数// CreateDevice.h
    #pragma INITCODE
    NTSTATUS
    CreateDevice( IN PDRIVER_OBJECT pDriverObject ) // 添加新设备
    {
    NTSTATUS status; // 状态码 PDEVICE_OBJECT pDeviceOb;          // 设备对象 PDEVICE_EXTENSION pDeviceExtension; // 自定义的设备扩展数据,HelloDDK.h 中 UNICODE_STRING DeviceName; // 设备名 UNICODE_STRING symLinkName; // 符号链接

    // 初始化字符串 RtlInitUnicodeString( &DeviceName, L"\\Device\\HelloDDKDevice"); RtlInitUnicodeString( &symLinkName, L"\\??\\HelloDDK");
    status = IoCreateDevice( pDriverObject, sizeof( DEVICE_EXTENSION ),
    &DeviceName, 
                                                            FILE_DEVICE_UNKNOWN,
    0, TRUE, &pDeviceOb ); if( !NT_SUCCESS( status ) )
    return status;

    pDeviceOb->Flags |= DO_BUFFERED_IO;
    // 填写设备扩展 pDeviceExtension = pDeviceOb->DeviceExtension; pDeviceExtension->pDevice = pDeviceOb; pDeviceExtension->sDeviceName = DeviceName; pDeviceExtension->sSymlinkName = symLinkName;

    // 创建设备的符号链接 status = IoCreateSymbolicLink( &symLinkName, &DeviceName ); if( !NT_SUCCESS( status ) )         // 符号链接创建“失败”
    {
    IoDeleteDevice( pDeviceOb ); // 删除设备对象 return status;
    } #if DBG // 调试状态
    _asm int 3 // 断点
    #endif
    KdPrint(( "in the CreateDevice() for the HelloDDK project \n" )); return STATUS_SUCCESS; //  成功
    }
    请帮我看看哪儿有问题,先谢了!
      

  10.   

    把#pragma INITCODE去掉试试,可能是字符串被分配在初始化区,初始化结束后被释放掉了。
      

  11.   

    确实如您所说,把 #pragma INITCODE 去掉,就能正常停止服务了。
    我觉得,符号链接等字符串,被我拷贝到了设备扩展对象中。而我的 HelloDDKUnload() 是从设备扩展对象中读取得到符号链接的字符串值。我想,如果我定义一个设备扩展的“全局对象”,把这个对象载入“分页内存”。然后,我把 CreateDevice() 载入“初始化内存”。我再在 CreateDevice() 中把符号链接等字符串拷贝到这个设备扩展的“全局对象”中。那么,我的 HelloDDKUnload() 就能从设备扩展对象中读取到正确的符号链接的字符串值。这样做,不知是否合理?
      

  12.   

    我按我上面的想法试了下,行不通,哈哈!好在 cnzdgs 帮我解决了问题。谢谢大家观注!!
      

  13.   

    我前面所说的“把#pragma INITCODE去掉”只是为了验证是不是这个问题,解决这个问题最好是把字符串定义为全局数据,不要写在#pragma INITCODE的作用域里面,这样就不会被释放了。