设为首页收藏本站

SKY外语、计算机论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 7351|回复: 5
打印 上一主题 下一主题

[C] (试发贴2) 驱动程序 -虚拟硬盘,将内存虚拟成硬盘[RamDisk](WDF驱动)

[复制链接]

7

主题

0

好友

174

积分

注册会员

Rank: 2

生肖
星座
天秤座
性别
跳转到指定楼层
楼主
发表于 2012-6-10 22:46:42 |只看该作者 |倒序浏览
本帖最后由 sky_yx 于 2015-12-30 14:17 编辑

// 编译以后用第一个 INF 文件 保存为 INF 文件
// 将 INF 文件和生成的SYS文件放在一起
// 在控制面板里添加硬件,选择这个INF文件
// 重启就会有个 0x100000 B 大小的虚拟硬盘 R: 盘
  1. ///////////////////SYS文件///////////////////
  2. ///////////使用C编译////////////
  3. #include <NTDDK.h>
  4. #include <ntdddisk.h>
  5. #define NTSTRSAFE_LIB
  6. #include <ntstrsafe.h>
  7. #include "ntintsafe.h"
  8. #include <WDF.h>
  9. #define NT_DEVICE_NAME                                L"\\Device\\Ramdisk"
  10. #define DOS_DEVICE_NAME                                L"\\DosDevices\\"
  11. #define RAMDISK_TAG                                        'DmaR'
  12. #define DOS_DEVNAME_LENGTH                        (sizeof(DOS_DEVICE_NAME) + sizeof(WCHAR) * 10)
  13. #define DRIVE_LETTER_LENGTH                        (sizeof(WCHAR) * 10)
  14. #define DRIVE_LETTER_BUFFER_SIZE        10
  15. #define DOS_DEVNAME_BUFFER_SIZE                (sizeof(DOS_DEVICE_NAME) / 2) + 10
  16. #define RAMDISK_MEDIA_TYPE                        0xF8
  17. #define DIR_ENTRIES_PER_SECTOR                16
  18. #define DEFAULT_DISK_SIZE                        (0x100000)                //1MB
  19. #define DEFAULT_ROOT_DIR_ENTRIES        0x200
  20. #define DEFAULT_SECTORS_PER_CLUSTER        2
  21. #define DEFAULT_DRIVE_LETTER                L"Z:"
  22. typedef struct _DISK_INFO {
  23.     ULONG   DiskSize;           // 磁盘大小,以Byte计算
  24.     ULONG   RootDirEntries;     // 磁盘上根文件系统的进入节点
  25.     ULONG   SectorsPerCluster;  // 磁盘每个簇由多少个扇区组成
  26.     UNICODE_STRING DriveLetter; // 磁盘的盘符
  27. } DISK_INFO, *PDISK_INFO;
  28. /*
  29. typedef struct _DISK_GEOMETRY {
  30.     LARGE_INTEGER Cylinders;                                //有多少个柱面
  31.     MEDIA_TYPE MediaType;                                        //磁盘介质的类型
  32.     ULONG TracksPerCylinder;                                //每个柱面有多少个磁道,也就是有多少个磁盘面
  33.     ULONG SectorsPerTrack;                                        //每个磁道有多少个扇区
  34.     ULONG BytesPerSector;                                        //每个扇区有多少个字节
  35. } DISK_GEOMETRY, *PDISK_GEOMETRY;
  36. */
  37. typedef struct _DEVICE_EXTENSION {
  38.     PUCHAR              DiskImage;                  // 指向磁盘镜像的指针
  39.     DISK_GEOMETRY       DiskGeometry;               // 磁盘特性
  40.     DISK_INFO           DiskRegInfo;                // 自定义的磁盘信息结构,安装时存放在注册表中
  41.     UNICODE_STRING      SymbolicLink;               // 这个盘的符号链接名,这是真正的符号链接名
  42.         //DiskRegInfo 中 DriverLetter 的存储空间,这是用户有注册表中指定的盘符
  43.     WCHAR               DriveLetterBuffer[DRIVE_LETTER_BUFFER_SIZE];
  44.         //SymbolicLink的存储空间
  45.     WCHAR               DosDeviceNameBuffer[DOS_DEVNAME_BUFFER_SIZE];
  46. } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
  47. WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_EXTENSION, DeviceGetExtension)
  48. typedef struct _QUEUE_EXTENSION {
  49.     PDEVICE_EXTENSION DeviceExtension;
  50. } QUEUE_EXTENSION, *PQUEUE_EXTENSION;
  51. WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(QUEUE_EXTENSION, QueueGetExtension)
  52. #pragma pack(1)
  53. typedef struct  _BOOT_SECTOR
  54. {
  55.     UCHAR       bsJump[3];          // X86跳转指令,跳转到 DBR 中的引导程序
  56.     CCHAR       bsOemName[8];       // 这个卷的 OEM 名称
  57.     USHORT      bsBytesPerSec;      // 每个扇区有多少个字节
  58.     UCHAR       bsSecPerClus;       // 每个簇有多少个扇区
  59.     USHORT      bsResSectors;       // 保留扇区数目,指的是第一个 FAT 表开始前的扇区数,包括 DBT 本身
  60.     UCHAR       bsFATs;             // 这个卷有多少个 FAT 表,在这里为 1
  61.     USHORT      bsRootDirEnts;      // 这个卷的根入口点有几个
  62.     USHORT      bsSectors;          // 这个卷一共有多少个扇区,对大于 65535 个扇区的卷,这个字段为 0
  63.     UCHAR       bsMedia;            // 这个卷的介质类型,在这里为 RAMDISK_MEDIA_TYPE
  64.     USHORT      bsFATsecs;          // 每个 FAT 表占用多少个扇区
  65.     USHORT      bsSecPerTrack;      // 每个磁道有多少个扇区,在这里为 32
  66.     USHORT      bsHeads;            // 有多少个磁头,在这里为 2
  67.     ULONG       bsHiddenSecs;       // 有多少个隐藏扇区,在这里设置为 0
  68.     ULONG       bsHugeSectors;      // 一个卷超过 65535 个扇区,会使用这个字段来说明总扇区数
  69.     UCHAR       bsDriveNumber;      // 驱动器编号,无用
  70.     UCHAR       bsReserved1;        // 保留字段
  71.     UCHAR       bsBootSignature;    // 磁盘扩展引导区标签,Windows 要求这个标签为 0x28 或 0x29
  72.     ULONG       bsVolumeID;         // 磁盘卷ID,在这里设置为 0x12345678
  73.     CCHAR       bsLabel[11];        // 磁盘卷标
  74.     CCHAR       bsFileSystemType[8];// 磁盘上的文件系统类型
  75.     CCHAR       bsReserved2[448];   // 保留字段
  76.     UCHAR       bsSig2[2];          // DBR 结束签名 - 0x55, 0xAA
  77. }   BOOT_SECTOR, *PBOOT_SECTOR;
  78. typedef struct  _DIR_ENTRY
  79. {
  80.     UCHAR       deName[8];          // 文件名
  81.     UCHAR       deExtension[3];     // 文件扩展名
  82.     UCHAR       deAttributes;       // 文件属性
  83.     UCHAR       deReserved;         // 系统保留
  84.     USHORT      deTime;             // 文件建立时间
  85.     USHORT      deDate;             // 文件建立日期
  86.     USHORT      deStartCluster;     // 文件的第一个簇的编号
  87.     ULONG       deFileSize;         // 文件大小
  88. }   DIR_ENTRY, *PDIR_ENTRY;
  89. #pragma pack()
  90. #define DIR_ATTR_READONLY   0x01
  91. #define DIR_ATTR_HIDDEN     0x02
  92. #define DIR_ATTR_SYSTEM     0x04
  93. #define DIR_ATTR_VOLUME     0x08
  94. #define DIR_ATTR_DIRECTORY  0x10
  95. #define DIR_ATTR_ARCHIVE    0x20
  96. BOOLEAN CheckParameters(IN PDEVICE_EXTENSION pDeviceExtension,
  97.                                                 IN LARGE_INTEGER liByteOffset,
  98.                                                 IN size_t Length)
  99. {
  100.         if(pDeviceExtension->DiskRegInfo.DiskSize < Length ||
  101.                 liByteOffset.QuadPart < 0 ||
  102.                 ((ULONGLONG)liByteOffset.QuadPart > (pDeviceExtension->DiskRegInfo.DiskSize - Length)) ||
  103.                 (Length & (pDeviceExtension->DiskGeometry.BytesPerSector - 1)))
  104.         {
  105.                 KdPrint(("参数错误\n 偏移:[%X]\n 长度:[%d]\n",
  106.                         liByteOffset,Length));
  107.                 return FALSE;
  108.         }
  109.         return TRUE;
  110. }
  111. VOID DeviceRead(IN WDFQUEUE WdfQueue,
  112.                                 IN WDFREQUEST WdfRequest,
  113.                                 IN size_t Length)
  114. {
  115.         //从队列的扩展域中获取到对应的磁盘设备的设备扩展
  116.         PDEVICE_EXTENSION                pDeviceExtension        = QueueGetExtension(WdfQueue)->DeviceExtension;
  117.         //用于各种函数返回值的状态变量
  118.         NTSTATUS                                ntStatus                        = STATUS_INVALID_PARAMETER;
  119.         //用于获取请求参数的变量
  120.         WDF_REQUEST_PARAMETERS        WdfRequestParameters;
  121.         //用于获取读请求起始地址的变量
  122.         LARGE_INTEGER                        liByteOffset;
  123.         //这是一个用于获取读缓冲区的内存句柄
  124.         WDFMEMORY                                hMemory;
  125.         //初始化参数变量,为之后从请求参数中获取各种信息做准备
  126.         WDF_REQUEST_PARAMETERS_INIT(&WdfRequestParameters);
  127.         //从请求参数中获取信息
  128.         WdfRequestGetParameters(WdfRequest,&WdfRequestParameters);
  129.         //将请求参数中读的起始位置取出来
  130.         liByteOffset.QuadPart = WdfRequestParameters.Parameters.Read.DeviceOffset;
  131.         //这里是自己实现一个参数检查函数.由于读取范围不能超过磁盘镜像大小
  132.         //且必须是扇区对齐,所以这里需要有一个参数检查,如果检查失败,
  133.         //则直接将这个错误参数(STATUS_INVALID_PARAMETER)为返回值结束
  134.         if(CheckParameters(pDeviceExtension,liByteOffset,Length))
  135.         {
  136.                 //从请求参数中获取读缓冲区的内存句柄
  137.                 ntStatus = WdfRequestRetrieveOutputMemory(WdfRequest,&hMemory);
  138.                 if(NT_SUCCESS(ntStatus))
  139.                         //根据之前获取到的参数进行内存拷贝,填写这个读请求缓冲区
  140.                         //从而完成这个读请求的操作
  141.                         ntStatus = WdfMemoryCopyFromBuffer(hMemory,
  142.                         0,pDeviceExtension->DiskImage + liByteOffset.LowPart,Length);
  143.         }
  144.         //结束这个读请求,这里要注意的是,需要将读取的长度作为返回的信息一并返回
  145.         WdfRequestCompleteWithInformation(WdfRequest,ntStatus,(ULONG_PTR)Length);
  146. }
  147. VOID DeviceWrite(IN WDFQUEUE WdfQueue,
  148.                                  IN WDFREQUEST WdfRequest,
  149.                                  IN size_t Length)
  150. {
  151.         //从队列的扩展域中获取到对应的磁盘设备的设备扩展
  152.         PDEVICE_EXTENSION                pDeviceExtension        = QueueGetExtension(WdfQueue)->DeviceExtension;
  153.         //用于各种函数返回值的状态变量
  154.         NTSTATUS                                ntStatus                        = STATUS_INVALID_PARAMETER;
  155.         //用于获取请求参数的变量
  156.         WDF_REQUEST_PARAMETERS        WdfRequestParameters;
  157.         //用于获取写请求起始地址的变量
  158.         LARGE_INTEGER                        liByteOffset;
  159.         //这是一个用于获取写缓冲区的内存句柄
  160.         WDFMEMORY                                hMemory;
  161.         //初始化参数变量,为之后从请求参数中获取各种信息做准备
  162.         WDF_REQUEST_PARAMETERS_INIT(&WdfRequestParameters);
  163.         //从请求参数中获取信息
  164.         WdfRequestGetParameters(WdfRequest,&WdfRequestParameters);
  165.         //将请求参数中写的起始位置取出来
  166.         liByteOffset.QuadPart = WdfRequestParameters.Parameters.Write.DeviceOffset;
  167.         //这里是自己实现一个参数检查函数.由于写入范围不能超过磁盘镜像大小
  168.         //且必须是扇区对齐,所以这里需要有一个参数检查,如果检查失败,
  169.         //则直接将这个错误参数(STATUS_INVALID_PARAMETER)为返回值结束
  170.         if(CheckParameters(pDeviceExtension,liByteOffset,Length))
  171.         {
  172.                 //从请求参数中获写入缓冲区的内存句柄
  173.                 ntStatus = WdfRequestRetrieveInputMemory(WdfRequest,&hMemory);
  174.                 if(NT_SUCCESS(ntStatus))
  175.                         //根据之前获取到的参数进行内存拷贝,写入内存磁盘数据
  176.                         //从而完成这个写请求的操作
  177.                         ntStatus = WdfMemoryCopyToBuffer(hMemory,
  178.                         0,pDeviceExtension->DiskImage + liByteOffset.LowPart,Length);
  179.         }
  180.         //结束这个写请求,这里要注意的是,需要将写入的长度作为返回的信息一并返回
  181.         WdfRequestCompleteWithInformation(WdfRequest,ntStatus,(ULONG_PTR)Length);
  182. }
  183. VOID DeviceControl(IN WDFQUEUE WdfQueue,
  184.                                    IN WDFREQUEST WdfRequest,
  185.                                    IN size_t OutputBufferLength,
  186.                                    IN size_t InputBufferlength,
  187.                                    IN ULONG IoControlCode)
  188. {
  189.         //初始化返回状态为非法的设备请求,这样在其它无关紧要的,不需要处理的
  190.         //DeviceIoControl 请求到来时,可以直接返回这个状态
  191.         NTSTATUS                                ntStatus = STATUS_INVALID_DEVICE_REQUEST;
  192.         //用来存放返回的DeviceIoControl所要求的长度
  193.         ULONG_PTR                                ulInformation = 0;
  194.         //中间变量
  195.         size_t                                        sBufferSize;
  196.         //和读/写回调函数相同,也通过队列的扩展来获取设备的扩展
  197.         PDEVICE_EXTENSION                pDeviceExtension = QueueGetExtension(WdfQueue)->DeviceExtension;
  198.         //由于我们对发过来的请求长度很有信心(因为是Windows标准请求),
  199.         //所以这里不需要输入和输出缓冲区的长度
  200.         UNREFERENCED_PARAMETER(OutputBufferLength);
  201.         UNREFERENCED_PARAMETER(InputBufferlength);
  202.         //判断是哪个DeviceIoControl请求
  203.         switch(IoControlCode)
  204.         {
  205.                 //这是一个获取当前分区信息的DeviceIoControl请求,需要处理
  206.         case IOCTL_DISK_GET_PARTITION_INFO:
  207.                 {
  208.                         //首先声明一个输出缓冲区指针
  209.                         PPARTITION_INFORMATION pOutputBuffer;
  210.                         //由于这个DeviceIoControl请求所需的信息大部分是从DBR中获取的
  211.                         //所以需要一个指针DBR的指针
  212.                         PBOOT_SECTOR pBootSector = (PBOOT_SECTOR)pDeviceExtension->DiskImage;
  213.                         //这是将要返回的信息长度,它会被上层发出DeviceIoControl请求的设备收到
  214.                         ulInformation = sizeof(PARTITION_INFORMATION);
  215.                         //通过框架函数来获取这个DeviceIoControl请求所携带的输出缓冲区
  216.                         ntStatus = WdfRequestRetrieveOutputBuffer(WdfRequest,
  217.                                 sizeof(PARTITION_INFORMATION),(PVOID*)&pOutputBuffer,&sBufferSize);
  218.                         //在获取缓冲区成功的情况下,将DBR中的相关信息填入缓冲区
  219.                         if(NT_SUCCESS(ntStatus))
  220.                         {
  221.                                 pOutputBuffer->PartitionType =
  222.                                         (pBootSector->bsFileSystemType[4] == '6') ? PARTITION_FAT_16 :
  223.                                         PARTITION_FAT_12;
  224.                                 //还需要根据这个驱动的现实情况来"编造"一些数据
  225.                                 pOutputBuffer->BootIndicator                = FALSE;
  226.                                 pOutputBuffer->RecognizedPartition        = TRUE;
  227.                                 pOutputBuffer->RewritePartition                = FALSE;
  228.                                 pOutputBuffer->StartingOffset.QuadPart = 0;
  229.                                 pOutputBuffer->PartitionLength.QuadPart = pDeviceExtension->DiskRegInfo.DiskSize;
  230.                                 pOutputBuffer->HiddenSectors                = (ULONG)(1L);
  231.                                 pOutputBuffer->PartitionNumber                = (ULONG)(-1L);
  232.                                 //最后由于成功地填充了缓冲区,因此将这个请求的状态设为成功
  233.                                 ntStatus = STATUS_SUCCESS;
  234.                         }
  235.                 }
  236.                 break;
  237.         case IOCTL_DISK_GET_DRIVE_GEOMETRY:
  238.                 {
  239.                         //首先声明一个输出缓冲区指针
  240.                         PDISK_GEOMETRY pOutputBuffer;
  241.                         //这是将要返回的信息的长度,它会被上层发出 DeviceIoControl 请求的设备收到
  242.                         ulInformation = sizeof(DISK_GEOMETRY);
  243.                         //通过框架函数来获取这个DeviceIoControl请求所携带的输出缓冲区
  244.                         ntStatus = WdfRequestRetrieveOutputBuffer(WdfRequest,
  245.                                 sizeof(DISK_GEOMETRY),(PVOID*)&pOutputBuffer,&sBufferSize);
  246.                         //在获取缓冲区成功的情况下,将相关信息填入缓冲区
  247.                         if(NT_SUCCESS(ntStatus))
  248.                         {
  249.                                 //这里实际上就是填入之前初始化好的磁盘几何信息
  250.                                 RtlCopyMemory(pOutputBuffer,
  251.                                         &(pDeviceExtension->DiskGeometry),
  252.                                         sizeof(DISK_GEOMETRY));
  253.                                 ntStatus = STATUS_SUCCESS;
  254.                         }
  255.                 }
  256.                 break;
  257.         //对于这两个 DeviceIoControl 请求,直接返回成功,因为这两个请求是不需要其它信息的
  258.         case IOCTL_DISK_CHECK_VERIFY:
  259.         case IOCTL_DISK_IS_WRITABLE:
  260.                 ntStatus = STATUS_SUCCESS;
  261.                 break;
  262.         }
  263.         //结束这个DeviceIoControl请求,需要将读取的长度作为返回的信息一并返回
  264.         WdfRequestCompleteWithInformation(WdfRequest,ntStatus,ulInformation);
  265. }
  266. NTSTATUS FormatDisk(IN PDEVICE_EXTENSION pDeviceExtension)
  267. {
  268.         PBOOT_SECTOR        pBootSector = (PBOOT_SECTOR) pDeviceExtension->DiskImage;
  269.         //一个指向第一个FAT表的指针
  270.         PUCHAR                        pFirstFatSector;
  271.         //用于记录有多少个根目录入口点
  272.         ULONG                        RootDirEntries;
  273.         //用于记录每个族由多少个扇区组成
  274.         ULONG                        SectorsPerCluster;
  275.         //用于记录FAT文件系统的类型,是FAT12还是FAT16
  276.         USHORT                        FatType;
  277.         //用于记录在FAT表里面一共有多少个表项
  278.         USHORT                        FatEntries;
  279.         //用于记录一个FAT表需要占用多少个扇区来存储
  280.         USHORT                        FatSectorCnt;
  281.         //用于指向第一个根目录入口点
  282.         PDIR_ENTRY                pRootDir;
  283.         //用于确定这个函数是可以存取分页内存的
  284.         PAGED_CODE();
  285.         //用于确定这个盘的引导扇区的大小确实是一个扇区
  286.         ASSERT(sizeof(BOOT_SECTOR) == 512);
  287.         //用于确定我们操作的磁盘镜像不是一个不可用的指针
  288.         ASSERT(pDeviceExtension->DiskImage != NULL);
  289.         //清空磁盘镜像
  290.         RtlZeroMemory(pDeviceExtension->DiskImage,pDeviceExtension->DiskRegInfo.DiskSize);
  291.         //每个扇区有512字节
  292.         pDeviceExtension->DiskGeometry.BytesPerSector = 0x200;
  293.         //每个磁道有32个扇区
  294.         pDeviceExtension->DiskGeometry.SectorsPerTrack = 0x20;
  295.         //每个柱面有两个磁道
  296.         pDeviceExtension->DiskGeometry.TracksPerCylinder = 0x02;
  297.         //柱面数目由磁盘总容量计算得到
  298.         pDeviceExtension->DiskGeometry.Cylinders.QuadPart =
  299.                 pDeviceExtension->DiskRegInfo.DiskSize / 0x200 / 0x20 / 0x02;
  300.         //磁盘的介质类型是我们自己定义的 RAMDISK_MEDIA_TYPE
  301.         pDeviceExtension->DiskGeometry.MediaType = (MEDIA_TYPE)RAMDISK_MEDIA_TYPE;
  302.         KdPrint(("柱面数:[%ld]\n 每柱面磁道数:[%ld]\n 每磁道扇区数:[%ld]\n 每扇区字节数:[%ld]\n",
  303.                 pDeviceExtension->DiskGeometry.Cylinders.QuadPart,
  304.                 pDeviceExtension->DiskGeometry.TracksPerCylinder,
  305.                 pDeviceExtension->DiskGeometry.SectorsPerTrack,
  306.                 pDeviceExtension->DiskGeometry.BytesPerSector));
  307.         //根据用户的指定值对根目录项的数目进行初使化
  308.         RootDirEntries = pDeviceExtension->DiskRegInfo.RootDirEntries;
  309.         //根据用户的指定值对每个簇有多少个扇区进行初使化
  310.         SectorsPerCluster = pDeviceExtension->DiskRegInfo.SectorsPerCluster;
  311.         //由于根目录入口点只使用 32 字节,但是最少占用一个扇区,这里是为了充分利用空间
  312.         //在用户指定的数目不合适时,会修正这个数目,以使扇区空间得到充分的利用
  313.         if(RootDirEntries & (DIR_ENTRIES_PER_SECTOR - 1))
  314.                 RootDirEntries = (RootDirEntries + (DIR_ENTRIES_PER_SECTOR - 1)) &
  315.                 ~(DIR_ENTRIES_PER_SECTOR -1);
  316.         KdPrint(("根目录入口点数:[%ld]\n 每个簇扇区数:[%ld]\n",
  317.                 RootDirEntries,SectorsPerCluster));
  318.         //对于一开始的跳转指令成员填入硬编码的指令,这是Windows指定的
  319.         pBootSector->bsJump[0] = 0xEB;
  320.         pBootSector->bsJump[1] = 0x3C;
  321.         pBootSector->bsJump[2] = 0x90;
  322.         //OEM 名称成员
  323.         pBootSector->bsOemName[0] = 'B';
  324.         pBootSector->bsOemName[1] = 'e';
  325.         pBootSector->bsOemName[2] = 'a';
  326.         pBootSector->bsOemName[3] = 'c';
  327.         pBootSector->bsOemName[4] = 'o';
  328.         pBootSector->bsOemName[5] = 'n';
  329.         pBootSector->bsOemName[6] = ' ';
  330.         pBootSector->bsOemName[7] = ' ';
  331.         //每个扇区有多少字节,这个成员的数值直接取自之前初始化的磁盘信息数据结构
  332.         pBootSector->bsBytesPerSec        = (USHORT)pDeviceExtension->DiskGeometry.BytesPerSector;
  333.         //这个卷只有一个保留扇区,即 DBR 本身
  334.         pBootSector->bsResSectors        = 1;
  335.         //和正常的卷不同,为了节省空间,我们只存放一份 FAT 表,而不是通常的两份
  336.         pBootSector->bsFATs                        = 1;
  337.         //根目录入口点数目由之前的计算得知
  338.         pBootSector->bsRootDirEnts        = (USHORT)RootDirEntries;
  339.         //这个磁盘的总扇区数由磁盘总大小和每个扇区的字节数计算得到
  340.         pBootSector->bsSectors                =
  341.                 (USHORT)(pDeviceExtension->DiskRegInfo.DiskSize /
  342.                 pDeviceExtension->DiskGeometry.BytesPerSector);
  343.         //这个磁盘介质类型由之前初始化的磁盘信息得到
  344.         pBootSector->bsMedia                = (UCHAR)pDeviceExtension->DiskGeometry.MediaType;
  345.         //每个簇有多少个扇区,由之前的计算初始化得到
  346.         pBootSector->bsSecPerClus        = (UCHAR)SectorsPerCluster;
  347.         //FAT表的表项数目是总扇区数减去保留扇区数,再减去根目录入口点所占用的扇区数
  348.         //然后除以每个簇的扇区数.最后的结果需要加 2,因为FAT表中第0项和第1项是保留的
  349.         FatEntries = (pBootSector->bsSectors -
  350.                 pBootSector->bsResSectors -
  351.                 pBootSector->bsRootDirEnts /
  352.                 DIR_ENTRIES_PER_SECTOR) /
  353.                 pBootSector->bsSecPerClus + 2;
  354.         //如果FAT表的表项数大于 4087 ,就使用 FAT16 文件系统,反之使用 FAT12 文件系统
  355.         if(FatEntries > 4087)
  356.         {
  357.                 FatType = 16;
  358.                 //修正
  359.                 FatSectorCnt        = (FatEntries * 2 + 511) / 512;
  360.                 FatEntries                = FatEntries + FatSectorCnt;
  361.                 FatSectorCnt        = (FatEntries * 2 + 511) / 512;
  362.         }
  363.         else
  364.         {
  365.                 FatType = 12;
  366.                 //修正
  367.                 FatSectorCnt        =(((FatEntries * 3 + 1) / 2) + 511) / 512;
  368.                 FatEntries                = FatEntries + FatSectorCnt;
  369.                 FatSectorCnt        =(((FatEntries * 3 + 1) / 2) + 511) / 512;
  370.         }
  371.         //初始化FAT表所占用的分区数
  372.         pBootSector->bsFATsecs                = FatSectorCnt;
  373.         //初始化DBR中每个磁道的扇区数
  374.         pBootSector->bsSecPerTrack        = (USHORT)pDeviceExtension->DiskGeometry.SectorsPerTrack;
  375.         //初始化磁头数,也就是每个柱面的磁道数
  376.         pBootSector->bsHeads                = (USHORT)pDeviceExtension->DiskGeometry.TracksPerCylinder;
  377.         //初始化启动签名,Windows要求是 0x28 或 0x29
  378.         pBootSector->bsBootSignature = 0x29;
  379.         //随便写一个卷的ID
  380.         pBootSector->bsVolumeID                = 0x12345678;
  381.         //将卷标设置成 "RamDisk"
  382.         pBootSector->bsLabel[0]                = 'R';
  383.         pBootSector->bsLabel[1]                = 'a';
  384.         pBootSector->bsLabel[2]                = 'm';
  385.         pBootSector->bsLabel[3]                = 'D';
  386.         pBootSector->bsLabel[4]                = 'i';
  387.         pBootSector->bsLabel[5]                = 's';
  388.         pBootSector->bsLabel[6]                = 'k';
  389.         pBootSector->bsLabel[7]                = ' ';
  390.         pBootSector->bsLabel[8]                = ' ';
  391.         pBootSector->bsLabel[9]                = ' ';
  392.         pBootSector->bsLabel[10]        = ' ';
  393.         //根据我们之前计算得出的结果来选择到底是 FAT12 还是 FAT16 文件系统
  394.         pBootSector->bsFileSystemType[0]        = 'F';
  395.         pBootSector->bsFileSystemType[1]        = 'A';
  396.         pBootSector->bsFileSystemType[2]        = 'T';
  397.         pBootSector->bsFileSystemType[3]        = '1';
  398.         pBootSector->bsFileSystemType[4]        = '?';
  399.         pBootSector->bsFileSystemType[5]        = ' ';
  400.         pBootSector->bsFileSystemType[6]        = ' ';
  401.         pBootSector->bsFileSystemType[7]        = ' ';
  402.         pBootSector->bsFileSystemType[4]        = (FatType == 16) ? '6' : '2';
  403.         //签署DBR最后的标志, 0x55AA
  404.         pBootSector->bsSig2[0]        = 0x55;
  405.         pBootSector->bsSig2[1]        = 0xAA;
  406.         //定位到FAT表的起始点,这里的定位方式是利用了DBR只有一个扇区这个条件
  407.         pFirstFatSector                = (PUCHAR)(pBootSector + 1);
  408.         //填写介质类型标识
  409.         pFirstFatSector[0]        = (UCHAR)pDeviceExtension->DiskGeometry.MediaType;
  410.         pFirstFatSector[1]        = 0xFF;
  411.         pFirstFatSector[2]        = 0xFF;
  412.         //注意:如果是FAT16,那么每个FAT表的表项是 4 字节
  413.         if(FatType == 16) pFirstFatSector[3] = 0xFF;
  414.         //由于紧跟着FAT表,所以根目录入口点的表的起始位置很容易定位
  415.         pRootDir = (PDIR_ENTRY)(pBootSector + 1 + FatSectorCnt);
  416.         //初使化卷标
  417.         pRootDir->deName[0] = 'M';
  418.         pRootDir->deName[1] = 'S';
  419.         pRootDir->deName[2] = '-';
  420.         pRootDir->deName[3] = 'R';
  421.         pRootDir->deName[4] = 'A';
  422.         pRootDir->deName[5] = 'M';
  423.         pRootDir->deName[6] = 'D';
  424.         pRootDir->deName[7] = 'R';
  425.         pRootDir->deExtension[0] = 'I';
  426.         pRootDir->deExtension[1] = 'V';
  427.         pRootDir->deExtension[2] = 'E';
  428.         //将这个入口点的属性设置为卷标属性
  429.         pRootDir->deAttributes = DIR_ATTR_VOLUME;
  430.         return STATUS_SUCCESS;
  431. }
  432. VOID DeviceCleanup(IN WDFOBJECT WdfObject)
  433. {
  434.         PDEVICE_EXTENSION pDeviceExtension = DeviceGetExtension(WdfObject);
  435.         PAGED_CODE();
  436.         if(pDeviceExtension->DiskImage)
  437.                 ExFreePool(pDeviceExtension->DiskImage);
  438. }
  439. VOID QueryParameters(IN PWSTR RegistryPath,
  440.                                          IN PDISK_INFO DiskRegInfo)
  441. {
  442.     RTL_QUERY_REGISTRY_TABLE rtlQueryRegTbl[5 + 1];  // Need 1 for NULL
  443.     NTSTATUS                 Status;
  444.     DISK_INFO                defDiskRegInfo;
  445.     PAGED_CODE();
  446.     ASSERT(RegistryPath != NULL);
  447.     // Set the default values
  448.     defDiskRegInfo.DiskSize          = DEFAULT_DISK_SIZE;
  449.     defDiskRegInfo.RootDirEntries    = DEFAULT_ROOT_DIR_ENTRIES;
  450.     defDiskRegInfo.SectorsPerCluster = DEFAULT_SECTORS_PER_CLUSTER;
  451.     RtlInitUnicodeString(&defDiskRegInfo.DriveLetter, DEFAULT_DRIVE_LETTER);
  452.     RtlZeroMemory(rtlQueryRegTbl, sizeof(rtlQueryRegTbl));
  453.     //
  454.     // Setup the query table
  455.     //
  456.     rtlQueryRegTbl[0].Flags         = RTL_QUERY_REGISTRY_SUBKEY;
  457.     rtlQueryRegTbl[0].Name          = L"Parameters";
  458.     rtlQueryRegTbl[0].EntryContext  = NULL;
  459.     rtlQueryRegTbl[0].DefaultType   = (ULONG_PTR)NULL;
  460.     rtlQueryRegTbl[0].DefaultData   = NULL;
  461.     rtlQueryRegTbl[0].DefaultLength = (ULONG_PTR)NULL;
  462.     //
  463.     // Disk paramters
  464.     //
  465.     rtlQueryRegTbl[1].Flags         = RTL_QUERY_REGISTRY_DIRECT;
  466.     rtlQueryRegTbl[1].Name          = L"DiskSize";
  467.     rtlQueryRegTbl[1].EntryContext  = &DiskRegInfo->DiskSize;
  468.     rtlQueryRegTbl[1].DefaultType   = REG_DWORD;
  469.     rtlQueryRegTbl[1].DefaultData   = &defDiskRegInfo.DiskSize;
  470.     rtlQueryRegTbl[1].DefaultLength = sizeof(ULONG);
  471.     rtlQueryRegTbl[2].Flags         = RTL_QUERY_REGISTRY_DIRECT;
  472.     rtlQueryRegTbl[2].Name          = L"RootDirEntries";
  473.     rtlQueryRegTbl[2].EntryContext  = &DiskRegInfo->RootDirEntries;
  474.     rtlQueryRegTbl[2].DefaultType   = REG_DWORD;
  475.     rtlQueryRegTbl[2].DefaultData   = &defDiskRegInfo.RootDirEntries;
  476.     rtlQueryRegTbl[2].DefaultLength = sizeof(ULONG);
  477.     rtlQueryRegTbl[3].Flags         = RTL_QUERY_REGISTRY_DIRECT;
  478.     rtlQueryRegTbl[3].Name          = L"SectorsPerCluster";
  479.     rtlQueryRegTbl[3].EntryContext  = &DiskRegInfo->SectorsPerCluster;
  480.     rtlQueryRegTbl[3].DefaultType   = REG_DWORD;
  481.     rtlQueryRegTbl[3].DefaultData   = &defDiskRegInfo.SectorsPerCluster;
  482.     rtlQueryRegTbl[3].DefaultLength = sizeof(ULONG);
  483.     rtlQueryRegTbl[4].Flags         = RTL_QUERY_REGISTRY_DIRECT;
  484.     rtlQueryRegTbl[4].Name          = L"DriveLetter";
  485.     rtlQueryRegTbl[4].EntryContext  = &DiskRegInfo->DriveLetter;
  486.     rtlQueryRegTbl[4].DefaultType   = REG_SZ;
  487.     rtlQueryRegTbl[4].DefaultData   = defDiskRegInfo.DriveLetter.Buffer;
  488.     rtlQueryRegTbl[4].DefaultLength = 0;
  489.     Status = RtlQueryRegistryValues(
  490.                  RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
  491.                  RegistryPath,
  492.                  rtlQueryRegTbl,
  493.                  NULL,
  494.                  NULL
  495.              );
  496.     if (NT_SUCCESS(Status) == FALSE) {
  497.         DiskRegInfo->DiskSize          = defDiskRegInfo.DiskSize;
  498.         DiskRegInfo->RootDirEntries    = defDiskRegInfo.RootDirEntries;
  499.         DiskRegInfo->SectorsPerCluster = defDiskRegInfo.SectorsPerCluster;
  500.         RtlCopyUnicodeString(&DiskRegInfo->DriveLetter, &defDiskRegInfo.DriveLetter);
  501.     }
  502.     KdPrint(("DiskSize          = 0x%lx\n", DiskRegInfo->DiskSize));
  503.     KdPrint(("RootDirEntries    = 0x%lx\n", DiskRegInfo->RootDirEntries));
  504.     KdPrint(("SectorsPerCluster = 0x%lx\n", DiskRegInfo->SectorsPerCluster));
  505.     KdPrint(("DriveLetter       = %wZ\n",   &(DiskRegInfo->DriveLetter)));
  506.     return;
  507. }
  508. NTSTATUS DeviceAdd(IN WDFDRIVER WDFDriver,IN PWDFDEVICE_INIT pWDFDeviceInit)
  509. {
  510.         //将要建立的设备对象的属性描述变量
  511.         WDF_OBJECT_ATTRIBUTES WdfDeviceAttributes;
  512.         //将要调用的各种函数的状态返回值
  513.         NTSTATUS ntStatus;
  514.         //将要建立的设备
  515.         WDFDEVICE WdfDevice;
  516.         //将要建立的队列对象属性描述变量
  517.         WDF_OBJECT_ATTRIBUTES WdfQueueAttributes;
  518.         //将要建立的队列配置变量
  519.         WDF_IO_QUEUE_CONFIG WdfIoQueueConfig;
  520.         //这个设备所对应的设备扩展域的指针
  521.         PDEVICE_EXTENSION pDeviceExtension;
  522.         //将要建立的队列扩展域的指针
  523.         PQUEUE_EXTENSION pQueueExtension = NULL;
  524.         //将要建立的队列
  525.         WDFQUEUE WdfQueue;
  526.         //声明一个UNICODE_STRING类型的变量 ntDeviceName,并且将它初始化为
  527.         //NT_DEVICE_ANEM 宏所说明的字符串,这里实际上是 L"\\Device\\Ramdisk"
  528.     DECLARE_CONST_UNICODE_STRING(ntDeviceName, NT_DEVICE_NAME);
  529.         //保证这个函数可以操作分页内存
  530.         PAGED_CODE();
  531.         //由于我们不使用WdfDriver这个参数,为了避免编译警告,加入下面这句
  532.         UNREFERENCED_PARAMETER(WDFDriver);
  533.         //首先需要为这个设备指定一个名称,这里使用刚才声明的 UNICODE_STRING 变量
  534.         ntStatus = WdfDeviceInitAssignName(pWDFDeviceInit,&ntDeviceName);
  535.         if(!NT_SUCCESS(ntStatus))return ntStatus;
  536.         //接下来需要对这个设备进行一些属性的设置
  537.         //包括设备类型,IO操作类型和设置的排它方式
  538.         WdfDeviceInitSetDeviceType(pWDFDeviceInit,FILE_DEVICE_DISK);
  539.         WdfDeviceInitSetIoType(pWDFDeviceInit,WdfDeviceIoDirect);
  540.         WdfDeviceInitSetExclusive(pWDFDeviceInit,FALSE);
  541.         //下面来指定这个设备的设备对象扩展,这里的 DEVICE_EXTENSION 是一个在前面声明好的结构体数据类型
  542.         //我们使用一个WDF_OBJECT_ATTRIBUTES类型的变量并用其设置好DEVICE_EXTENSION
  543.         WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&WdfDeviceAttributes,DEVICE_EXTENSION);
  544.         //下面还要将用这个WDF_OBJECT_ATTRIBUTES类型的变量来指定这个设备的清除回调函数
  545.         //这个WDF_OBJECT_ATTRIBUTES类型的变量将会在下面建立设备时作为参数传进去
  546.         WdfDeviceAttributes.EvtCleanupCallback = DeviceCleanup;
  547.         //到这里所有的准备工作都已经就绪,可以开始真正建立这个设备了
  548.         //建立出的设备保存在WdfDevice这个局部变量中
  549.         ntStatus = WdfDeviceCreate(&pWDFDeviceInit,&WdfDeviceAttributes,&WdfDevice);
  550.         if(!NT_SUCCESS(ntStatus))return ntStatus;
  551.         //这个 pDeviceExtension 是之前声明的一个局部指针变量,将其指向新建立的设备的设备扩展域
  552.         pDeviceExtension = DeviceGetExtension(WdfDevice);
  553.        
  554.         //将队列的配置变量初始化为默认值
  555.         WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&WdfIoQueueConfig,WdfIoQueueDispatchSequential);
  556.         //由于我们对发往这个设备的 DeviceIoControl 请求和读/写请求感兴趣
  557.         //所以将这三个请求的处理函数设置为自己的函数,其余的请求使用默认值
  558.         WdfIoQueueConfig.EvtIoDeviceControl = DeviceControl;
  559.         WdfIoQueueConfig.EvtIoRead = DeviceRead;
  560.         WdfIoQueueConfig.EvtIoWrite = DeviceWrite;
  561.         //指定这个队列的队列对象扩展,这里的 QUEUE_EXTENSION 是之前声明好的结构体数据类型
  562.         WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&WdfQueueAttributes,QUEUE_EXTENSION);
  563.         //创建这个队列,将之前创建的设备作为队列的父对象
  564.         //这样在这个设备被销毁的同时这个队列也会被销毁
  565.         //这样就不用担心队列的结束问题
  566.         ntStatus = WdfIoQueueCreate(WdfDevice,&WdfIoQueueConfig,&WdfQueueAttributes,&WdfQueue);
  567.         if(!NT_SUCCESS(ntStatus))return ntStatus;
  568.         //将指针 pQueueExtension 指向刚生成的队列的队列扩展
  569.         pQueueExtension = QueueGetExtension(WdfQueue);
  570.         //这里初始化队列扩展里的 DeviceExtension 项,并将其设置为刚建立的设备的设备扩展指针
  571.         //这样以后在有队列的地方,可以轻松获取到这个队列所对应的设备的设备扩展
  572.         pQueueExtension->DeviceExtension = pDeviceExtension;
  573.         //将生成的设备的设备扩展中相应的 UNICODE_STRING 初使化
  574.         pDeviceExtension->DiskRegInfo.DriveLetter.Buffer =
  575.                 (PWSTR)&pDeviceExtension->DriveLetterBuffer;
  576.         pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength =
  577.                 sizeof(pDeviceExtension->DriveLetterBuffer);
  578.         //从系统为本驱动提供的注册表键中获取我们需要的信息
  579.         QueryParameters(WdfDriverGetRegistryPath(WdfDeviceGetDriver(WdfDevice)),
  580.                 &pDeviceExtension->DiskRegInfo);
  581.         //分配用户指定大小的非分页内存,并使用我们自己的内存Tag值
  582.         pDeviceExtension->DiskImage = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool,
  583.                 pDeviceExtension->DiskRegInfo.DiskSize,
  584.                 RAMDISK_TAG);
  585.         //下面的代码只有在内存分配成功时才会运行
  586.         if(pDeviceExtension->DiskImage)
  587.         {
  588.                 UNICODE_STRING usDeviceName;
  589.                 UNICODE_STRING usWin32Name;
  590.                 //在这里调用我们自己实现的函数去初始化磁盘
  591.                 FormatDisk(pDeviceExtension);
  592.                 ntStatus = STATUS_SUCCESS;
  593.                 RtlInitUnicodeString(&usWin32Name,DOS_DEVICE_NAME);
  594.                 RtlInitUnicodeString(&usDeviceName,NT_DEVICE_NAME);
  595.                 //这里准备好用来存储符号链接名的UNICODE_STRING变量
  596.                 pDeviceExtension->SymbolicLink.Buffer = (PWSTR)
  597.                         &pDeviceExtension->DosDeviceNameBuffer;
  598.                 pDeviceExtension->SymbolicLink.MaximumLength =
  599.                         sizeof(pDeviceExtension->DosDeviceNameBuffer);
  600.                 pDeviceExtension->SymbolicLink.Length = usWin32Name.Length;
  601.                 //将符号链接名一开始设置为 "\\DosDevices\\",这是所有符号链接有的前缀
  602.                 RtlCopyUnicodeString(&pDeviceExtension->SymbolicLink,&usWin32Name);
  603.                 //在上面赋值好前缀后面连接我们从用户配置中读出来的用户指定盘符
  604.                 RtlAppendUnicodeStringToString(&pDeviceExtension->SymbolicLink,
  605.                         &pDeviceExtension->DiskRegInfo.DriveLetter);
  606.                 //现在符号链接名已经准备好,调用WDF驱动框架模型提供的函数来为之前生成的设备建立符号链接
  607.                 ntStatus = WdfDeviceCreateSymbolicLink(WdfDevice,&pDeviceExtension->SymbolicLink);
  608.         }
  609.         //最后返回状态,函数结束
  610.         return ntStatus;
  611. }
  612. EXTERN_C NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistryPath)
  613. {
  614.         WDF_DRIVER_CONFIG WDFDriverConfig;
  615.         WDF_DRIVER_CONFIG_INIT(&WDFDriverConfig,DeviceAdd);
  616.         KdPrint(("DriverEntry"));
  617.         return WdfDriverCreate(pDriverObject,pRegistryPath,
  618.                 WDF_NO_OBJECT_ATTRIBUTES,
  619.                 &WDFDriverConfig,WDF_NO_HANDLE);
  620. }
复制代码


分享到: QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
分享淘帖0 收藏收藏0 评分评分
+q67824885

7

主题

0

好友

174

积分

注册会员

Rank: 2

生肖
星座
天秤座
性别
沙发
发表于 2012-6-10 22:48:43 |只看该作者
本帖最后由 sky_yx 于 2015-12-30 14:17 编辑
  1. //////////////////////INF文件////////////////////////
  2. [Version]
  3. Signature="$WINDOWS NT$"
  4. Class=Sample
  5. ClassGuid={78A1C341-4539-11d3-B88D-00C04FAD5171}
  6. Provider=%MSFT%
  7. DriverVer=12/20/2009,6.1.7600.16385
  8. CatalogFile=KmdfSamples.cat
  9. [DestinationDirs]
  10. DefaultDestDir = 12
  11. [ClassInstall32]
  12. Addreg=SampleClassReg
  13. [SampleClassReg]
  14. HKR,,,0,%ClassName%
  15. HKR,,Icon,,-5
  16. [DiskCopyfiles]
  17. WdfRamdisk.sys             ;驱动程序文件名
  18. [SourceDisksNames]
  19. 1=%InstDisk%,
  20. [SourceDisksFiles]
  21. WdfRamdisk.sys=1           ;驱动程序文件名
  22. [Manufacturer]
  23. %MSFT% = DiskDevice,NTx86
  24. ; For Win2K
  25. [DiskDevice]
  26. %DiskDevDesc% = DiskInstall, Ramdisk
  27. ; For XP and later
  28. [DiskDevice.NTx86]
  29. %DiskDevDesc% = DiskInstall, Ramdisk
  30. [DiskInstall.NT]
  31. CopyFiles = DiskCopyfiles
  32. [DiskInstall.NT.Services]
  33. AddService = Ramdisk, %SPSVCINST_ASSOCSERVICE%, DiskServiceInst
  34. [DiskServiceInst]
  35. ServiceType   = %SERVICE_KERNEL_DRIVER%
  36. StartType     = %SERVICE_DEMAND_START%
  37. ErrorControl  = %SERVICE_ERROR_NORMAL%
  38. DisplayName   = %DiskServiceDesc%
  39. ServiceBinary = %12%\WdfRamdisk.sys
  40. AddReg        = DiskAddReg
  41. [DiskAddReg]
  42. HKR, "Parameters", "BreakOnEntry",      %REG_DWORD%, 0x00000000
  43. HKR, "Parameters", "DebugLevel",        %REG_DWORD%, 0x00000000
  44. HKR, "Parameters", "DebugComp",         %REG_DWORD%, 0xFFFFFFFF
  45. HKR, "Parameters", "DiskSize",          %REG_DWORD%, 0x00100000
  46. HKR, "Parameters", "DriveLetter",       %REG_SZ%,    "R:"
  47. HKR, "Parameters", "RootDirEntries",    %REG_DWORD%, 0x00000200
  48. HKR, "Parameters", "SectorsPerCluster", %REG_DWORD%, 0x00000002
  49. ;-------------- Coinstaller installation
  50. [DestinationDirs]
  51. CoInstaller_CopyFiles = 11
  52. [DiskInstall.NT.CoInstallers]
  53. AddReg=CoInstaller_AddReg
  54. CopyFiles=CoInstaller_CopyFiles
  55. [CoInstaller_CopyFiles]
  56. WdfCoInstaller01009.dll
  57. [SourceDisksFiles]
  58. WdfCoInstaller01009.dll=1 ; make sure the number matches with SourceDisksNames
  59. [CoInstaller_AddReg]
  60. HKR,,CoInstallers32,0x00010000, "WdfCoInstaller01009.dll,WdfCoInstaller"
  61. [DiskInstall.NT.Wdf]
  62. KmdfService = Ramdisk, Ramdisk_wdfsect
  63. [Ramdisk_wdfsect]
  64. KmdfLibraryVersion = 1.9
  65. [Strings]
  66. MSFT            = "Microsoft"                        ;制造商
  67. ClassName       = "Sample Device"                ;设备类型名
  68. DiskDevDesc     = "WDF Sample RAM disk Driver"        ;设备描述
  69. DiskServiceDesc = "Ramdisk Driver"                ;服务描述
  70. InstDisk        = "Ramdisk Install Disk"        ;
  71. ;*******************************************
  72. ;Handy macro substitutions (non-localizable)
  73. SPSVCINST_ASSOCSERVICE = 0x00000002
  74. SERVICE_KERNEL_DRIVER  = 1
  75. SERVICE_DEMAND_START   = 3
  76. SERVICE_ERROR_NORMAL   = 1
  77. REG_DWORD              = 0x00010001
  78. REG_SZ                 = 0x00000000
复制代码


+q67824885
回复

使用道具 评分 举报

200

主题

3

好友

2260

积分

管理员

Rank: 9Rank: 9Rank: 9

性别
保密

热心会员 推广达人 宣传达人 灌水之王 突出贡献 优秀版主 荣誉管理 论坛元老 最佳新人 活跃会员

板凳
发表于 2012-6-11 01:10:41 |只看该作者
真正的高手+真正的技术帖子,非顶不可!
回复

使用道具 评分 举报

您需要登录后才可以回帖 登录 | 立即注册


手机版|SKY外语计算机学习 ( 粤ICP备12031577 )    

GMT+8, 2024-12-22 22:26 , Processed in 0.115220 second(s), 28 queries .

回顶部