这是寒江独钓里面第5章的例子了, 分析自DDK中的RamDisk驱动. 具体来说没有太大的用处, 只是对WDF驱动的套路有个了解吧, 但是这个WDF到现在为止明显没有形成气候. 写个驱动出来, 安装都是很麻烦的事情, 哪里有NT式驱动舒服啊, 不过WDF确实是潮流, 这个也确实不能够忽略..

其实要说NT式驱动能够在浏览器上面显示一个磁盘的话, 这个用Nt式驱动来做也很简单, 前面几章也就做了一个模拟磁盘读写的驱动. 这个驱动的难点是建立一个文件系统, 文件系统的话是比较棘手的一个东西, 这个驱动用的是简单的AT12/16.. 所以写完感觉还是意犹未尽啊!寒江独钓也就一个WDF的例子, 所以我从网上去DOWN了一个NT式驱动的虚拟磁盘的源码, 这两天再分析分析.. 

都说了这个驱动的难点就是建立文件系统, 其他基本上也没有什么难点, 所以最主要是要搞定FAT16的文件系统. 这个网上大把资料. 搞成FAT32也不是问题. 所以基本上没有什么好说的, 代码注释非常齐全了.

这边是截图和代码:
        ramdisk_wdf.rar

  

这边是驱动的头文件:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
	Windows 内核下最简单的虚拟磁盘驱动, 将所有的读写请求转到内存中
	WDF驱动, 修改自MicroSoft的例子. 这是头文件
	编译方法参见就是普通的build就可以. TAB = 8
*/

#ifndef _RAMDISK_H_
#define _RAMDISK_H_

#pragma warning(disable:4201)  // nameless struct/union warning

#include <ntddk.h>
#include <ntdddisk.h>

#pragma warning(default:4201)

#include <wdf.h>
#define NTSTRSAFE_LIB
#include <ntstrsafe.h>

#define NT_DEVICE_NAME                  L"\\\\Device\\\\Ramdisk"
#define DOS_DEVICE_NAME                 L"\\\\DosDevices\\\\"

#define RAMDISK_TAG                     'DmaR'  // "RamD"
#define DOS_DEVNAME_LENGTH              (sizeof(DOS_DEVICE_NAME)+sizeof(WCHAR)*10)
#define DRIVE_LETTER_LENGTH             (sizeof(WCHAR)*10)

#define DRIVE_LETTER_BUFFER_SIZE        10
#define DOS_DEVNAME_BUFFER_SIZE         (sizeof(DOS_DEVICE_NAME) / 2) + 10

#define RAMDISK_MEDIA_TYPE              0xF8
#define DIR_ENTRIES_PER_SECTOR          16

//
// 这些都是默认参数, 只有在注册表中没有该项才启用, 但是安装文件已经设置了
// 驱动的参数, 所以基本来说这个就是形同虚设
//
#define DEFAULT_DISK_SIZE               (1024*1024*512)     // 默认的磁盘大小
#define DEFAULT_ROOT_DIR_ENTRIES        512		    // 默认根目录入口
#define DEFAULT_SECTORS_PER_CLUSTER     2		    // 默认柱面扇区数量
#define DEFAULT_DRIVE_LETTER            L"Z:"		    // 默认盘符

//---------------------------------------------------------------------------
typedef struct _DISK_INFO {
	ULONG   DiskSize;           // 磁盘的大小以Byte计算, 所以不能够超过4G
	ULONG   RootDirEntries;     // 系统上根文件系统的进入点
	ULONG   SectorsPerCluster;  // 磁盘的每个族由多少个扇区组成
	UNICODE_STRING DriveLetter; // 磁盘的盘符
} DISK_INFO, *PDISK_INFO;

//---------------------------------------------------------------------------
typedef struct _DEVICE_EXTENSION {
	PUCHAR              DiskImage;                  // 指向一块内存区域, 作为内存盘的实际存储空间
	DISK_GEOMETRY       DiskGeometry;               // 存储内存盘的物理信息, WinDDK提供
	DISK_INFO           DiskRegInfo;                // 我们自己定义的磁盘信息结构, 在安装时存放在注册表中
	UNICODE_STRING      SymbolicLink;               // 共享给用户态的符号链接名称
	WCHAR               DriveLetterBuffer[DRIVE_LETTER_BUFFER_SIZE];	//DiskRegInfo中的盘符的存储空间
	WCHAR               DosDeviceNameBuffer[DOS_DEVNAME_BUFFER_SIZE];	//符号连接名的存放空间
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME( DEVICE_EXTENSION, DeviceGetExtension )

typedef struct _QUEUE_EXTENSION {
	PDEVICE_EXTENSION DeviceExtension;
} QUEUE_EXTENSION, *PQUEUE_EXTENSION;

WDF_DECLARE_CONTEXT_TYPE_WITH_NAME( QUEUE_EXTENSION, QueueGetExtension )

#pragma pack(1)

typedef struct  _BOOT_SECTOR {
	UCHAR       bsJump[3];          // 跳转指令, 跳到DBR中的引导程序
	CCHAR       bsOemName[8];       // 卷的OEM名称
	USHORT      bsBytesPerSec;      // 每个扇区有多少个字节
	UCHAR       bsSecPerClus;       // 每个族有多少个扇区
	USHORT      bsResSectors;       // 保留扇区数目, 指的是第一个FAT
	UCHAR       bsFATs;             // 这个卷有多少个FAT表
	USHORT      bsRootDirEnts;      // 这个卷根入口点有几个
	USHORT      bsSectors;          // 这个卷有多少个扇区, 对于大于65535个的扇区卷, 这个字段为0
	UCHAR       bsMedia;            // 这个卷的介质类型 RAMDISK_MEDIA_TYPE
	USHORT      bsFATsecs;          // 每个FAT表占用多少个扇区
	USHORT      bsSecPerTrack;      // 每个磁道有多少个扇区, 我们使用32
	USHORT      bsHeads;            // 有多少个磁头, 我们使用2
	ULONG       bsHiddenSecs;       // 有多少个隐藏分区, 我们使用0
	ULONG       bsHugeSectors;      // 一个卷如果超过65535扇区, 会使用这字段来说明总扇区数
	UCHAR       bsDriveNumber;      // 驱动器编号, 未使用
	UCHAR       bsReserved1;        // 保留字段
	UCHAR       bsBootSignature;    // 磁盘扩展引导区标志, Windows规定必须为 0x29 或者0x28
	ULONG       bsVolumeID;         // 磁盘卷ID - set to 0x12345678
	CCHAR       bsLabel[11];        // 磁盘卷标
	CCHAR       bsFileSystemType[8];// 文件系统类型 - FAT12 or FAT16
	CCHAR       bsReserved2[448];   // 保留
	UCHAR       bsSig2[2];          // DBR结束标记, 必须以0x55AA结束 - 0x55, 0xAA
}   BOOT_SECTOR, *PBOOT_SECTOR;
//---------------------------------------------------------------------------
typedef struct  _DIR_ENTRY {
	UCHAR       deName[8];          // 文件名称
	UCHAR       deExtension[3];     // 文件扩展名
	UCHAR       deAttributes;       // 文件属性
	UCHAR       deReserved;         // 系统保留
	USHORT      deTime;             // 文件建立时间
	USHORT      deDate;             // 文件建立日期
	USHORT      deStartCluster;     // 文件第一个族的编号
	ULONG       deFileSize;         // 文件大小
}   DIR_ENTRY, *PDIR_ENTRY;

#pragma pack()

//
// Directory Entry Attributes
//

#define DIR_ATTR_READONLY   0x01
#define DIR_ATTR_HIDDEN     0x02
#define DIR_ATTR_SYSTEM     0x04
#define DIR_ATTR_VOLUME     0x08
#define DIR_ATTR_DIRECTORY  0x10
#define DIR_ATTR_ARCHIVE    0x20

DRIVER_INITIALIZE DriverEntry;

EVT_WDF_DRIVER_DEVICE_ADD RamDiskEvtDeviceAdd;

EVT_WDF_DEVICE_CONTEXT_CLEANUP RamDiskEvtDeviceContextCleanup;

EVT_WDF_IO_QUEUE_IO_READ RamDiskEvtIoRead;
EVT_WDF_IO_QUEUE_IO_WRITE RamDiskEvtIoWrite;
EVT_WDF_IO_QUEUE_IO_DEVICE_CONTROL RamDiskEvtIoDeviceControl;

VOID RamDiskQueryDiskRegParameters( __in PWSTR RegistryPath, __in PDISK_INFO DiskRegInfo );

NTSTATUS RamDiskFormatDisk( IN PDEVICE_EXTENSION DeviceExtension );

BOOLEAN RamDiskCheckParameters( IN PDEVICE_EXTENSION devExt, IN LARGE_INTEGER ByteOffset, IN size_t Length );

#endif    // _RAMDISK_H_

这边是驱动的主文件:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
/*
	Windows 内核下最简单的虚拟磁盘驱动, 将所有的读写请求转到内存中
	WDF驱动, 修改自MicroSoft的例子. 这是驱动主文件
	编译方法参见就是普通的build就可以. TAB = 8
*/
#include "ramdisk.h"
#include "ntintsafe.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, DriverEntry)
#pragma alloc_text(PAGE, RamDiskEvtDeviceAdd)
#pragma alloc_text(PAGE, RamDiskEvtDeviceContextCleanup)
#pragma alloc_text(PAGE, RamDiskQueryDiskRegParameters)
#pragma alloc_text(PAGE, RamDiskFormatDisk)
#endif

//===========================================================================
//	驱动入口
//===========================================================================
NTSTATUS DriverEntry( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath ) {

	WDF_DRIVER_CONFIG config;

	KdPrint( ( "Windows Ramdisk Driver - 简单的磁盘过滤框架, 是微软写得.\n" ) );
	KdPrint( ( "Built %s %s\n", __DATE__, __TIME__ ) );

	//这边设置了AddDevice函数的地址, 由Pnp调用, 连卸载例程都没有
	WDF_DRIVER_CONFIG_INIT( &amp;config, RamDiskEvtDeviceAdd );

	//直接按照正常的套路返回了
	return WdfDriverCreate( DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES,
	                        &amp;config, WDF_NO_HANDLE );
}

//===========================================================================
//	设备读取请求, 类型NT式驱动的IRP_MJ_READ
//Queue		:队列对象
//Request	:具体的请求
//Length	:用来说明需要读写的长度
//返回值	:无
//===========================================================================
VOID RamDiskEvtIoRead( WDFQUEUE Queue, WDFREQUEST Request, size_t Length ) {

	NTSTATUS               Status = STATUS_INVALID_PARAMETER;
	WDF_REQUEST_PARAMETERS Parameters;
	LARGE_INTEGER          ByteOffset;
	WDFMEMORY              hMemory;

	//
	// 从队列扩展中获取设备扩展, 初始化时我们已经关联了设备扩展
	//
	PDEVICE_EXTENSION      devExt = QueueGetExtension( Queue )->DeviceExtension;

	//
	// 初始化参数变量, 其实就是一个清0操作
	//
	WDF_REQUEST_PARAMETERS_INIT( &amp;Parameters );

	//
	// 从请求中获取参数信息, 和IoGetCurrentIrpStackLocation形成对比
	//
	WdfRequestGetParameters( Request, &amp;Parameters );

	//欲读取的偏移
	ByteOffset.QuadPart = Parameters.Parameters.Read.DeviceOffset;

	//
	//这里自己实现了一个参数检测函数, 由于读取的范围不能够超过磁盘镜像
	//的大小, 并且必须以扇区对齐, 所以这里需要检测参数. 如果检测失败, 返回错误
	//
	if ( RamDiskCheckParameters( devExt, ByteOffset, Length ) ) {

		//
		// 从读取请求参数中获取读取缓冲区的内存句柄
		//
		Status = WdfRequestRetrieveOutputMemory( Request, &amp;hMemory );
		if( NT_SUCCESS( Status ) ) {

			//
			// 根据之前获取到的读参数进行内存Copy, 填写这个缓冲区完成读取请求
			//
			Status = WdfMemoryCopyFromBuffer( hMemory,  0,
			                                  devExt->DiskImage + ByteOffset.LowPart, Length );
		}
	}

	KdPrint(( "读取请求来了一次 ByteOffset:%p Length:%d\n",(ULONG)ByteOffset.QuadPart, Length  ));

	//
	// 类似于这号函数IoCompleteRequest
	//
	WdfRequestCompleteWithInformation( Request, Status, ( ULONG_PTR )Length );
}

//===========================================================================
//	设备写入请求, 类型NT式驱动的IRP_MJ_WRITE
//Queue		:队列对象
//Request	:具体的请求
//Length	:用来说明需要读写的长度
//返回值	:无
//===========================================================================
VOID  RamDiskEvtIoWrite(  WDFQUEUE Queue, WDFREQUEST Request,  size_t Length ) {

	NTSTATUS               Status = STATUS_INVALID_PARAMETER;
	WDF_REQUEST_PARAMETERS Parameters;
	LARGE_INTEGER          ByteOffset;
	WDFMEMORY              hMemory;

	//
	// 从队列扩展中获取设备扩展
	//
	PDEVICE_EXTENSION      devExt = QueueGetExtension( Queue )->DeviceExtension;

	//
	// 初始化参数变量, 其实就是一个清0操作
	//
	WDF_REQUEST_PARAMETERS_INIT( &amp;Parameters );

	//
	// 从请求中获取参数信息, 和IoGetCurrentIrpStackLocation形成对比
	//
	WdfRequestGetParameters( Request, &amp;Parameters );

	//
	// 写入偏移
	//
	ByteOffset.QuadPart = Parameters.Parameters.Write.DeviceOffset;

	//
	// 检测参数是否合法
	//
	if ( RamDiskCheckParameters( devExt, ByteOffset, Length ) ) {

		//
		// 从写入请求参数中获取写入缓冲区的内存句柄
		//
		Status = WdfRequestRetrieveInputMemory( Request, &amp;hMemory );
		if( NT_SUCCESS( Status ) ) {

			//
			// 从虚拟磁盘中中Copy内存到R3缓冲区中
			//
			Status = WdfMemoryCopyToBuffer( hMemory, 0,
			                                devExt->DiskImage + ByteOffset.LowPart, Length );
		}

	}

	KdPrint(( "写入请求来了一次 ByteOffset:%p Length:%d\n",(ULONG)ByteOffset.QuadPart, Length  ));

	//
	// 类似于这号函数IoCompleteRequest
	//
	WdfRequestCompleteWithInformation( Request, Status, ( ULONG_PTR )Length );
}

//===========================================================================
//	设备控制请求, 类似NT式驱动的IRP_MJ_DEVICE_CONTROL
//Queue		:队列对象
//Request	:具体的请求
//Length	:用来说明需要读写的长度
//返回值	:无
//===========================================================================
VOID RamDiskEvtIoDeviceControl( WDFQUEUE Queue, WDFREQUEST Request, size_t OutputBufferLength,
                                size_t InputBufferLength, ULONG IoControlCode ) {

	ULONG_PTR         information = 0;
	size_t            bufSize;
	NTSTATUS          Status = STATUS_INVALID_DEVICE_REQUEST;

	//
	// 通过队列扩展获取设备扩展
	//
	PDEVICE_EXTENSION devExt = QueueGetExtension( Queue )->DeviceExtension;

	//
	//避免不使用参数警告
	//
	UNREFERENCED_PARAMETER( OutputBufferLength );
	UNREFERENCED_PARAMETER( InputBufferLength );

//---------------------------------------------------------------------------
//	根据不同的操作码执行不同的操作
//---------------------------------------------------------------------------
	switch ( IoControlCode ) {

		//
		// 获取当前分区信息的的DeviceIoControl请求, 要处理
		//
	case IOCTL_DISK_GET_PARTITION_INFO: {

		PPARTITION_INFORMATION outputBuffer;

		//
		// 完成这个请求大部分数据来自于DBR, 所以这里获取指向DBR的指针
		//
		PBOOT_SECTOR bootSector = ( PBOOT_SECTOR ) devExt->DiskImage;

		//
		// 欲返回信息的长度
		//
		information = sizeof( PARTITION_INFORMATION );

		//
		// 通过框架函数来获取这个DeviceIoControl请求所携带的缓冲区
		//
		Status = WdfRequestRetrieveOutputBuffer( Request, sizeof( PARTITION_INFORMATION ), &amp;outputBuffer, &amp;bufSize );
		if( NT_SUCCESS( Status ) ) {

			//
			// 填写分区类型
			//
			outputBuffer->PartitionType = ( bootSector->bsFileSystemType[4] =='6' ) ?
			                              PARTITION_FAT_16 : PARTITION_FAT_12;

			//
			// BootIndicator为TRUE表示可引导的
			//
			outputBuffer->BootIndicator       = FALSE;

			//
			// 为TRUE时,系统识别的分区类型,如果为FALSE,系统没有识别的分区类型
			//
			outputBuffer->RecognizedPartition = TRUE;

			//
			// 为TRUE时, 分区信息已经改变,如果为FALSE,分区信息并没有改变
			//
			outputBuffer->RewritePartition    = FALSE;

			//
			// 指定驱动器上的分区开始的字节偏移量.
			//
			outputBuffer->StartingOffset.QuadPart = 0;

			//
			// 指定分区的字节的长度(磁盘大小)
			//
			outputBuffer->PartitionLength.QuadPart = devExt->DiskRegInfo.DiskSize;

			//
			// 隐藏分区
			//
			outputBuffer->HiddenSectors       = ( ULONG ) ( 1L );

			//
			// 分区号
			//
			outputBuffer->PartitionNumber     = ( ULONG ) ( -1L );

			Status = STATUS_SUCCESS;

			KdPrint( ( "获取当前分区信息成功!\n" ) );
		}
		break;
	}
//---------------------------------------------------------------------------
//		获取硬盘的物理信息请求
//---------------------------------------------------------------------------
	case IOCTL_DISK_GET_DRIVE_GEOMETRY:  {

		PDISK_GEOMETRY outputBuffer;

		//
		// 这里是将要返回的信息长度
		//
		information = sizeof( DISK_GEOMETRY );

		//
		// 通过框架函数来获取这个DeviceIoControl请求所携带的的输出缓冲区
		//
		Status = WdfRequestRetrieveOutputBuffer( Request, sizeof( DISK_GEOMETRY ), &amp;outputBuffer, &amp;bufSize );
		if( NT_SUCCESS( Status ) ) {

			//
			// 将磁盘物理结构Copy到上层设备
			//
			RtlCopyMemory( outputBuffer, &amp;( devExt->DiskGeometry ), sizeof( DISK_GEOMETRY ) );
			Status = STATUS_SUCCESS;
		}

		KdPrint( ( "获取硬盘的物理参数成功!\n" ) );
	}
	break;
//---------------------------------------------------------------------------
//		这两个请求, 直接返回成功就可以了
//---------------------------------------------------------------------------
	case IOCTL_DISK_CHECK_VERIFY:

		KdPrint( ( "磁盘校验, 直接返回成功!\n" ) );

	case IOCTL_DISK_IS_WRITABLE:

		KdPrint( ( "确定磁盘是否可写, 返回True, 直接成功!\n" ) );

		Status = STATUS_SUCCESS;
		break;
	}

	//
	// 类似于这号函数IoCompleteRequest
	//
	WdfRequestCompleteWithInformation( Request, Status, information );
}

//===========================================================================
//	设备的IRP_MJ_CLOSE消息,
//Device	:设备对象
//返回值	:无
//===========================================================================
VOID RamDiskEvtDeviceContextCleanup( IN WDFDEVICE Device ) {

	//
	// 获取到设备扩展指针
	//
	PDEVICE_EXTENSION pDeviceExtension = DeviceGetExtension( Device );

	PAGED_CODE();

	//
	// 是否掉设备扩展内存就可以了
	//
	if( pDeviceExtension->DiskImage ) {
		ExFreePool( pDeviceExtension->DiskImage );
	}

	KdPrint( ( "设备清除函数被调用一次, 释放了申请的物理内存" ) );
}

//===========================================================================
//	类似WDM的AddDevice函数, 由Pnp管理器调用
//Driver	:
//DeviceInit	:这个参数是WDF驱动模型自动分配出来的一个数据结构, 用于建立新设别
//===========================================================================
NTSTATUS RamDiskEvtDeviceAdd( WDFDRIVER Driver,  PWDFDEVICE_INIT DeviceInit ) {
	NTSTATUS                Status;
	WDFDEVICE               device;
	WDFQUEUE                queue;
	PQUEUE_EXTENSION        pQueueContext = NULL;
	PDEVICE_EXTENSION       pDeviceExtension;
	WDF_OBJECT_ATTRIBUTES   deviceAttributes;
	WDF_OBJECT_ATTRIBUTES   queueAttributes;
	WDF_IO_QUEUE_CONFIG     ioQueueConfig;

	//初始化我们要创建的设备名称
	DECLARE_CONST_UNICODE_STRING( ntDeviceName, NT_DEVICE_NAME );

	PAGED_CODE();
	UNREFERENCED_PARAMETER( Driver );
	KdPrint( ( "RamDiskEvtDeviceAdd被调用!\n" ) );

	//指定设备名称
	Status = WdfDeviceInitAssignName( DeviceInit, &amp;ntDeviceName );
	if ( !NT_SUCCESS( Status ) ) {
		return Status;
	}

	//指定设备类型
	WdfDeviceInitSetDeviceType( DeviceInit, FILE_DEVICE_DISK );

	//设备IO操作方式
	WdfDeviceInitSetIoType( DeviceInit, WdfDeviceIoDirect );
	//是否是独占打开
	WdfDeviceInitSetExclusive( DeviceInit, FALSE );

	//初始化设备扩展
	WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE( &amp;deviceAttributes, DEVICE_EXTENSION );

	//指定清除回调函数, 基本上就类似于NT驱动的IRP_MJ_CLOSE
	deviceAttributes.EvtCleanupCallback = RamDiskEvtDeviceContextCleanup;

	//属性指定完毕, 真正开始创建设备
	Status = WdfDeviceCreate( &amp;DeviceInit, &amp;deviceAttributes, &amp;device );
	if ( !NT_SUCCESS( Status ) ) {
		return Status;
	}

	//获取设备扩展指针
	pDeviceExtension = DeviceGetExtension( device );

	//将处理队列初始化为默认值
	WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE ( &amp;ioQueueConfig, WdfIoQueueDispatchSequential );

	//设定3个特殊的分发函数地址
	ioQueueConfig.EvtIoDeviceControl = RamDiskEvtIoDeviceControl;
	ioQueueConfig.EvtIoRead          = RamDiskEvtIoRead;
	ioQueueConfig.EvtIoWrite         = RamDiskEvtIoWrite;

	//指定队列的扩展属性, 注意要和前面的设备扩展区分开来
	WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE( &amp;queueAttributes, QUEUE_EXTENSION );

	//到这里属性就配置的差不多了, 可以创建队列了将之前我们创建的设备作为
	//这个队列的父对象, 这样在设备被销毁的时候这个队列也会被销毁
	Status = WdfIoQueueCreate( device, &amp;ioQueueConfig, &amp;queueAttributes,  &amp;queue );
	if ( !NT_SUCCESS( Status ) ) {
		return Status;
	}

	//获取队列指针
	pQueueContext = QueueGetExtension( queue );

	//初始化队列扩展里的DeviceExtension项, 并将其设置为刚建立的设备
	//的设备扩展, 这样以后就可以方便的通过队列扩展找到设备扩展了
	pQueueContext->DeviceExtension = pDeviceExtension;

//---------------------------------------------------------------------------
	//读取注册表, 初始化和内存盘有关的一些属性
//---------------------------------------------------------------------------

	//
	// 初始化存放盘符的缓冲区
	//
	pDeviceExtension->DiskRegInfo.DriveLetter.Buffer = ( PWSTR ) &amp;pDeviceExtension->DriveLetterBuffer;
	pDeviceExtension->DiskRegInfo.DriveLetter.MaximumLength = sizeof( pDeviceExtension->DriveLetterBuffer );

	//
	// 获取注册表中存放的磁盘信息
	// WdfDeviceGetDriver		:从设备中获取这个设备对应的驱动对象
	// WdfDriverGetRegistryPath	:获取驱动对象对应的注册表项目
	//
	RamDiskQueryDiskRegParameters( WdfDriverGetRegistryPath( WdfDeviceGetDriver( device ) ),
	                               &amp;pDeviceExtension->DiskRegInfo );

//---------------------------------------------------------------------------
	//
	// 分配等同磁盘大小的内存, 这边用非分页内存, 有点浪费啊
	// 可以看出来, 微软的人写的代码也很一般, 这边内存分配失败, 还是会返回
	// 正确, 我倒, 什么工作都没有做, 原来他们说的敲例子的人都是二等开发人员
	// 还是有些道理的
	//
	pDeviceExtension->DiskImage = ExAllocatePoolWithTag( NonPagedPool,
	                              pDeviceExtension->DiskRegInfo.DiskSize,  RAMDISK_TAG );
	if ( pDeviceExtension->DiskImage ) {

		UNICODE_STRING deviceName;
		UNICODE_STRING win32Name;

		//这里调用我们自己实现的函数来格式化内存盘
		RamDiskFormatDisk( pDeviceExtension );

		Status = STATUS_SUCCESS;

//---------------------------------------------------------------------------
		// 现在开始尝试创建符号链接
//---------------------------------------------------------------------------

		//
		// 初始化一个内容为"\\\\DosDevice\\\\"的Unicode变量
		//
		RtlInitUnicodeString( &amp;win32Name, DOS_DEVICE_NAME );

		//
		// 初始化一个内容为"\\\\Device\\\\Ramdisk\\\\"的Unicode变量
		//
		RtlInitUnicodeString( &amp;deviceName, NT_DEVICE_NAME );

		//
		// 准备存放符号链接名称的缓冲区
		//
		pDeviceExtension->SymbolicLink.Buffer = ( PWSTR ) &amp;pDeviceExtension->DosDeviceNameBuffer;
		pDeviceExtension->SymbolicLink.MaximumLength = sizeof( pDeviceExtension->DosDeviceNameBuffer );
		pDeviceExtension->SymbolicLink.Length = win32Name.Length;

		//
		// 到这里符号连接名是"\\\\DosDevices\\\\"
		//
		RtlCopyUnicodeString( &amp;pDeviceExtension->SymbolicLink, &amp;win32Name );

		//
		// 在上面的基础之上,读取出来我们设置的盘符 "\\\\DosDevice\\\\Z:\\"
		//
		RtlAppendUnicodeStringToString( &amp;pDeviceExtension->SymbolicLink,
		                                &amp;pDeviceExtension->DiskRegInfo.DriveLetter );

		//创建符号链接
		Status = WdfDeviceCreateSymbolicLink( device, &amp;pDeviceExtension->SymbolicLink );
	}

	return Status;
}

//===========================================================================
//	从注册表中读取磁盘的配置信息
//RegistryPath	:欲读取的注册表路径
//DiskRegInfo	:存放结果信息的结构体指针
//返回值	:无
//===========================================================================
VOID RamDiskQueryDiskRegParameters( __in PWSTR RegistryPath, __in PDISK_INFO DiskRegInfo ) {
	NTSTATUS                 Status;
	DISK_INFO                defDiskRegInfo;
	RTL_QUERY_REGISTRY_TABLE rtlQueryRegTbl[5 + 1];  //需要一个后面为NULL

//---------------------------------------------------------------------------
	PAGED_CODE();
	ASSERT( RegistryPath != NULL );

	//
	// 如果读取注册表失败了, 这些就是默认参数了
	//
	defDiskRegInfo.DiskSize          = DEFAULT_DISK_SIZE;
	defDiskRegInfo.RootDirEntries    = DEFAULT_ROOT_DIR_ENTRIES;
	defDiskRegInfo.SectorsPerCluster = DEFAULT_SECTORS_PER_CLUSTER;

	//初始化默认的盘符
	RtlInitUnicodeString( &amp;defDiskRegInfo.DriveLetter, DEFAULT_DRIVE_LETTER );

//---------------------------------------------------------------------------
	//初始化欲查询的数据属性
//---------------------------------------------------------------------------
	RtlZeroMemory( rtlQueryRegTbl, sizeof( rtlQueryRegTbl ) );

	//
	// 设置欲查询的数据
	//
	rtlQueryRegTbl[0].Flags         = RTL_QUERY_REGISTRY_SUBKEY;
	rtlQueryRegTbl[0].Name          = L"Parameters";
	rtlQueryRegTbl[0].EntryContext  = NULL;
	rtlQueryRegTbl[0].DefaultType   = ( ULONG_PTR )NULL;
	rtlQueryRegTbl[0].DefaultData   = NULL;
	rtlQueryRegTbl[0].DefaultLength = ( ULONG_PTR )NULL;

//---------------------------------------------------------------------------
	// 欲查询的磁盘参数
//---------------------------------------------------------------------------

	//磁盘大小
	rtlQueryRegTbl[1].Flags         = RTL_QUERY_REGISTRY_DIRECT;
	rtlQueryRegTbl[1].Name          = L"DiskSize";
	rtlQueryRegTbl[1].EntryContext  = &amp;DiskRegInfo->DiskSize;
	rtlQueryRegTbl[1].DefaultType   = REG_DWORD;
	rtlQueryRegTbl[1].DefaultData   = &amp;defDiskRegInfo.DiskSize;
	rtlQueryRegTbl[1].DefaultLength = sizeof( ULONG );

	//系统上根文件系统的进入点
	rtlQueryRegTbl[2].Flags         = RTL_QUERY_REGISTRY_DIRECT;
	rtlQueryRegTbl[2].Name          = L"RootDirEntries";
	rtlQueryRegTbl[2].EntryContext  = &amp;DiskRegInfo->RootDirEntries;
	rtlQueryRegTbl[2].DefaultType   = REG_DWORD;
	rtlQueryRegTbl[2].DefaultData   = &amp;defDiskRegInfo.RootDirEntries;
	rtlQueryRegTbl[2].DefaultLength = sizeof( ULONG );

	//磁盘的每个族由多少个扇区组成
	rtlQueryRegTbl[3].Flags         = RTL_QUERY_REGISTRY_DIRECT;
	rtlQueryRegTbl[3].Name          = L"SectorsPerCluster";
	rtlQueryRegTbl[3].EntryContext  = &amp;DiskRegInfo->SectorsPerCluster;
	rtlQueryRegTbl[3].DefaultType   = REG_DWORD;
	rtlQueryRegTbl[3].DefaultData   = &amp;defDiskRegInfo.SectorsPerCluster;
	rtlQueryRegTbl[3].DefaultLength = sizeof( ULONG );

	//磁盘的盘符
	rtlQueryRegTbl[4].Flags         = RTL_QUERY_REGISTRY_DIRECT;
	rtlQueryRegTbl[4].Name          = L"DriveLetter";
	rtlQueryRegTbl[4].EntryContext  = &amp;DiskRegInfo->DriveLetter;
	rtlQueryRegTbl[4].DefaultType   = REG_SZ;
	rtlQueryRegTbl[4].DefaultData   = defDiskRegInfo.DriveLetter.Buffer;
	rtlQueryRegTbl[4].DefaultLength = 0;

	Status = RtlQueryRegistryValues( RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL, RegistryPath,
	                                 rtlQueryRegTbl, NULL, NULL );
	if ( NT_SUCCESS( Status ) == FALSE ) {

		DiskRegInfo->DiskSize          = defDiskRegInfo.DiskSize;
		DiskRegInfo->RootDirEntries    = defDiskRegInfo.RootDirEntries;
		DiskRegInfo->SectorsPerCluster = defDiskRegInfo.SectorsPerCluster;
		RtlCopyUnicodeString( &amp;DiskRegInfo->DriveLetter, &amp;defDiskRegInfo.DriveLetter );
	}
//---------------------------------------------------------------------------
	KdPrint( ( "在注册表中获取磁盘信息!\n" ) );
	KdPrint( ( "磁盘大小	= 0x%lx\n", DiskRegInfo->DiskSize ) );
	KdPrint( ( "根目录入口	= 0x%lx\n", DiskRegInfo->RootDirEntries ) );
	KdPrint( ( "族扇区数	= 0x%lx\n", DiskRegInfo->SectorsPerCluster ) );
	KdPrint( ( "驱动器盘符	= %wZ\n",   &amp;( DiskRegInfo->DriveLetter ) ) );

	return;
}
//===========================================================================
//	格式化一个磁盘
//devExt	:设备扩展
//返回值	:状态
//===========================================================================
NTSTATUS RamDiskFormatDisk( IN PDEVICE_EXTENSION devExt  ) {
	PUCHAR       firstFatSector;	// 指向第一个FAT表的指针
	ULONG        rootDirEntries;	// 记录有多少根目录入口点
	ULONG        sectorsPerCluster;	// 每个族有多少个扇区构成
	USHORT       fatType;		// 记录FAT文件系统类型, FAT12/16
	USHORT       fatEntries;	// 记录FAT表里面有多少个表项
	USHORT       fatSectorCnt;	// 用于记录一个FAT表项需要占用多少个扇区
	PDIR_ENTRY   rootDir;		// 根目录入口点

	//
	// 启动扇区指针, 也就是存放的申请的内存首地址
	//
	PBOOT_SECTOR bootSector = ( PBOOT_SECTOR ) devExt->DiskImage;

//---------------------------------------------------------------------------
	PAGED_CODE();

	//
	// 确定这个盘的引导扇区是512字节, 然后清空内存
	//
	ASSERT( sizeof( BOOT_SECTOR ) == 512 );
	ASSERT( devExt->DiskImage != NULL );

	RtlZeroMemory( devExt->DiskImage, devExt->DiskRegInfo.DiskSize );

//---------------------------------------------------------------------------
	//初始化磁盘物理结构
//---------------------------------------------------------------------------
	//
	// 每个扇区有512个字节
	//
	devExt->DiskGeometry.BytesPerSector = 512;

	//
	// 每个磁道有32个扇区
	//
	devExt->DiskGeometry.SectorsPerTrack = 32;

	//
	// 每个柱面有两个磁道
	//
	devExt->DiskGeometry.TracksPerCylinder = 2;

	//
	// 柱面数就要由总容量计算了
	//
	devExt->DiskGeometry.Cylinders.QuadPart = devExt->DiskRegInfo.DiskSize / 512 / 32 / 2;

	//
	// 磁盘的类型就是我们自己定义的 RAMDISK_MEDIA_TYPE
	//
	devExt->DiskGeometry.MediaType = RAMDISK_MEDIA_TYPE;

	KdPrint( (  "柱面数: %ld\n 柱面磁道数: %ld\n 磁道扇区数: %ld\n 扇区字节数: %ld\n",
	            devExt->DiskGeometry.Cylinders.QuadPart,
	            devExt->DiskGeometry.TracksPerCylinder,
	            devExt->DiskGeometry.SectorsPerTrack,
	            devExt->DiskGeometry.BytesPerSector ) );

//---------------------------------------------------------------------------
//	初始化文件系统信息
//---------------------------------------------------------------------------
	//
	// 根据用户指定的值, 初始化根目录数目
	//
	rootDirEntries = devExt->DiskRegInfo.RootDirEntries;

	//
	// 根据用户指定值对每个族有多少个扇区进行初始化
	//
	sectorsPerCluster = devExt->DiskRegInfo.SectorsPerCluster;

	//
	// 由于根目录入口点只使用32字节, 但是最少占用一个扇区,
	// 这里为了充分利用空间, 在用户指定数目不合适时, 会修正这个数目
	//
	if ( rootDirEntries &amp; ( DIR_ENTRIES_PER_SECTOR - 1 ) ) {

		rootDirEntries = ( rootDirEntries + ( DIR_ENTRIES_PER_SECTOR - 1 ) ) &amp;
		                 ~( DIR_ENTRIES_PER_SECTOR - 1 );
	}

	KdPrint( ( "根目录入口: %ld\n 族扇区数: %ld\n",
	           rootDirEntries, sectorsPerCluster  ) );

//---------------------------------------------------------------------------
//	我们这个磁盘不含有MBR, 直接就是从DBR开始
//---------------------------------------------------------------------------
	//
	// 这里必须使用0xEB和0x90, 这是文件系统的检查标记
	//
	bootSector->bsJump[0] = 0xeb;
	bootSector->bsJump[1] = 0x3c;
	bootSector->bsJump[2] = 0x90;

	//
	// 填写OEM名称成员, 这里当然是写 JoenRam了, 刚好8个
	//
	bootSector->bsOemName[0] ='J';
	bootSector->bsOemName[1] ='o';
	bootSector->bsOemName[2] ='e';
	bootSector->bsOemName[3] ='n';
	bootSector->bsOemName[4] ='R';
	bootSector->bsOemName[5] ='a';
	bootSector->bsOemName[6] ='m';
	bootSector->bsOemName[7] ='';

	//
	//每个扇区有多少个字节, 这个成员直接取之前初始化磁盘的信息结构
	//
	bootSector->bsBytesPerSec = ( SHORT )devExt->DiskGeometry.BytesPerSector;

	//
	// 这个卷只有一个保留扇区, 既DBR本身
	//
	bootSector->bsResSectors  = 1;

	//
	// 和正常的卷不同, 为了节省空间, 我们只存放一份FAT表, 而不是通常的两份
	//
	bootSector->bsFATs        = 1;

	//
	// 根目录的入口点数目, 由之前的的计算得知
	//
	bootSector->bsRootDirEnts = ( USHORT )rootDirEntries;

	//
	// 这个磁盘的总扇区数由磁盘总大小和每个扇区的字节数计算得到 磁盘大小/扇区字节
	//
	bootSector->bsSectors	  = ( USHORT )( devExt->DiskRegInfo.DiskSize / devExt->DiskGeometry.BytesPerSector );

	//
	// 磁盘介质类型由之前的初始化磁盘信息得到
	//
	bootSector->bsMedia       = ( UCHAR )devExt->DiskGeometry.MediaType;

	//
	// 每个族有多少个扇区由之前的计算值初始化得到
	//
	bootSector->bsSecPerClus  = ( UCHAR )sectorsPerCluster;

//---------------------------------------------------------------------------
	// 计算这个磁盘FAT表所占用的空间
//---------------------------------------------------------------------------

	//
	// FAT表的表项数目是由总扇区数减去保留的扇区数, 再减去根目录入口点所占用
	// 的扇区数, 然后除以每族的扇区数, 罪的结果需要+2, 因为FAT第0项, 第1项是保留的
	//
	fatEntries = ( bootSector->bsSectors - bootSector->bsResSectors - bootSector->bsRootDirEnts /
	               DIR_ENTRIES_PER_SECTOR ) / bootSector->bsSecPerClus + 2;

	//
	// 上面已经计算出了FAT表的表项数量, 根据这个表项数量首先可以决定到底是用FAT12还是FAT16
	// 文件系统, 决定了文件系统就可以计算出每个FAT表的表项需要占用多少空间, 进而计算出整个FAT
	// FAT表所占用的扇区数
	//
	if ( fatEntries > 4087 ) {
		fatType =  16;						//文件系统类型
		fatSectorCnt = ( fatEntries * 2 + 511 ) / 512;		//一个表项需要占用多少个扇区
		fatEntries   = fatEntries + fatSectorCnt;		//记FAT表里面有多少个表项
		fatSectorCnt = ( fatEntries * 2 + 511 ) / 512;		//一个FAT表项需要占用多少个扇区
	} else {
		fatType =  12;
		fatSectorCnt = ( ( ( fatEntries * 3 + 1 ) / 2 ) + 511 ) / 512;
		fatEntries   = fatEntries + fatSectorCnt;
		fatSectorCnt = ( ( ( fatEntries * 3 + 1 ) / 2 ) + 511 ) / 512;
	}

//---------------------------------------------------------------------------
//	接着初始化DBR的其他属性
//---------------------------------------------------------------------------
	//
	// 初始化FAT表所占用的分区数
	//
	bootSector->bsFATsecs       = fatSectorCnt;

	//
	// 初始化DBR中每个磁道的扇区数
	//
	bootSector->bsSecPerTrack   = ( USHORT )devExt->DiskGeometry.SectorsPerTrack;

	//
	// 初始化磁头数, 也就是每个柱面的磁道数
	//
	bootSector->bsHeads         = ( USHORT )devExt->DiskGeometry.TracksPerCylinder;

	//
	// 初始化启动签名, Windows要求必须是0x28 或者0x29
	//
	bootSector->bsBootSignature = 0x29;

	//
	// 卷ID, 随意了
	//
	bootSector->bsVolumeID      = 0x12345678;

	//
	// 设置卷标, 随便搞了, 反正就是11字符
	//
	bootSector->bsLabel[0]  ='J';
	bootSector->bsLabel[1]  ='o';
	bootSector->bsLabel[2]  ='e';
	bootSector->bsLabel[3]  ='n';
	bootSector->bsLabel[4]  ='D';
	bootSector->bsLabel[5]  ='i';
	bootSector->bsLabel[6]  ='s';
	bootSector->bsLabel[7]  ='k';
	bootSector->bsLabel[8]  ='';
	bootSector->bsLabel[9]  ='';
	bootSector->bsLabel[10] ='';

	//
	// 设置文件系统类型
	//
	bootSector->bsFileSystemType[0] ='F';
	bootSector->bsFileSystemType[1] ='A';
	bootSector->bsFileSystemType[2] ='T';
	bootSector->bsFileSystemType[3] ='1';
	bootSector->bsFileSystemType[4] ='?';
	bootSector->bsFileSystemType[5] ='';
	bootSector->bsFileSystemType[6] ='';
	bootSector->bsFileSystemType[7] ='';
	bootSector->bsFileSystemType[4] = ( fatType == 16 ) ?'6' :'2';

	//
	// DBR结尾标记
	//
	bootSector->bsSig2[0] = 0x55;
	bootSector->bsSig2[1] = 0xAA;

//---------------------------------------------------------------------------
	//上面总算把这个DBR给初始化完了, 接下来需要初始化FAT表
//---------------------------------------------------------------------------

	//
	// 定位到FAT表的起始点, 这里的定位方式是利用了DBR治愈一个扇区这个条件
	//
	firstFatSector    = ( PUCHAR )( bootSector + 1 );

	//
	// 填写介质类型
	//
	firstFatSector[0] = ( UCHAR )devExt->DiskGeometry.MediaType;

	// 填写结束标记
	//
	firstFatSector[1] = 0xFF;
	firstFatSector[2] = 0xFF;

	if ( fatType == 16 ) {
		firstFatSector[3] = 0xFF;
	}
//---------------------------------------------------------------------------
	//初始化根目录, 在FAT12/16文件系统中, 通常第一个根目录入口点存储了最终
	//被作为卷标的目录入口点.
//---------------------------------------------------------------------------
	//
	// 由于紧跟着FAT表, 所以根目录入口点的表起始位置很好找的
	//
	rootDir = ( PDIR_ENTRY )( bootSector + 1 + fatSectorCnt );

	//
	// 初始化卷标 JoenChen
	//
	rootDir->deName[0] ='J';
	rootDir->deName[1] ='o';
	rootDir->deName[2] ='e';
	rootDir->deName[3] ='n';
	rootDir->deName[4] ='C';
	rootDir->deName[5] ='h';
	rootDir->deName[6] ='e';
	rootDir->deName[7] ='n';

	//
	// 设置设备的扩展名称, 空着吧
	//
	rootDir->deExtension[0] ='';
	rootDir->deExtension[1] ='';
	rootDir->deExtension[2] ='';

	//
	// 将这个入口点的属性设置为卷标属性
	//
	rootDir->deAttributes = DIR_ATTR_VOLUME;

	KdPrint( ( "格式化磁盘成功!\n" ) );

	return STATUS_SUCCESS;
}

//===========================================================================
//	磁盘传递的参数检测
//devExt	:设备扩展指针
//ByteOffset	:读取偏移
//Length	:读取长度
//===========================================================================
BOOLEAN RamDiskCheckParameters( PDEVICE_EXTENSION devExt, LARGE_INTEGER ByteOffset, size_t Length ) {

	//
	// 读取参数检测, 如果磁盘的大小小于读取的长度, 或者偏移小于0,
	// 或者偏移大于磁盘大小+读取的长度, 获取长度有没有按照扇区对齐
	//
	if( devExt->DiskRegInfo.DiskSize < Length || ByteOffset.QuadPart < 0 ||
	        ( ( ULONGLONG )ByteOffset.QuadPart > ( devExt->DiskRegInfo.DiskSize - Length ) ) ||
	        ( Length &amp; ( devExt->DiskGeometry.BytesPerSector - 1 ) ) ) {

		//
		// 直接返回参数错误
		//
		KdPrint( ( "参数错误\nByteOffset: %x\n Length: %d\n", ByteOffset, Length ) );

		return FALSE;
	}

	return TRUE;
}