本论坛为只读模式,仅供查阅,不能注册新用户,不能发帖/回帖,有问题可发邮件 xikug.xp (^) gmail.com
查看: 6411|回复: 7

DiskPerf代码解读 [复制链接]

Rank: 3Rank: 3

发表于 2010-11-6 05:24:50 |显示全部楼层
水平有限,欢迎大家多批评。有什么意见请跟贴,我尽量回复,谢谢大家。

Diskperf代码分析



/*++
Copyright (C) Microsoft Corporation, 1991 - 1999

Module Name:

    diskperf.c

Abstract:

    This driver monitors disk accesses capturing performance data.

Environment:

    kernel mode only

Notes:

--*/


#define INITGUID

#include "ntddk.h"
#include "ntdddisk.h"
#include "stdarg.h"
#include "stdio.h"
#include <ntddvol.h>

#include <mountdev.h>
#include "wmistr.h"
#include "wmidata.h"
#include "wmiguid.h"
#include "wmilib.h"

#include "ntstrsafe.h"

// 下面这个宏用来把ExAllocatePool替换成ExAllocatePoolWithTag,MS的文档中推荐用后
// 者,以便调试,所以做了这个替换
#ifdef POOL_TAGGING
#ifdef ExAllocatePool
#undef ExAllocatePool
#endif
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'frPD')
#endif

#define DISKPERF_MAXSTR         64

//
// Device Extension
//

typedef struct _DEVICE_EXTENSION {

    //
    // Back pointer to device object
    //

    PDEVICE_OBJECT DeviceObject;

    //
    // 目标设备对象
    //

    PDEVICE_OBJECT TargetDeviceObject;

    //
    // 物理设备对象
    //
    PDEVICE_OBJECT PhysicalDeviceObject;

    //
    // Disk number for reference in WMI
    //

    ULONG       DiskNumber;

    //
    // If device is enabled for counting always
    //

    LONG        EnabledAlways;

    //
    // Use to keep track of Volume info from ntddvol.h
    //

    WCHAR StorageManagerName[8];

    //
    // Disk performance counters
    // and locals used to compute counters
    //

    ULONG   Processors;
    PDISK_PERFORMANCE DiskCounters;    // per processor counters
    LARGE_INTEGER LastIdleClock;
    LONG QueueDepth;
    LONG CountersEnabled;

    //
    // 同步分页路径通知用
    //
    KEVENT PagingPathCountEvent;
    LONG  PagingPathCount;

    //
    //  物理设备名或者WMI 实例名字
    //

    UNICODE_STRING PhysicalDeviceName;
    WCHAR PhysicalDeviceNameBuffer[DISKPERF_MAXSTR];

    //
    // Private context for using WmiLib
    //
    WMILIB_CONTEXT WmilibContext;

} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

#define DEVICE_EXTENSION_SIZE sizeof(DEVICE_EXTENSION)
#define PROCESSOR_COUNTERS_SIZE FIELD_OFFSET(DISK_PERFORMANCE, QueryTime)

/*
每个处理器的计数器的布局,是一个连续的内存块:

    处理器1
+-----------------------+     +-----------------------+
|PROCESSOR_COUNTERS_SIZE| ... |PROCESSOR_COUNTERS_SIZE|
+-----------------------+     +-----------------------+
这里PROCESSOR_COUNTERS_SIZE比DISK_PERFORMANCE的尺寸要小,因为我们只统计实际使用的。
*/

UNICODE_STRING DiskPerfRegistryPath;



//
// 函数定义
//

DRIVER_INITIALIZE DriverEntry;

DRIVER_ADD_DEVICE DiskPerfAddDevice;

DRIVER_DISPATCH DiskPerfForwardIrpSynchronous;

__drv_dispatchType(IRP_MJ_PNP)
DRIVER_DISPATCH DiskPerfDispatchPnp;

__drv_dispatchType(IRP_MJ_POWER)
DRIVER_DISPATCH DiskPerfDispatchPower;

DRIVER_DISPATCH DiskPerfSendToNextDriver;

__drv_dispatchType(IRP_MJ_CREATE)
DRIVER_DISPATCH DiskPerfCreate;

__drv_dispatchType(IRP_MJ_READ)
__drv_dispatchType(IRP_MJ_WRITE)
DRIVER_DISPATCH DiskPerfReadWrite;

__drv_dispatchType(IRP_MJ_DEVICE_CONTROL)
DRIVER_DISPATCH DiskPerfDeviceControl;

__drv_dispatchType(IRP_MJ_SYSTEM_CONTROL)
DRIVER_DISPATCH DiskPerfWmi;

__drv_dispatchType(IRP_MJ_FLUSH_BUFFERS)
__drv_dispatchType(IRP_MJ_SHUTDOWN)
DRIVER_DISPATCH DiskPerfShutdownFlush;

DRIVER_DISPATCH DiskPerfStartDevice;
DRIVER_DISPATCH DiskPerfRemoveDevice;

IO_COMPLETION_ROUTINE DiskPerfIoCompletion;
IO_COMPLETION_ROUTINE DiskPerfIrpCompletion;

DRIVER_UNLOAD DiskPerfUnload;


VOID
DiskPerfLogError(
    IN PDEVICE_OBJECT DeviceObject,
    IN ULONG UniqueId,
    IN NTSTATUS ErrorCode,
    IN NTSTATUS Status
    );

NTSTATUS
DiskPerfRegisterDevice(
    IN PDEVICE_OBJECT DeviceObject
    );

WMI_QUERY_REGINFO_CALLBACK DiskperfQueryWmiRegInfo;

WMI_QUERY_DATABLOCK_CALLBACK DiskperfQueryWmiDataBlock;

VOID
DiskPerfSyncFilterWithTarget(
    IN PDEVICE_OBJECT FilterDevice,
    IN PDEVICE_OBJECT TargetDevice
    );

WMI_FUNCTION_CONTROL_CALLBACK DiskperfWmiFunctionControl;

VOID
DiskPerfAddCounters(
    IN OUT PDISK_PERFORMANCE TotalCounters,
    IN PDISK_PERFORMANCE NewCounters,
    IN LARGE_INTEGER Frequency
    );

#if DBG

ULONG DiskPerfDebug = 0;

VOID
DiskPerfDebugPrint(
    ULONG DebugPrintLevel,
    PCCHAR DebugMessage,
    ...
    );

#define DebugPrint(x)   DiskPerfDebugPrint x

#else

#define DebugPrint(x)

#endif

//
// 下面的宏定义用来优化编译器,取消一些无关代码(比如分页代码)。
//
//

#ifdef ALLOC_PRAGMA
#pragma alloc_text (INIT, DriverEntry)
#pragma alloc_text (PAGE, DiskPerfCreate)
#pragma alloc_text (PAGE, DiskPerfAddDevice)
#pragma alloc_text (PAGE, DiskPerfDispatchPnp)
#pragma alloc_text (PAGE, DiskPerfStartDevice)
#pragma alloc_text (PAGE, DiskPerfRemoveDevice)
#pragma alloc_text (PAGE, DiskPerfUnload)
#pragma alloc_text (PAGE, DiskPerfWmi)
#pragma alloc_text (PAGE, DiskperfQueryWmiRegInfo)
#pragma alloc_text (PAGE, DiskperfQueryWmiDataBlock)
#pragma alloc_text (PAGE, DiskPerfRegisterDevice)
#pragma alloc_text (PAGE, DiskPerfSyncFilterWithTarget)
#endif

WMIGUIDREGINFO DiskperfGuidList[] =
{
    { &DiskPerfGuid,
      1,
      0
    }
};

#define DiskperfGuidCount (sizeof(DiskperfGuidList) / sizeof(WMIGUIDREGINFO))

#define USE_PERF_CTR

#ifdef USE_PERF_CTR
#define DiskPerfGetClock(a, b) (a) = KeQueryPerformanceCounter((b))
#else
#define DiskPerfGetClock(a, b) KeQuerySystemTime(&(a))
#endif



NTSTATUS
DriverEntry(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING RegistryPath
    )

/*++

函数说明:

    可安装驱动的初始化入口点。
    这个方法直接被I/O管理器调用,用来创建磁盘性能驱动。这里的driver对象创建好,
    然后Pnp管理器调用DiskPerfAddDevice来把它附加到启动设备上。


参数:

    DriverObject – 磁盘性能驱动对象。

RegistryPath – 指向一个unicode的字符串,表示路径。
也就是注册表中,驱动特定的键。


返回值:

    STATUS_SUCCESS 如果成功的话

--*/

{

    ULONG               ulIndex;
    PDRIVER_DISPATCH  * dispatch;

    //
    // 保存注册表路径
    //

    DiskPerfRegistryPath.MaximumLength = RegistryPath->Length
                                            + sizeof(UNICODE_NULL);
    DiskPerfRegistryPath.Buffer = ExAllocatePool(
                                    PagedPool,
                                    DiskPerfRegistryPath.MaximumLength);
    if (DiskPerfRegistryPath.Buffer != NULL)
    {
        RtlCopyUnicodeString(&DiskPerfRegistryPath, RegistryPath);
    } else {
        DiskPerfRegistryPath.Length = 0;
        DiskPerfRegistryPath.MaximumLength = 0;
    }

    //
    // 创建分派函数
    //
    for (ulIndex = 0, dispatch = DriverObject->MajorFunction;
         ulIndex <= IRP_MJ_MAXIMUM_FUNCTION;
         ulIndex++, dispatch++) {

        *dispatch = DiskPerfSendToNextDriver;
    }

    //
    // 创建设备驱动入口点
    //

    DriverObject->MajorFunction[IRP_MJ_CREATE]          = DiskPerfCreate;
    DriverObject->MajorFunction[IRP_MJ_READ]            = DiskPerfReadWrite;
    DriverObject->MajorFunction[IRP_MJ_WRITE]           = DiskPerfReadWrite;
    DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]  = DiskPerfDeviceControl;
    DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL]  = DiskPerfWmi;

    DriverObject->MajorFunction[IRP_MJ_SHUTDOWN]        = DiskPerfShutdownFlush;
    DriverObject->MajorFunction[IRP_MJ_FLUSH_BUFFERS]   = DiskPerfShutdownFlush;
    DriverObject->MajorFunction[IRP_MJ_PNP]             = DiskPerfDispatchPnp;
    DriverObject->MajorFunction[IRP_MJ_POWER]           = DiskPerfDispatchPower;


    // 下面这行代码请尤其注意:这行代码是确保本驱动被装载的时候会执行
// DiskPerfAddDevice的关键

    DriverObject->DriverExtension->AddDevice            = DiskPerfAddDevice;
    DriverObject->DriverUnload                          = DiskPerfUnload;

    return(STATUS_SUCCESS);

} // end DriverEntry()

#define FILTER_DEVICE_PROPOGATE_FLAGS            0
#define FILTER_DEVICE_PROPOGATE_CHARACTERISTICS (FILE_REMOVABLE_MEDIA |  \
                                                 FILE_READ_ONLY_DEVICE | \
                                                 FILE_FLOPPY_DISKETTE    \
                                                 )

VOID
DiskPerfSyncFilterWithTarget(
    IN PDEVICE_OBJECT FilterDevice,
    IN PDEVICE_OBJECT TargetDevice
    )
{
    ULONG                   propFlags;

    PAGED_CODE();

//
// 把目标设备的一些有用标记复制到diskperf中。挂载管理器将查找
// diskperf对象的功能点,并且表明磁盘是可移动设备还是其他的
// 设备
    //
    propFlags = TargetDevice->Flags & FILTER_DEVICE_PROPOGATE_FLAGS;
    FilterDevice->Flags |= propFlags;

    propFlags = TargetDevice->Characteristics & FILTER_DEVICE_PROPOGATE_CHARACTERISTICS;
    FilterDevice->Characteristics |= propFlags;


}

NTSTATUS
DiskPerfAddDevice(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT PhysicalDeviceObject
    )
/*++
方法说明:

    为对应得物理设备对象(PDO=Physical Device Object)创建一个新的过滤设备对
象(FiDO)。然后把这个设备对象“粘”到驱动的堆栈上去。
   


参数:

    DriverObject – 磁盘性能驱动对象。
    PhysicalDeviceObject – 下层驱动的物理设备对象。

返回值:

    NTSTATUS
--*/

{
    NTSTATUS                status;
    PDEVICE_OBJECT          filterDeviceObject;
    PDEVICE_EXTENSION       deviceExtension;
    PWMILIB_CONTEXT         wmilibContext;
    PCHAR                   buffer;
    ULONG                   buffersize;

    PAGED_CODE();

    //
    //  为本设备创建一个过滤设备对象(分离)。
    //

    DebugPrint((2, "DiskPerfAddDevice: Driver %X Device %X\n",
            DriverObject, PhysicalDeviceObject));

    // 这里尤其要注意FILE_DEVICE_DISK这个参数,这个用来表示你创建的
    // 设备需要“粘”到磁盘设备对象,如果这个参数弄错了,你可能就“粘”
    // 到其他地方去了。
    status = IoCreateDevice(DriverObject,
                            DEVICE_EXTENSION_SIZE,
                            NULL,
                            FILE_DEVICE_DISK,
                            FILE_DEVICE_SECURE_OPEN,
                            FALSE,
                            &filterDeviceObject);

    if (!NT_SUCCESS(status)) {
       DebugPrint((1, "DiskPerfAddDevice: Cannot create filterDeviceObject\n"));
       return status;
    }

    filterDeviceObject->Flags |= DO_DIRECT_IO;

    deviceExtension = (PDEVICE_EXTENSION) filterDeviceObject->DeviceExtension;

    RtlZeroMemory(deviceExtension, DEVICE_EXTENSION_SIZE);
    DiskPerfGetClock(deviceExtension->LastIdleClock, NULL);
    DebugPrint((10, "DiskPerfAddDevice: LIC=%I64u\n",
                    deviceExtension->LastIdleClock));

    //
//  为每个处理器分配计数器
//
    // 注意:为了节省内存,我们不必为QueryTime分配额外的内存。
    //  如果有额外需要的话,记录需要的额外的内存大小。
    //

#if (NTDDI_VERSION >= NTDDI_WIN7)
    deviceExtension->Processors = KeQueryActiveProcessorCountEx(ALL_PROCESSOR_GROUPS);
#elif (NTDDI_VERSION >= NTDDI_VISTA)
    deviceExtension->Processors = KeQueryActiveProcessorCount(NULL);   
#else
    deviceExtension->Processors = KeNumberProcessors;
#endif

    buffersize= PROCESSOR_COUNTERS_SIZE * deviceExtension->Processors;
    buffer = (PCHAR) ExAllocatePool(NonPagedPool, buffersize);
    if (buffer != NULL) {
        RtlZeroMemory(buffer, buffersize);
        deviceExtension->DiskCounters = (PDISK_PERFORMANCE) buffer;
    }
    else {
        DiskPerfLogError(
            filterDeviceObject,
            513,
            STATUS_SUCCESS,
            IO_ERR_INSUFFICIENT_RESOURCES);
    }

//
// 把设备对象“粘”到设备链中最高层的设备对象中
// 同时返回上次的最高层设备对象,这个用来在调用IoCallDriver
// 把IRP下发的时候用。
    //

    deviceExtension->PhysicalDeviceObject = PhysicalDeviceObject;

    deviceExtension->TargetDeviceObject =
        IoAttachDeviceToDeviceStack(filterDeviceObject, PhysicalDeviceObject);

    if (deviceExtension->TargetDeviceObject == NULL) {
        ExFreePool(deviceExtension->DiskCounters);
        deviceExtension->DiskCounters = NULL;
        IoDeleteDevice(filterDeviceObject);
        DebugPrint((1, "DiskPerfAddDevice: Unable to attach %X to target %X\n",
            filterDeviceObject, PhysicalDeviceObject));
        return STATUS_NO_SUCH_DEVICE;
    }

    //
//  在device extension中保存过滤设备对象。
//
    deviceExtension->DeviceObject = filterDeviceObject;

    deviceExtension->PhysicalDeviceName.Buffer
            = deviceExtension->PhysicalDeviceNameBuffer;

    KeInitializeEvent(&deviceExtension->PagingPathCountEvent,
                      NotificationEvent, TRUE);


    //
    //  初始化WMI库上下文
    //
    wmilibContext = &deviceExtension->WmilibContext;
    RtlZeroMemory(wmilibContext, sizeof(WMILIB_CONTEXT));
    wmilibContext->GuidCount = DiskperfGuidCount;
    wmilibContext->GuidList = DiskperfGuidList;
    wmilibContext->QueryWmiRegInfo = DiskperfQueryWmiRegInfo;
    wmilibContext->QueryWmiDataBlock = DiskperfQueryWmiDataBlock;
    wmilibContext->WmiFunctionControl = DiskperfWmiFunctionControl;

    //
    // 默认DO_POWER_PAGABLE
    //

    filterDeviceObject->Flags |=  DO_POWER_PAGABLE;

    //
    //  清除DO_DEVICE_INITIALIZING标记
    //

    filterDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    return STATUS_SUCCESS;

} // end DiskPerfAddDevice()


NTSTATUS
DiskPerfDispatchPnp(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
/*++

方法说明:

    PNP分派

参数:

    DeviceObject    - 设备对象

    Irp             - I/O请求包

返回值:

    NTSTATUS

--*/

{
    PIO_STACK_LOCATION  irpSp = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS            status;
    PDEVICE_EXTENSION   deviceExtension;

    PAGED_CODE();

    DebugPrint((2, "DiskPerfDispatchPnp: Device %X Irp %X\n",
        DeviceObject, Irp));

    switch(irpSp->MinorFunction) {

        case IRP_MN_START_DEVICE:
            //
            // 调用StartRoutine
            //
            DebugPrint((3,
               "DiskPerfDispatchPnp: Schedule completion for START_DEVICE"));
            status = DiskPerfStartDevice(DeviceObject, Irp);
            break;

        case IRP_MN_REMOVE_DEVICE:
        {
            //
            // 调用移除方法
            //
            DebugPrint((3,
               "DiskPerfDispatchPnp: Schedule completion for REMOVE_DEVICE"));
            status = DiskPerfRemoveDevice(DeviceObject, Irp);
            break;
        }
        case IRP_MN_DEVICE_USAGE_NOTIFICATION:
        {
            PIO_STACK_LOCATION irpStack;
            BOOLEAN setPagable;

            DebugPrint((3,
               "DiskPerfDispatchPnp: Processing DEVICE_USAGE_NOTIFICATION"));
            irpStack = IoGetCurrentIrpStackLocation(Irp);

            if (irpStack->Parameters.UsageNotification.Type != DeviceUsageTypePaging) {
                status = DiskPerfSendToNextDriver(DeviceObject, Irp);
                break; // 跳出 case 语句
            }

            deviceExtension = DeviceObject->DeviceExtension;

            //
            // 等待分页路径事件
            //

            status = KeWaitForSingleObject(&deviceExtension->PagingPathCountEvent,
                                           Executive, KernelMode,
                                           FALSE, NULL);

            //
            // 如果移除上一次分页的设备,需要设置DO_POWER_PAGABLE
            // 如果失败,则清除这个标志
            //

            setPagable = FALSE;
            if (!irpStack->Parameters.UsageNotification.InPath &&
                deviceExtension->PagingPathCount == 1 ) {

                //
                // removing the last paging file
                // must have DO_POWER_PAGABLE bits set
                //

                if (DeviceObject->Flags & DO_POWER_INRUSH) {
                    DebugPrint((3, "DiskPerfDispatchPnp: last paging file "
                                "removed but DO_POWER_INRUSH set, so not "
                                "setting PAGABLE bit "
                                "for DO %p\n", DeviceObject));
                } else {
                    DebugPrint((2, "DiskPerfDispatchPnp: Setting  PAGABLE "
                                "bit for DO %p\n", DeviceObject));
                    DeviceObject->Flags |= DO_POWER_PAGABLE;
                    setPagable = TRUE;
                }

            }

            //
            // 同步发送irp
            //

            status = DiskPerfForwardIrpSynchronous(DeviceObject, Irp);

            //
            // 处理成功,失败的情况。now deal with the failure and success cases.
            // 注意,发送到下层驱动后,irp不允许失败。
            //
            //

            if (NT_SUCCESS(status)) {

                IoAdjustPagingPathCount(
                    &deviceExtension->PagingPathCount,
                    irpStack->Parameters.UsageNotification.InPath);

                if (irpStack->Parameters.UsageNotification.InPath) {
                    if (deviceExtension->PagingPathCount == 1) {

                        //
                        // 分页文件第1条件
                        //

                        DebugPrint((3, "DiskPerfDispatchPnp: Clearing PAGABLE bit "
                                    "for DO %p\n", DeviceObject));
                        DeviceObject->Flags &= ~DO_POWER_PAGABLE;
                    }
                }

            } else {

                //
                // 清除上文的改变
                //

                if (setPagable == TRUE) {
                    DeviceObject->Flags &= ~DO_POWER_PAGABLE;
                    setPagable = FALSE;
                }

            }

            //
            // 设置事件,以便下一个事件可以触发
            //

            KeSetEvent(&deviceExtension->PagingPathCountEvent,
                       IO_NO_INCREMENT, FALSE);

            //
            //  并且完成irp
            //

            IoCompleteRequest(Irp, IO_NO_INCREMENT);
            return status;
            break;

        }

        default:
            DebugPrint((3,
               "DiskPerfDispatchPnp: Forwarding irp"));
            //
            //  简单的转发所有其他的Irp
            //
            return DiskPerfSendToNextDriver(DeviceObject, Irp);

    }

    return status;

} // end DiskPerfDispatchPnp()



NTSTATUS
DiskPerfIrpCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )

/*++

函数说明:

    转发IRP 完成方法。设置一个事件并且返回
STATUS_MORE_PROCESSING_REQUIRED,这样Irp转发的时候会等待这个事件,
然后在清理工作完成后再次完成。


参数:

    DeviceObject 是WMI驱动的设备对象。
    Irp 就是WMI的irp
    Context 是转发器等待的事件句柄 PKEVENT。

返回值:

    STATUS_MORE_PORCESSING_REQUIRED

--*/

{
    PKEVENT Event = (PKEVENT) Context;

    UNREFERENCED_PARAMETER(DeviceObject);
    UNREFERENCED_PARAMETER(Irp);

    KeSetEvent(Event, IO_NO_INCREMENT, FALSE);

    return(STATUS_MORE_PROCESSING_REQUIRED);

} // end DiskPerfIrpCompletion()



NTSTATUS
DiskPerfStartDevice(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
/*++

函数说明:

    当收到Pnp启动Irp时调用本函数。
    它将调用完成函数,初始化和注册WMI。

参数:

    DeviceObject - 指向设备对象的指针

    Irp – 指向irp的指针


返回值:

    处理开始Irp的状态

--*/
{
    PDEVICE_EXTENSION   deviceExtension;
    NTSTATUS            status;

    PAGED_CODE();

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    status = DiskPerfForwardIrpSynchronous(DeviceObject, Irp);

    DiskPerfSyncFilterWithTarget(DeviceObject,
                                 deviceExtension->TargetDeviceObject);

    //
    //  完成WMI注册
    //
    DiskPerfRegisterDevice(DeviceObject);

    //
    //  完成Irp
    //
    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return status;
}



NTSTATUS
DiskPerfRemoveDevice(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
/*++

函数说明:

    当设备被移除时调用此函数。
    首先将把自身从WMI反注册,删除自身前把自己从
    设备栈上移除。


参数:

    DeviceObject – 指向设备对象的指针

    Irp – 指向irp的指针


返回值:

    移除设备后的状态

--*/
{
    NTSTATUS            status;
    PDEVICE_EXTENSION   deviceExtension;
    PWMILIB_CONTEXT     wmilibContext;

    PAGED_CODE();

    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //
    // 首先从WMI中反注册
    //
    IoWMIRegistrationControl(DeviceObject, WMIREG_ACTION_DEREGISTER);

    //
//  尽快把计数器清零,来把结构置为不可用
//
    wmilibContext = &deviceExtension->WmilibContext;
    InterlockedExchange(
        (PLONG) &(wmilibContext->GuidCount),
        (LONG) 0);
    RtlZeroMemory(wmilibContext, sizeof(WMILIB_CONTEXT));

    status = DiskPerfForwardIrpSynchronous(DeviceObject, Irp);

    if (deviceExtension->DiskCounters) {
        ExFreePool(deviceExtension->DiskCounters);
    }

    IoDetachDevice(deviceExtension->TargetDeviceObject);
    IoDeleteDevice(DeviceObject);

    //
    // 完成Irp
    //
    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return status;
}



NTSTATUS
DiskPerfSendToNextDriver(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

函数说明:

    当本驱动不处理某个Irp时
这个函数发送Irp 到队列的下一个驱动


参数:

    DeviceObject
    Irp

返回值:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION   deviceExtension;

    IoSkipCurrentIrpStackLocation(Irp);
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);

} // end DiskPerfSendToNextDriver()


NTSTATUS
DiskPerfDispatchPower(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PDEVICE_EXTENSION deviceExtension;


#if (NTDDI_VERSION < NTDDI_VISTA)
    PoStartNextPowerIrp(Irp);
    IoSkipCurrentIrpStackLocation(Irp);

    deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
    return PoCallDriver(deviceExtension->TargetDeviceObject, Irp);
#else
    IoSkipCurrentIrpStackLocation(Irp);

    deviceExtension = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
    return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
#endif

} // end DiskPerfDispatchPower


NTSTATUS
DiskPerfForwardIrpSynchronous(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

函数说明:

    当Irp 需要底层驱动优先处理的时候,把
    本Irp发送到下一个驱动处理。

参数:

    DeviceObject
    Irp

返回值:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION   deviceExtension;
    KEVENT event;
    NTSTATUS status;

    KeInitializeEvent(&event, NotificationEvent, FALSE);
    deviceExtension = (PDEVICE_EXTENSION) DeviceObject->DeviceExtension;

    //
    // 把irp堆栈拷贝到下一个设备
    //

    IoCopyCurrentIrpStackLocationToNext(Irp);

    //
    // 设置完成方法
    //

    IoSetCompletionRoutine(Irp, DiskPerfIrpCompletion,
                            &event, TRUE, TRUE, TRUE);

    //
    // 调用更低层设备
    //

    status = IoCallDriver(deviceExtension->TargetDeviceObject, Irp);

    //
    //  等待真正完成
    //

    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = Irp->IoStatus.Status;
    }

    return status;

} // end DiskPerfForwardIrpSynchronous()



NTSTATUS
DiskPerfCreate(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

函数说明:

    这个函数接受打开命令。通过状态返回建立驱动。


参数:

    DeviceObject – 活动的上下文
    Irp          - 设备控制参数块

返回值:

    NT 状态

--*/

{
    PAGED_CODE();

    UNREFERENCED_PARAMETER(DeviceObject);

    Irp->IoStatus.Status = STATUS_SUCCESS;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return STATUS_SUCCESS;

} // end DiskPerfCreate()


NTSTATUS
DiskPerfReadWrite(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

方法说明:

    这是diskperf驱动粘帖到的磁盘的读写入口点。
    这个驱动收集统计信息并且设置完成方法,这样当
    请求完成时可以收集额外的信息。然后调用下层的
    驱动。


参数:

    DeviceObject
    Irp

返回值:

    NTSTATUS

--*/

{
    PDEVICE_EXTENSION  deviceExtension   = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION currentIrpStack   = IoGetCurrentIrpStackLocation(Irp);
#if (NTDDI_VERSION >= NTDDI_WIN7)
    ULONG              processor         = KeGetCurrentProcessorNumberEx(NULL);
#else
    ULONG              processor         = KeGetCurrentProcessorNumber();
#endif
    PDISK_PERFORMANCE  partitionCounters = NULL;
    LONG               queueLen;
    PLARGE_INTEGER     timeStamp;

//
// 因为处理器可能动态添加,确保
// 这里有当前处理器的上下文环境。
    //

    if (deviceExtension->DiskCounters != NULL &&
        processor < deviceExtension->Processors) {

        partitionCounters = (PDISK_PERFORMANCE)
                            ((PCHAR) deviceExtension->DiskCounters
                                + (processor*PROCESSOR_COUNTERS_SIZE));
    }

//
// 设备没有合适的初始化。只能盲目的传递irp
    //
    if (deviceExtension->CountersEnabled <= 0 ||
        deviceExtension->PhysicalDeviceNameBuffer[0] == 0 ||
        partitionCounters == NULL) {
        return DiskPerfSendToNextDriver(DeviceObject, Irp);
    }

    //
    // 增加队列深度计数
    //

    queueLen = InterlockedIncrement(&deviceExtension->QueueDepth);

    //
// 把当前的堆栈拷贝到下一个堆栈
//

    IoCopyCurrentIrpStackLocationToNext(Irp);

    //
    // 当前请求开始的事件戳
    //

    timeStamp = (PLARGE_INTEGER) &currentIrpStack->Parameters.Read;
    DiskPerfGetClock(*timeStamp, NULL);
    DebugPrint((10, "DiskPerfReadWrite: TS=%I64u\n", *timeStamp));

    if (queueLen == 1) {
        partitionCounters->IdleTime.QuadPart
            += timeStamp->QuadPart -
                deviceExtension->LastIdleClock.QuadPart;
        deviceExtension->LastIdleClock.QuadPart = timeStamp->QuadPart;
    }

    //
    // 设置回调完成方法
    //

    IoSetCompletionRoutine(Irp,
                           DiskPerfIoCompletion,
                           DeviceObject,
                           TRUE,
                           TRUE,
                           TRUE);

    //
    // 返回所有调用磁盘驱动的结果
    //

    return IoCallDriver(deviceExtension->TargetDeviceObject,
                        Irp);

} // end DiskPerfReadWrite()


NTSTATUS
DiskPerfIoCompletion(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP           Irp,
    IN PVOID          Context
    )

/*++

函数说明:

    完成一个IRP后,本方法从系统获取控制。
    它将计算IRP开始时间和当前时间的差异,
    然后减小队列深度


参数:

    DeviceObject –为IRP准备
    Irp          - 刚完成的I/O请求。
    Context      - 未使用

返回值:

    IRP 的状态

--*/

{
    PDEVICE_EXTENSION  deviceExtension   = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpStack          = IoGetCurrentIrpStackLocation(Irp);
#if (NTDDI_VERSION >= NTDDI_WIN7)
    ULONG              processor         = KeGetCurrentProcessorNumberEx(NULL);
#else
    ULONG              processor         = KeGetCurrentProcessorNumber();
#endif
    PDISK_PERFORMANCE  partitionCounters = NULL;
    LARGE_INTEGER      timeStampComplete;
    PLARGE_INTEGER     difference;
    LONG               queueLen;

    UNREFERENCED_PARAMETER(Context);

    if (Irp->PendingReturned) {
        IoMarkIrpPending(Irp);
    }

    //
    //  当前请求完成的事件戳
    //

    difference = (PLARGE_INTEGER) &irpStack->Parameters.Read;
    DiskPerfGetClock(timeStampComplete, NULL);
    difference->QuadPart = timeStampComplete.QuadPart - difference->QuadPart;
    DebugPrint((10, "DiskPerfIoCompletion: TS=%I64u diff %I64u\n",
                     timeStampComplete, difference->QuadPart));

//
// 减少卷的队列深度计数。
// 这个不需要自旋锁,使用Interlocked函数完成即可。
// 这是唯一合法的方法。
    //

    queueLen = InterlockedDecrement(&deviceExtension->QueueDepth);

    if (queueLen < 0) { // do not over-decrement. Only happens at start
        queueLen = InterlockedIncrement(&deviceExtension->QueueDepth);
    }
    if (queueLen == 0) {
        deviceExtension->LastIdleClock = timeStampComplete;
    }

//
// 更新计数器,但是,仅当在单处理器具有预分配上下
// 文时才使用。
    //

    if (deviceExtension->DiskCounters != NULL &&
        processor < deviceExtension->Processors) {

        partitionCounters = (PDISK_PERFORMANCE)
                            ((PCHAR) deviceExtension->DiskCounters
                                + (processor*PROCESSOR_COUNTERS_SIZE));
    }

    if (partitionCounters == NULL) {
        return STATUS_SUCCESS;
    };

    if (irpStack->MajorFunction == IRP_MJ_READ) {

        //
        // 增加这个请求的字节数。用字节来表示读取计数器。
        //

        partitionCounters->BytesRead.QuadPart += Irp->IoStatus.Information;

        //
        // 增加读取请求的处理计数器。
        //

        partitionCounters->ReadCount++;

        //
        // 计算请求处理时间。
        //

        partitionCounters->ReadTime.QuadPart += difference->QuadPart;
        DebugPrint((11, "Added RT delta %I64u total %I64u qlen=%d\n",
            difference->QuadPart, partitionCounters->ReadTime.QuadPart,
            queueLen));
    }

    else {

        //
        //增加这个请求的字节数。用字节来表示写入计数器。
        //

        partitionCounters->BytesWritten.QuadPart += Irp->IoStatus.Information;

        //
        //增加写入请求的处理计数器。
        //

        partitionCounters->WriteCount++;

        //
        //计算请求处理时间。
        //

        partitionCounters->WriteTime.QuadPart += difference->QuadPart;
        DebugPrint((11, "Added WT delta %I64u total %I64u qlen=%d\n",
            difference->QuadPart, partitionCounters->WriteTime.QuadPart,
            queueLen));
    }

    if (Irp->Flags & IRP_ASSOCIATED_IRP) {
        partitionCounters->SplitCount++;
    }

    return STATUS_SUCCESS;

} // DiskPerfIoCompletion


NTSTATUS
DiskPerfDeviceControl(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    )

/*++

函数说明:

    设备控制分派函数,只处理磁盘性能设备控制。
    所有其他的控制命令都直接发送给下层磁盘驱动。
    磁盘性能驱动返回当前的性能数据快照。


参数:

    DeviceObject – 活动上下文
    Irp          -设备控制参数快

返回值:

    返回状态。

--*/

{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION currentIrpStack = IoGetCurrentIrpStackLocation(Irp);

    DebugPrint((2, "DiskPerfDeviceControl: DeviceObject %X Irp %X\n",
                    DeviceObject, Irp));

    if (currentIrpStack->Parameters.DeviceIoControl.IoControlCode ==
        IOCTL_DISK_PERFORMANCE) {

        NTSTATUS        status;

        //
        //  校验对于性能数据来说用户缓冲区是否够大。
        //

        if (currentIrpStack->Parameters.DeviceIoControl.OutputBufferLength <
                sizeof(DISK_PERFORMANCE)) {

        //
        // 表示非成功状态,没有数据传输。
        //

        status = STATUS_BUFFER_TOO_SMALL;
        Irp->IoStatus.Information = 0;

        }

        else {
            ULONG i;
            PDISK_PERFORMANCE totalCounters;
            PDISK_PERFORMANCE diskCounters = deviceExtension->DiskCounters;
            LARGE_INTEGER frequency, perfctr;

            if (diskCounters == NULL) {
                Irp->IoStatus.Status = STATUS_UNSUCCESSFUL;
                IoCompleteRequest(Irp, IO_NO_INCREMENT);
                return STATUS_UNSUCCESSFUL;
            }

            if (InterlockedCompareExchange(&deviceExtension->EnabledAlways, 1, 0) == 0)
            {
                InterlockedIncrement(&deviceExtension->CountersEnabled);

                //
                // reset per processor counters only
                //
                if (deviceExtension->DiskCounters != NULL)
                {
                    RtlZeroMemory(deviceExtension->DiskCounters, PROCESSOR_COUNTERS_SIZE * deviceExtension->Processors);
                }

                DiskPerfGetClock(deviceExtension->LastIdleClock, NULL);

                deviceExtension->QueueDepth = 0;

                DebugPrint((10, "DiskPerfDeviceControl: LIC=%I64u\n", deviceExtension->LastIdleClock));
                DebugPrint((3, "DiskPerfDeviceControl: Counters enabled %d\n", deviceExtension->CountersEnabled));
            }

            totalCounters = (PDISK_PERFORMANCE) Irp->AssociatedIrp.SystemBuffer;
            RtlZeroMemory(totalCounters, sizeof(DISK_PERFORMANCE));
#ifdef USE_PERF_CTR
            perfctr = KeQueryPerformanceCounter(&frequency);
#endif
            KeQuerySystemTime(&totalCounters->QueryTime);

            for (i=0; i<deviceExtension->Processors; i++) {
                DiskPerfAddCounters(totalCounters, diskCounters, frequency);
                diskCounters = (PDISK_PERFORMANCE)
                               ((PCHAR) diskCounters + PROCESSOR_COUNTERS_SIZE);
            }
            totalCounters->QueueDepth = deviceExtension->QueueDepth;

            if (totalCounters->QueueDepth == 0) {
                LARGE_INTEGER difference;

                difference.QuadPart =
#ifdef USE_PERF_CTR
                    perfctr.QuadPart
#else
                   totalCounters->QueryTime.QuadPart
#endif
                        - deviceExtension->LastIdleClock.QuadPart;
                if (difference.QuadPart > 0) {
                    totalCounters->IdleTime.QuadPart +=
#ifdef USE_PERF_CTR
                        10000000 * difference.QuadPart / frequency.QuadPart;
#else
                        difference.QuadPart;
#endif
                }
            }
            totalCounters->StorageDeviceNumber
                = deviceExtension->DiskNumber;
            RtlCopyMemory(
                &totalCounters->StorageManagerName[0],
                &deviceExtension->StorageManagerName[0],
                8 * sizeof(WCHAR));
            status = STATUS_SUCCESS;
            Irp->IoStatus.Information = sizeof(DISK_PERFORMANCE);
        }

        //
        // 完成请求。
        //

        Irp->IoStatus.Status = status;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return status;

    }

    else {

        //
        // 将当前的堆栈回退1。
        //

        Irp->CurrentLocation++,
        Irp->Tail.Overlay.CurrentStackLocation++;

        //
        // Pass unrecognized device control requests
        // down to next driver layer.
        //

        return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);
    }
} // end DiskPerfDeviceControl()




NTSTATUS DiskPerfWmi(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

函数说明:

    这个函数处理WMI的信息请求。由于磁盘性能信息是只读的,
    所以总是收集数据,没有任何的事件,除非是QueryAllData,QuerySingleInstance和
    GetRegInfo请求支持的事件。


参数:

    DeviceObject – 活动的上下文
    Irp          - 设备控制参数块

返回值:

    返回的状态

--*/

{
    PIO_STACK_LOCATION      irpSp;
    NTSTATUS status;
    PWMILIB_CONTEXT wmilibContext;
    SYSCTL_IRP_DISPOSITION disposition;
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;

    PAGED_CODE();

    DebugPrint((2, "DiskPerfWmi: DeviceObject %X Irp %X\n",
                    DeviceObject, Irp));
    wmilibContext = &deviceExtension->WmilibContext;
    if (wmilibContext->GuidCount == 0)  // wmilibContext is not valid
    {
        DebugPrint((3, "DiskPerfWmi: WmilibContext invalid"));
        return DiskPerfSendToNextDriver(DeviceObject, Irp);
    }

    irpSp = IoGetCurrentIrpStackLocation(Irp);

        DebugPrint((3, "DiskPerfWmi: Calling WmiSystemControl\n"));
        status = WmiSystemControl(wmilibContext,
                                    DeviceObject,
                                    Irp,
                                    &disposition);
        switch (disposition)
        {
            case IrpProcessed:
            {
                break;
            }

            case IrpNotCompleted:
            {
                IoCompleteRequest(Irp, IO_NO_INCREMENT);
                break;
            }

//          case IrpForward:
//          case IrpNotWmi:
            default:
            {
                status = DiskPerfSendToNextDriver(DeviceObject, Irp);
                break;
            }
        }
    return(status);

}


NTSTATUS
DiskPerfShutdownFlush(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

/*++

函数说明:


    关闭或者刷新IRP时候调用本函数。在系统真正关闭之前或者系统刷新时
    发送这些IRP。


参数:

DriverObject –指向将被系统关闭的设备对象。
    Irp          -包含的Irp

返回值:

    NT 状态

--*/

{
    PDEVICE_EXTENSION  deviceExtension = DeviceObject->DeviceExtension;

    DebugPrint((2, "DiskPerfShutdownFlush: DeviceObject %X Irp %X\n",
                    DeviceObject, Irp));

    IoSkipCurrentIrpStackLocation(Irp);
    return IoCallDriver(deviceExtension->TargetDeviceObject, Irp);

} // end DiskPerfShutdownFlush()


VOID
DiskPerfUnload(
    IN PDRIVER_OBJECT DriverObject
    )

/*++

函数说明:

    释放所有分配的资源.。

参数:

    DriverObject -  指向驱动对象。

返回值:

    VOID.

--*/
{
    PAGED_CODE();

    UNREFERENCED_PARAMETER(DriverObject);

    return;
}



NTSTATUS
DiskPerfRegisterDevice(
    IN PDEVICE_OBJECT DeviceObject
    )

/*++

函数说明:

    为设备初始化合适的名字,
    并且向WMI注册。


参数:

    DeviceObject – 指向需要初始化的设备对象。

返回值:

    初始化的状态。注意:如果注册失败,
    DeviceExtension中的设备名称将为空。


--*/

{
    NTSTATUS                status;
    IO_STATUS_BLOCK         ioStatus;
    KEVENT                  event;
    PDEVICE_EXTENSION       deviceExtension;
    PIRP                    irp;
    STORAGE_DEVICE_NUMBER   number;
    ULONG                   registrationFlag = 0;

    PAGED_CODE();

    DebugPrint((2, "DiskPerfRegisterDevice: DeviceObject %X\n",
                    DeviceObject));
    deviceExtension = DeviceObject->DeviceExtension;

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    //
    //  请求设备号
    //
    irp = IoBuildDeviceIoControlRequest(
                    IOCTL_STORAGE_GET_DEVICE_NUMBER,
                    deviceExtension->TargetDeviceObject,
                    NULL,
                    0,
                    &number,
                    sizeof(number),
                    FALSE,
                    &event,
                    &ioStatus);
    if (!irp) {
        DiskPerfLogError(
            DeviceObject,
            256,
            STATUS_SUCCESS,
            IO_ERR_INSUFFICIENT_RESOURCES);
        DebugPrint((3, "DiskPerfRegisterDevice: Fail to build irp\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    status = IoCallDriver(deviceExtension->TargetDeviceObject, irp);
    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = ioStatus.Status;
    }

    if (NT_SUCCESS(status)) {

        //
        // 保存设备号,作为DiskIoNotifyRoutine的参数使用。
        //
        deviceExtension->DiskNumber = number.DeviceNumber;

        //
        // 为每个分区创建设备名字
        //

        RtlStringCbPrintfW(
            deviceExtension->PhysicalDeviceNameBuffer,
            sizeof(deviceExtension->PhysicalDeviceNameBuffer),
            L"\\Device\\Harddisk%d\\Partition%d",
            number.DeviceNumber, number.PartitionNumber);

        RtlInitUnicodeString(&deviceExtension->PhysicalDeviceName, &deviceExtension->PhysicalDeviceNameBuffer[0]);

        //
        // 为物理磁盘设置默认名字
        //
        RtlCopyMemory(
            &(deviceExtension->StorageManagerName[0]),
            L"PhysDisk",
            8 * sizeof(WCHAR));
        DebugPrint((3, "DiskPerfRegisterDevice: Device name %ws\n",
                       deviceExtension->PhysicalDeviceNameBuffer));
    }
    else {

        // request for partition's information failed, try volume

        ULONG           outputSize = sizeof(MOUNTDEV_NAME);
        PMOUNTDEV_NAME  output;
        VOLUME_NUMBER   volumeNumber;
        ULONG           nameSize;

        output = ExAllocatePool(PagedPool, outputSize);
        if (!output) {
            DiskPerfLogError(
                DeviceObject,
                257,
                STATUS_SUCCESS,
                IO_ERR_INSUFFICIENT_RESOURCES);
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        KeInitializeEvent(&event, NotificationEvent, FALSE);
        irp = IoBuildDeviceIoControlRequest(
                    IOCTL_MOUNTDEV_QUERY_DEVICE_NAME,
                    deviceExtension->TargetDeviceObject, NULL, 0,
                    output, outputSize, FALSE, &event, &ioStatus);
        if (!irp) {
            ExFreePool(output);
            DiskPerfLogError(
                DeviceObject,
                258,
                STATUS_SUCCESS,
                IO_ERR_INSUFFICIENT_RESOURCES);
            return STATUS_INSUFFICIENT_RESOURCES;
        }

        status = IoCallDriver(deviceExtension->TargetDeviceObject, irp);
        if (status == STATUS_PENDING) {
            KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
            status = ioStatus.Status;
        }

        if (status == STATUS_BUFFER_OVERFLOW) {
            outputSize = sizeof(MOUNTDEV_NAME) + output->NameLength;
            ExFreePool(output);
            output = ExAllocatePool(PagedPool, outputSize);

            if (!output) {
                DiskPerfLogError(
               

Rank: 3Rank: 3

发表于 2010-11-6 05:25:29 |显示全部楼层
还不错杀。

Rank: 3Rank: 3

发表于 2010-11-6 13:55:06 |显示全部楼层
不错不错

Rank: 3Rank: 3

发表于 2010-11-9 01:15:09 |显示全部楼层
很好啊,谢谢了。

Rank: 1

发表于 2010-11-9 01:45:43 |显示全部楼层
还不错杀。
不错不错
很好啊,谢谢了。
从来没有看到过这么好的分析.谢谢了

Rank: 2

发表于 2011-5-20 19:22:15 |显示全部楼层
通篇都是照搬DDK代码例子 英文翻成中文而已
没一个字是自己的想法
还自言自语的说不错  忘记换马甲了吧?
最后一个吧楼主自己的话又说了一遍 说不是LZ马甲都没人信

Rank: 1

发表于 2012-12-14 10:45:35 |显示全部楼层
楼主english牛啊

Rank: 1

发表于 2012-12-26 16:57:48 |显示全部楼层
还没入门,驱动比较难搞啊
您需要登录后才可以回帖 登录 | 立即加入

Archiver|手机版|第8个男人 - 论坛为只读模式,仅供查阅

GMT+8, 2019-5-19 17:38 , Processed in 0.033561 second(s), 8 queries .

Design by pvo.cn

© 2011 Pvo Inc.

回顶部