【文章标题】: 真正的驱动入门()[附小程序代码]
【文章作者】: CCDeath       
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
   很多初学驱动入门比较难.最后发现“难”是难在配置方面和WDM型几个比较抽象的概念。偶学了有半个月吧,很多网上写
   的不全,终于了解比较简单的整个流程了...
  
  1.书籍推荐  有个大侠推荐的...这些书籍可以从看雪、电骡、迅雷、驱网、得益网下得到....
  
  前提必须会点c语言、MFC框架[测试的时候,弄一个比较漂亮的截面:-)]
  《windows 2000 内部揭密>>--------------------------------------这里面可以学到什么是IRP、内存管理、文件系统之类
  《驱动模型设计>>-----------------------------------------------好详细的一本书.
  《windows 2000\xp wdm 设备驱动程序设计>>-----------------------使用DS开发,无论如何使用里面的工具会更帅...说真的...
  《DDK文档及DDK里面的例子>>DDK开发包----------------------------安装的时候要注意,一定把她及所在例子钩起来,不然
                                                                 你找不到DDK文档的....
  www.driverdevelop.com------------------------------------------人气有点低,不够还很很不错..,专业人士较多
  www.pediy.com--------------------------------------------------人气很高,很热情,各个领域人的都有.
  
  2.
    虚拟机(VMWare等):建议装Windows 2000.iso  不然配置会出现很多的错误,同时候用softice或者windbg,避免OS蓝屏,把
                     时间浪费在开机上...
  
    DriverStudio3.2:开发工具包.如果喜欢在DDK环境下,也可以利用这个开发包里面的一些工具:DriverMonitor、
                    EAZDriverInstaller,softice
  
    Windbg:中文教程较少.但是跟微软走,比较有保障.就怕他最后要收钱...双机调试.
  
    VC6.0++:这个要先安装,然后才安装DDK,这个顺序可不能乱..            
  
    DDK:没得说..VC开发工具包含这个目录,才能弹出提示符号比如 this->点这边才能弹出。.
               在工具->选项->目录->H:\WINDDK\2600\INC\DDK\WXP.有时候H:\WINDDK\2600\INC\DDK不能识别,不能this->
  
    VC助手:这个工具一定要装,很好用的。很快捷,掉渣...
  
  注意:如果配置上还有问题的话,百度将会是你最后的归宿...
  
  3.先解决简单概念问题后解决WDM模型的概念
  
  a.OS:时间分片,但是记得给每个线程的时间CPU不一定相同.
  
  #include "windows.h"
  #include "iostream.h"
    void main()
    {
      HANDLE  hThread;
      hThread=CreateThread(NULL,0,ThreadProc,NULL,0,NULL);//不写ThreadProc了...懒 
      CloseHandle(hThread);//一.关闭线程句柄,线程可没终止.2.主线程main对线程引用不感兴趣3. 前为n=0.
                           //当执行CreateThread() n+1;hThread=CreateThread() n+1+1
                          // 所以现在计数可是n(n=0)+2=2;当关闭CloseHandle(hThread)n=n-1;还有线程结束时n=n-1;
                          // 最后n=0,系统检测为0,才会释放线程
  }
  
  顺便提一下Sleep()函数:貌似很简单一个问题,睡觉谁不会呀...却十分体现出CPU分片的问题.
  Sleep(400),比如我们的hThread的时间是运行600毫秒,突然运行到300秒,来了个Sleep(400),过了之后,清醒过来,请问现在
  hThread还可以运行多少毫秒?是300秒?还是0秒? 应该是0秒...在Sleep睡眠期间,CPU干什么呢?赶快从队列找一个优先级
  高线程的来玩玩
  
  b.有异步的地方,肯定需要同步的存在....宏观上是异步,但是执行同一种,同一时间,资源,就需要同步
  有很多种具体书籍<<win32多线程设计>>--侯捷译。不知道是谁写的...:-)。这时候什么信号量、互斥对象、事件将会
  是你的最爱
  
  void main()
  {
    HANDLE hMutext=CreateMutex(NULL,true\false,"ccdeath");//true表示调用CreateMutex线程拥有所有权,即main()主线程拥有,其他线称不可以CloseHanle(hMutex),只能
  //创建之后都是设为已通知状态.                                                        //拥有者释放 
  }
  
  直接理解:事件为钥匙和要访问的资源为房间,更为简便.
  
  有信号(已通知状态):就是告诉线程,说偶(事件)这边有钱,大家(线程)快来抢钱...
  
  c.类型转换
  
  PVOID& IoctlBuffer (void);ULONG RegWord; 
  
  (ULONG)IocltBuffer()=?RegWord;----1.先PVOID===指针+void。先解决void转化
  (ULONG *)IocltBuffer()=RegWord;---2.接着解决指针转化
  ------------------------------------3.转化完之后.再进行一般化处理
  |----------------------------------------------------------------|
  |int *p;-----------------------------ULONG * IocltBuffer()       |
  |int i=5;----------------------------ULONG RegWord;              |
  |*p=i;-------------------------------*IoCltBuffer()=RegWord;     |
  |----------------------------------------------------------------|
  *(ULONG *)IocltBuffer()=RegWord-----4.这才是是正确的.
  
  我们看IocltBuffer()是个指针.
  
  d.类型定义,虽然看似简单.
  
  typedef NTSTATUS (*R)(KIrp I);------5
  NTSTATUS wo(Krp I)------------------6
  R=&wo;------------------------------7 什么情况下成立,就是5和6参数和返回值要相同.
  有时候在DDK编程中出现NTSTATUS **AddDevice(PDRIVER_OBJCET DriverObject,PDEVICE_OBJECT fdo);
  DriverEntry()
  {  DriverObject->MarjorFunction[IRP_MJ_XX]}=**AddDevice;//我们是怎么判断为什么她的原形是上面那个呢?用VC助手
  //go一下,跑到MajorFunction[]就明白。原来用了typedef,从根据根据上面7就知道了,类型与返回值要相同...
  规律总结typedef int p; 
           p i=5;
  就是把变量(在int p时候,p为变量)当成类型(typedef int p,p为类型)来用。
  
  
  5.接下来我们继续看实际例子,从武安河稍微改装了一个,一个是基于DS+main(),另为一个是基于DS+MFC测试程序。
  
  a.我的DS是3.2。好象没有main和MFC框架只有win32sdk。不知道你们是不是...碰到好多人跟偶是一样的....
  
  *.inf:配置文件,怎么安装驱动,提供这个驱动的拥有者的信息之类的等...
  *.sys:我们要的驱动....驱动大业,蒸蒸日上;看学学院,蒸蒸日上。
  
  首先点"DDK Build Settings"->Luanch Program.接着启动VC,工具栏就看见DriverStudio工具栏了.点设置,开始...
  在设置中,反正没有console测试程序,干脆测试程序就不要集成到驱动程序里面去。把设置中的"集成测试程序"打勾
  给去掉...自己在写一个集成测试程序,再集合到驱动程序也一样...
  
  1.文件打开空间(*dsw)  ,去到C:\Program Files\Compuware\DriverStudio\DriverWorks\source下重新编译一下,
  选择批(最后两个)组建.
   Build Environment Variables
   BASEDIR=H:\WINDDK\2600
   vdw_wdm.lib - 0 error(s), 0 warning(s)
  
  就OK了...
  
  2.然后再编译,靠,还出现问题...不怕,偶已经从win2003 ddk中把需要的文件都拷贝过来了。缺什么,偶们就补什么..
   csq.lib
   csq.h
   ntstrsafe.lib
  在附件里,偶会上传的.
  
  3.基本OK了。开始写驱动,接着写驱动程序,接着调试驱动程序,接着安装驱动程序,打开监视器观察是否有错误发生,再
  返回来修改驱动程序。好了,一步步来了...
  
  先在DriverEntry(){ DbgPrint("驱动加载了....");} 编译一下,生成*.sys. 用监视器(DS里面的工具Moniter)打开这个
  *.sys.接着按go...是不是看到驱动加载了...
  
  122.734    Default        CCDeath: CCDeathDriver::DriverEntry++. Compiled at 21:33:07 on Dec  7 2007
  122.734    Default        CCDeath: 驱动加载了....
  122.734    Default        CCDeath: CCDeathDriver::DriverEntry--. STATUS 0
  122.734    Default        CCDeath: CCDeathDriver::Unload++.
  122.734    Default        CCDeath: CCDeathDriver::Unload--.
       Monitor         -- end -- 
  
  高兴吧....
  
  
  要写一个通信程序...驱动程序和应用程序是两个不同世界的人...想沟通是需要桥梁的....
  
  按照上面方法创建一个驱动程序为CCDeath.沟通的桥梁就是CCDeathInterface.h和CCDeathIoctl.h,自己手动添加...具体
  要里面的内容要看书籍才行。具体函数就是DeviceControl()这个东西。
  
  CCDeathIoctl.h://控制命令,请注意存放了如何ruan  冲区的方式是METHOD_BUFFERED\METHOD_IN_DIRECT\METHOD_NEITHER
  CCDeathInterface.h//设备的接口,我们可以用的....
  
  从新打开一个工程新建一个consle测试程序.为TestCCDeath;也包含CCDeathInterface.h和CCDeathIoctl.h这两个头文件才
  能沟通..."..//CCDeathInterface.h" "..//CCDeathIoctl".
  把TestCCDeath放入到CCDeath目录下,接着打开CCDeath工程,选择“工程”->"插入工程到工作空间"。
  添加头文件和变量
  
  //添加头文件:
  #include "..\\CCDeathInterface.h"
  #include "..\\CCDeathIoctl.h"
  //添加变量:
  GUID CCDeathClassGuid=CCDeathDevice_CLASS_GUID;//要修改下面一个函数哦
  
  在DeviceControl()函数中对3中方式进行处理...
  
  NTSTATUS CCDeathDevice::DeviceControl(KIrp I)
  {
    
    NTSTATUS status = STATUS_SUCCESS;
  
    switch (I.IoctlCode())
    {
      case READ_DWORD:
      status=CCDeath_DWORD_Hander(I);    
      break;
    case READ_STRING:
      status=CCDeath_STRING_Hander(I);
      break;
    case READ_BOOLEAN:
      status=CCDeath_BOOLEAN_Hander(I);
      break;
    default:
      status = STATUS_INVALID_DEVICE_REQUEST;
      break;
    }
          return  status;
  }
  
  NTSTATUS CCDeathDevice::CCDeath_DWORD_Hander(KIrp I)
  {
  ....
处理
  }
  三个处理共同点:
  I.information()=sizeof(ULONG);//成功的时候
  I.information()=0;//失败的时候
  三个处理不同点:
  
  
  
  写测试程序:
  碰到第一个问题:Cannot open include file: 'devintf.h': No such file or directory
  解决方法:在TestCCDeath.dsp加这么一行
  # ADD CPP /I "$(DRIVERWORKS)\include"
  
  
  碰到第二个问题:
  OpenByInterface.obj : error LNK2001: unresolved external symbol __imp__SetupDiDestroyDeviceInfoList@4
  OpenByInterface.obj : error LNK2001: unresolved external symbol __imp__SetupDiGetClassDevsA@16
  OpenByInterface.obj : error LNK2001: unresolved external symbol __imp__SetupDiGetDeviceInterfaceDetailA@24
  OpenByInterface.obj : error LNK2001: unresolved external symbol __imp__SetupDiEnumDeviceInterfaces@20
  Release/TestCCDeath.exe : fatal error LNK1120: 4 unresolved externals
  执行 link.exe 时出错.
  TestCCDeath.exe - 1 error(s), 0 warning(s)
  解决方法:#pragma comment(lib,"setupapi")
  
  一切OK 了,运行一下测试程序结果。
  
  开始要测试CCDeath驱动程序了:
  错误码ERROR opening device: (0) returned from CreateFile
  退出测试程序...
  
  Press any key to continue
  
  碰到问题三:上面的运行失败了。
  解决办法那是我们还没安装驱动了...
  
  b.安装驱动:有2种方法,个人比较喜欢下面这种...因为简单,所以喜欢....
  1.先打开DS中的工具Monitor开始监视
  2.打开DS中的工具EZDriverInstaller(快速安装驱动),选择*.inf。在Add New Device,加载*.sys.
  
  OK
  3.运行我们的测试程序.
  4.观察监视机,再反复调试..softice ... 下次见了...
  
  运行结果:
  
  开始要测试CCDeath驱动程序了:
  设备已经被发现,打开句柄成功....
  READ_DWORD:0x1e240
  Press any key to continue
  
  
  
  
  
  .
  
  
  
  
  
--------------------------------------------------------------------------------
【经验总结】
  下一次继续哦.....觉得有帮助...顶顶哦....
  Press any key to continue....
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年12月07日 23:06:35


【文章标题】: 真正的驱动入门(二)
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
      (-)出后,感觉如果要想学驱动还看一下《驱动模型设计》等等一堆大牛写的,我的只能提供一种更便捷途径,比如配置
  方面的问题,一些大牛书写的比较模糊的概念。注:概念是针对偶比较模糊...:-)。
  
      这次先完成上次的基于MFC框架测试程序的编写,接着分析WDM模型模糊的概念。最后一个就是我们的多功能程序,把所
  学融合到一起...今天和明天和后天一口气完成...
  
       接着上次的.我们现在来做一个MFC框架程序.选择对话框模式,并去掉配置过程中的“关于对话框”和“ActiveX控件”,
  使程序看起来更简洁和苗条..H0 ho....
  
      在TestCCDeathMfc.dsp工程添加OpenByInterface.cpp.编译一下。说无法找到 <devintf.h>  //
  
      接着修改TestCCDeathMfc.dsp,在Source=\OpenByInterface.cpp.添加跟以前一样...
  
      修改OpenByInterface.cpp头文件改为如下:
  
  #include "StdAfx.h"
  #include <winioctl.h>   //XP需要 这个....
  #include <devintf.h>  // DriverWorks
  
      在主框架添加头文件:
  
  //添加头文件:
  #include "..\\CCDeathInterface.h"
  #include "..\\CCDeathIoctl.h"
  #include <winioctl.h>   //XP需要 这个....
  //函数声明:
  HANDLE OpenByInterface(GUID* pClassGuid, DWORD instance, PDWORD pError);
  //变量声明:
  HANDLE  hDevice = INVALID_HANDLE_VALUE;
  GUID ClassGuid = CCDeathDevice_CLASS_GUID;
  
  加入到驱动程序工程。接着设为活动工程,开始进行界面设置
  OK 程序运行效果一样.....
  
  MFC测试框架效果图:
  
  一、第一步:请结合<<驱动模型设计>>Walter Oney 来看....写的太好了,第一次看不懂,看了第二次,还是不懂,看了第三遍,
  懂了一点点。
  
  a.分页与非分页 && 任意线程上下文与非任意上下文
  
  分页:不妨理解内存与硬盘的交互
  非分页:不妨理解内存
  任意线程上下文:任意,可以理解不固定,不确定的线称。上下文(环境):描述表,就是一个结构了,包含线程信息。
                  就象女朋友可以有很多个。
  非任意线程上下文:就是已经确定的线程。就象老婆只有一个。
  
  
  b.IRQL与自旋锁:主要解决共享资源的问题;  而内核同步对象主要控制线程流程。
  
  IRQL是解决单CPU抢先问题,一个1cpu
  自旋锁是解决多CPU抢先(并发)问题,多个CPU。
  
  但是 我们明白我们程序设计要符合顶层的需要,必须得让我们的驱动程序支持多CPU,所以采用IRPL是不够的。自旋锁,是
  最明智的选择。
  
  其实IRQL很好理解的...还有自旋锁也很好理解的....沉住气...多看几遍。注:把这几函数给背起来,怀恋小时侯,小学背
  课文....
  
  背函数:
  IRP:
    KIRQL Irp;
    KeRaiseIrql(XXX_LEVEL,&Irp);
    KeLowerIrql(Irp);
  
  SpinLock:
    KSPIN_LOCK  spinlock;
    KIRQL Irp;
    KeAcquireSpinLock(&spinlock,&Irp);
    KeReleaseSpinLock(&spinlock,Irp);
  
  所谓的自旋是指序将在一个小的循环内重复这个“测试并设置(test-and-set)”操作。其实自旋是一种很棒的机制,我
  爱原子......HoHo....
  
  对于DISPATCH_LEVEL都封装了更快捷的操作,前提是你必须知道你这个是在DISPATCH_LEVEL,不然其余就免谈了..
  
  IRP:
    KeRaiseIrqlToDpcLevel();
  
  SpinLock:
  
    KeAcquireSpinLockAtDpcLevel();
    KeRealseSpinLockFromDpcLevel();
  
  
  对于界限DISPATCH_LEVEL
  
    IRQL:执行在高于或等于DISPATCH_LEVEL级上的代码绝对不能造成页故障。
    SpinLock你仅能在低于或等于DISPATCH_LEVEL级上请求自旋锁,在你拥有自旋锁期间,内核将把你的代码提升到
    DISPATCH_LEVEL级上运行。
  c.还是想说一下数组指针与指针数组...
    static NTSTATUS (*fcntab[])(PDEVICE_OBJECT, PIRP) = <--3
    {
      HandleStartDevice, // IRP_MN_START_DEVICE
      HandleQueryRemove, // IRP_MN_QUERY_REMOVE_DEVICE
      <etc.>,
    };
  
  数组指针是指向数组的, 
  如: int a[3][3],(*p)[3]; 
      p=a; 
   
  指针数组是这样一种特殊的数组:它的每一个数组元素都是一个指针。 可以这么理解:指针数组,数组一个就行了,不被
  束缚(没有括号)
  如:int *p[3]; 
          |   |
         指针+ 数组 = 指针数组 好记吧....
  *p[0],*p[1],*p[2]都是一个指针。 
  
  
  
  二、请求包的问题。只要看懂这个这节,其余相对比较简单:-)....
  
  
  IRP的结构是用来与驱动程序通信的,记得上次的I.information()=sizeof(ULONG)吗?
  常用的东东:I/O管理器、Pnp管理器、线程调度器、 OS、驱动程序.明白各自的职责。
  
  a.IRP结构较难理解的地方:
  
  IRP结构中的一个函数.
  PendingReturned(BOOLEAN):这个函数偶一开始都不知道是怎么一回事...天呀....
  
  if ( Irp->PendingReturned(true) )//true时候返回STATUS_PENDING.如果返回这个值必须调用这个值;
  {
    IoMarkIrpPending( Irp );//pengding中文意思的是等候的意思...
  } 
  一个IRP用IoMarkIrpPending()把它转为未处理完成状态
  STATUS_PENDING以通知调用者我们没有完成这个IRP。 
  
  b.I/O堆栈单元跟IRP有什么关系:
  
  我们可以从数据结构的角度来分析她:下面是IRP的结构中的CurrentLocation (CHAR)成员函数和Tail成员变量,可以看出。
  CurrentLocation (CHAR)和Tail.Overlay.CurrentStackLocation(PIO_STACK_LOCATION)没有公开为驱动程序使用,因为你完
  全可以使用象IoGetCurrentIrpStackLocation这样的函数获取这些信息。但意识到CurrentLocation就是当前I/O堆栈单元的索
  引以及CurrentStackLocation就是指向它的指针,会对驱动程序调试有一些帮助。
  
  c.I/O堆栈单元很歧义的一句话
  
   “同时还创建了一个与之关联的IO_STACK_LOCATION结构数组:数组中的每个堆栈单元都对应一个将处理该IRP的驱动程序,
  另外还有一个堆栈单元供IRP的创建者使用”
  怎么跑出来两个堆栈单元??崩溃ing....
  
  
  
  驱动程序通常做的是分配一个带有额外堆栈单元的IRP,在第一个单元中设置DeviceObject指针,在调用
  IoSetCompletionRoutine和IoCallDriver前用IoSetNextIrpStackLocation函数跳过那个额外堆栈单元。如果你这样做,
  那么在完成例程中调用IoMarkIrpPending将不会出现问题,并且完成例程也能得到了一个有效的设备对象。 
  用哪个函数创建这个额外的堆栈单元呢? 
  
  2K源代码来自驱网
  
  #define IopInitializeIrp( Irp, PacketSize, StackSize ) { \ 
  RtlZeroMemory( (Irp), (PacketSize) ); \ 
  (Irp)->Type = (CSHORT) IO_TYPE_IRP; \ 
  (Irp)->Size = (USHORT) ((PacketSize)); \ 
  (Irp)->StackCount = (CCHAR) ((StackSize)); \ 
  (Irp)->CurrentLocation = (CCHAR) ((StackSize) + 1); \ 
  (Irp)->ApcEnvironment = KeGetCurrentApcEnvironment(); \ 
  InitializeListHead (&(Irp)->ThreadListEntry); \ 
  (Irp)->Tail.Overlay.CurrentStackLocation = \ 
  ((PIO_STACK_LOCATION) ((UCHAR *) (Irp) + \ 
  sizeof( IRP ) + \ 
  ( (StackSize) * sizeof( IO_STACK_LOCATION )))); } 
  
  NTSTATUS 
  FASTCALL 
  IopfCallDriver( 
  IN PDEVICE_OBJECT DeviceObject, 
  IN OUT PIRP Irp 
  ) 
  
  /*++ 
  
  Routine Description: 
  
  This routine is invoked to pass an I/O Request Packet (IRP) to another 
  driver at its dispatch routine. 
  
  Arguments: 
  
  DeviceObject - Pointer to device object to which the IRP should be passed. 
  
  Irp - Pointer to IRP for request. 
  
  Return Value: 
  
  Return status from driver's dispatch routine. 
  
  --*/ 
  
  { 
  PIO_STACK_LOCATION irpSp; 
  PDRIVER_OBJECT driverObject; 
  NTSTATUS status; 
  
  // 
  // Ensure that this is really an I/O Request Packet. 
  // 
  
  ASSERT( Irp->Type == IO_TYPE_IRP ); 
  
  // 
  // Update the IRP stack to point to the next location. 
  // 
  Irp->CurrentLocation--; 
  
  if (Irp->CurrentLocation <= 0) { 
  KeBugCheckEx( NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) Irp, 0, 0, 0 ); 
  } 
  
  irpSp = IoGetNextIrpStackLocation( Irp ); 
  Irp->Tail.Overlay.CurrentStackLocation = irpSp; 
  
  // 
  // Save a pointer to the device object for this request so that it can 
  // be used later in completion. 
  // 
  
  irpSp->DeviceObject = DeviceObject; 
  
  // 
  // Invoke the driver at its dispatch routine entry point. 
  // 
  
  driverObject = DeviceObject->DriverObject; 
  
  PERFINFO_DRIVER_MAJORFUNCTION_CALL(Irp, irpSp, driverObject); 
  
  status = driverObject->MajorFunction[irpSp->MajorFunction]( DeviceObject, 
  Irp ); 
  
  PERFINFO_DRIVER_MAJORFUNCTION_RETURN(Irp, irpSp, driverObject); 
  
  return status; 
  } 
  
  太微妙了,IO请求包个人感觉概念抽象程度较高...至少在这边要看2遍以上....想想整个流程真的很清晰....
                   NTSTATUS status=
  
  NTSTATUS IoCallDriver(PDEVICE_OBJECT device, PIRP Irp)
  IoCallDriver()
  {...
  return (*driver->MajorFunction[fcn])(device, Irp);
  
  }
                                       
  三.即插即用 ;下面只是提了一下,重点在综合程序里头。
  术语:I/O资源
  WDM包含四种标准I/O资源类型:I/O端口、内存寄存器、DMA通道、中断请求。 
  因为I/O总线与CPU在寻址物理硬件的方式上不同,所以存在着两种资源列表。
  raw资源包含总线相关的数值,而translated资源包含系统相关的数值。
  //===综合程序解说
  3种通知方式:
  窗口通知(SDK,可参考ccdeath中的《实用软件-详细代码注释》) :响应WM_DEVICECHANGE消息
  服务通知 :调用RegisterServiceCtrlHanderEx寄存一个扩展的控制处理函数后
  内核模式通知:调用IoRegisterPlugPlayNotification.
  自制通知:IoReportTargetDeviceChange| IoReportTargetDeviceChageAsynchronous
  控制命令:
  //采用METHOD_BUFFERD模式
  #ifndef __CCDeathPnPIoctl__h_
  #define __CCDeathPnPIoctl__h_
  
  #define PNPEVENT_IOCTL_800 CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_BUFFERED, FILE_ANY_ACCESS)
  #endif
  接口
  //接口文件包含窗口通知、定制通知、内核通知
  
  #define WindowsDevice_CLASS_GUID \
     { 0x7cbbad7c, 0x3873, 0x476b, { 0xa1, 0x22, 0x1e, 0x8e, 0x1a, 0x7e, 0xc6, 0x6a } }
  
  DEFINE_GUID(MakeDevice_CLASS_GUID, 0x28ce01a1, 0x9436, 0x11d2, 0x81, 0xb5, 0x0, 0xc0, 0x4f, 0xa3, 0x30, 0xa6);
  
  #define  KernerDevice_CLASS_GUID \
     { 0xc6287288, 0x81d8, 0x4c2b, { 0x92, 0x21, 0x95, 0xb1, 0xaa, 0xed, 0x29, 0x49 } }
  
  //在Device.cpp加入:头文件和变量名和一些API声明;
  
  //===========下面我们来看核心部分...
  
  
  
  
  四.读写数据
  
  五.电源管理
  
  ...AddDevice(....)
  {.....
  
    CCDeathDevice* pDevice = new (           //分配内存一个设备对象内存
              NULL,                    // no name
        FILE_DEVICE_UNKNOWN,
              NULL,                    // no name
        0,
        DO_DIRECT_IO
          | DO_POWER_PAGABLE//状态位DO_POWER_PAGABLE
        )
      CCDeathDevice(Pdo, m_Unit);//初始化对象
  
    if (pDevice == NULL)                      //分配是否成功
    {
      T<<"错误的创建设备对象"<<"\n";
      status = STATUS_INSUFFICIENT_RESOURCES;
    }
    else
    {
      status = pDevice->ConstructorStatus();//判断其构造函数有没有错误 ...
      if (!NT_SUCCESS(status))
      {
        delete pDevice;//有错构造,则删除设备对象
      }
      else
      {
        m_Unit++;
        pDevice->ReportNewDevicePowerState(PowerDeviceD1);//由开始状态0转入1
      }
    }
  ....
  }
  //分配在OnDevicePowerUp()和OnDeviceSleep()例程中分别判断是系统电源还是设备电源就可以了....
  然后再写2个函数分别根据当前上升的电源状态标位得出具体的状态名...
  
  PCHAR DevicePowerStateName(DEVICE_POWER_STATE ps);
  PCHAR SystemPowerStateName(SYSTEM_POWER_STATE ps);
  
  比如:
  PCHAR SystemPowerStateName(SYSTEM_POWER_STATE ps)//PS是具体的数值
  {
    static PCHAR PowerStates[] = {//先根据DDK找出电源的状态,列出来
                                //或者SYSTEM_POWER_STATE,光标定位在这,VC助手go一下,把
                                //这个状态拷贝过来   
      "PowerSystemUnspecified",
      "PowerSystemWorking",
      "PowerSystemSleeping1",
      "PowerSystemSleeping2",
      "PowerSystemSleeping3",
      "PowerSystemHibernate",
      "PowerSystemShutdown"
    };
  
    if (ps > PowerSystemShutdown)  //最大值是6,如果超过这6种,被定位没有声明的状态。
      return "<undefined power state>";
    else
      return PowerStates[ps];//根据位置判断好
  }
  
  电源睡眠的时候不需要检测。
  电源上升的时候每隔30秒检测一下。
  
  
  六.windows管理诊断管理[省略]
  
  七.综合程序
  
  顺便提一下,生成对话框之后必须删除设备句柄。自己在写一个构造函数,或者重载OnDestory()都是可以的...
  CPnp::~CPnp()
  {
    if (m_hDevice!=INVALID_HANDLE_VALUE)
    {
      delete m_hDevice;
    }
  }
  
  电源管理程序部分如上,运行效果如下:
  
  
  10.328    Default        ccdeath说: CCDeathDevice::Create++.  IRP 8254B008
  10.328    Default        ccdeath说: CCDeathDevice::Create--.  IRP 8254B008, STATUS 0
  10.328    Default        ccdeath说: CCDeathDevice::OnDevicePowerUp++.  IRP 827A5C90
  10.328    Default        ccdeath说: 已经进入了OnDevicePowerUp
  10.328    Default        ccdeath说: 设备的电源状态PowerDeviceD0
  10.328    Default        ccdeath说: CCDeathDevice::OnDevicePowerUp--.  IRP 827A5C90, STATUS 0
  12.719    Default        ccdeath说: CCDeathDevice::DeviceControl++.  IRP 8269C610
  12.719    Default        ccdeath说: 已经进入到电源处理了....
  12.719    Default        ccdeath说: CCDeathDevice::DeviceControl--.  IRP 8269C610, STATUS 0
  41.469    Default        ccdeath说: CCDeathDevice::OnDeviceSleep++.  IRP 82673338
  41.469    Default        ccdeath说: 已经进入了OnDevicePowerSleep
  41.469    Default        ccdeath说: 设备的电源状态PowerDeviceD1
  41.469    Default        ccdeath说: CCDeathDevice::OnDeviceSleep--.  IRP 82673338, STATUS 0
       Monitor         -- end -- 
  
     
  
--------------------------------------------------------------------------------
【经验总结】
  
  12月9号是偶的生日,谢谢郭姐姐买蛋糕给偶吃和送礼物给偶,让偶四年里度过最后一个,也是最好的一个生日...祝福郭姐
  姐永远快乐,出去工作以后,事事顺心,开开心心....
  
  这些都是安河兄的例子,只是稍微改装了...花了三天写的...
  
  下次拿自己做的,主要分析调试*.Sys方面的...下一回就是终结版了...
  
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2007年12月10日 11:12:17

  • 标 题: 答复
  • 作 者:foxabu
  • 时 间:2007-12-08 04:10

很好很强大: 
虽然LZ的确比较怀旧的说 
 虚拟机(VMWare等):建议装Windows 2000.iso  不然配置会出现很多的错误,同时候用softice或者windbg,避免OS蓝屏,把
                     时间浪费在开机上...
据说2K 已经退出微软的主流支持了.无论是研究服务器模式 还是station模式 貌似都不是特别好
比如说新的驱动模式WDK 就没有2K的份 .
个人觉得 搞一般开始还是XP比较好 毕竟现在XP的普及率高达 0.8
  
    DriverStudio3.2:开发工具包.如果喜欢在DDK环境下,也可以利用这个开发包里面的一些工具:DriverMonitor、
                    EAZDriverInstaller,softice
这个东西在XP上都不是很好安装  Vista 就更不用说了 装了等于不装.,第一 它已经停止开发了
其次它的调试器无论是单机还是双击 都基本上处于无法用的状态.微软有专门的驱动诊断工具..DS这个东西貌似还算有些过时了
DS里面的工具Moniter: 这个用被微软收购的  DbgView来看就可以的说.
  
    Windbg:中文教程较少.但是跟微软走,比较有保障.就怕他最后要收钱...双机调试.
  
    VC6.0++:这个要先安装,然后才安装DDK,这个顺序可不能乱..            
最近都出VS2008 了不晓得 中国人是缺钱还是咋的 就喜欢那个VC6.简直想不通.先不说得不到任何微软的支持 .编译器也显然比起之后的版本差.要搞效率的朋友至少应该注意了 VC6 可没有SSe2 指令选项,其次新版的SDK 根本无法在VC6种编译通过.01年就是它得最后版本了.我在想01年 貌似连 XP都还没有比如SP2
时代 更不要说什么2k3 Vista了.
况且VC6的不稳定和编译器对模板支持还有ISO支持都是有问题的.完全搞不明白的说. 
  
    DDK:没得说..VC开发工具包含这个目录,才能弹出提示符号比如 this->点这边才能弹出。.
               在工具->选项->目录->H:\WINDDK\2600\INC\DDK\WXP.有时候H:\WINDDK\2600\INC\DDK不能识别,不能this->
  反正DDK6000 我是在微软的官方下载的 .我个人觉得还是新点好些.一些未文档化的API .都有了部分LIB文件 可以直接链接了.另外 DDK6000 开始 帮助文档就和MSDN一样了 是那个专用浏览器而不是CHM 这个很罪恶的东西 
    VC助手:这个工具一定要装,很好用的。很快捷,掉渣...
这个暂时不发表意见 ,我用VS2008 配合 VA 完全是不顺手.当然写点模板或者C++STL 应用还是有好处的,因为这个VS的代码提示有些问题.以前还是比较喜欢用的 过了一年多不用 还真是不习惯了 不过话说回来了 用它浏览代码还是不错的  代码颜色比较漂亮.不过话又说回来了 无论怎么说 VA的代码辅助功能 根VC#比起来还是差距大着了[貌似有些偷换概念了  ] 微软有时考虑东西还是蛮人性化的. 所以我觉得不用VA 就学Windows源代码那样 甚至用notepad来写:) 开个玩笑


另外建议真正想玩驱动的朋友  去CodeProject 看那个入门教程   那个教程的应该说不错 至少得到中外人士的一致肯定 比起MSDN来更分类系统一些.基本把 WDM的 内存管理 IO管理 这些用最为精炼的语句做了一个大致的介绍.至少看了以后 写点SSDT HOOKER 不是什么难事了
但是最后还是得用MSDN来啃的 ,任何所谓的'快速'入门或者XX天入门这种  都不太可信,除非你只是做一个兴趣话的了解 当新闻看  那就无可厚非了.
LZ不要生气奥 :)