﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-漫步者-文章分类-windows驱动开发</title><link>http://www.cppblog.com/Walker/category/15446.html</link><description>先学会转文章，在仔细读文章，最后自己写点东西........</description><language>zh-cn</language><lastBuildDate>Thu, 07 Jun 2012 07:08:46 GMT</lastBuildDate><pubDate>Thu, 07 Jun 2012 07:08:46 GMT</pubDate><ttl>60</ttl><item><title>bug check</title><link>http://www.cppblog.com/Walker/articles/147058.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Tue, 24 May 2011 13:41:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/147058.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/147058.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/147058.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/147058.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/147058.html</trackback:ping><description><![CDATA[1 访问用户空间地址<br />2 加锁与解锁不匹配<img src ="http://www.cppblog.com/Walker/aggbug/147058.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-24 21:41 <a href="http://www.cppblog.com/Walker/articles/147058.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>.INF文件</title><link>http://www.cppblog.com/Walker/articles/146850.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Sat, 21 May 2011 01:21:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146850.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146850.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146850.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146850.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146850.html</trackback:ping><description><![CDATA[<strong>1、.INF文件是什么<br /><br /></strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .INF是 Device Information File 的缩写，是微软公司为供硬件设备制造商发布其设备驱动程序而发展的&#8212;&#8212;许多硬件设备的驱动程序都是使用 .INF文件来安装的。.INF文件从 Windows3.X 时代就开始大量被使用了。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .INF 文件是一种具有特定格式的纯文本文件，我们可说它是一种安装脚本（SetupScript）。虽然 .INF 只是纯文本文件，但是当我们在文件管理器explorer对 .INF文件按鼠标右键後，在右键菜单上就会出现&#8220;安装&#8221;命令，这是因为微软公司已在其操作系统 Windows 中内置提供了 Setup API（可以解释.INF脚本文件），我们只需用文本编辑软件编写 .INF文件，便可完成大部份的安装工作，所以尤其是在软体的大小并不是很大的情况下，安装工作不是很复杂的时候，使用 .INF文件来进行安装工作将会是一个好选择。而且如果要安装设备驱动程序，.INF文件是目前唯一的选择。 可以用 .INF文件创建包括注册表条目和目的目录的自定义软件安装指令。.INF文件可以提供有限的平台独立性，并指定有限的软件依赖性。目前.INF文件最普 遍的应用是为安装硬件设备的驱动程序服务的，本文的目的就是介绍 .INF文件的功能、结构、并提供了几个事例来说明如何用.INF文件，如何扩展.INF文件的用途，比如制作绿色软件，仅供参考。<br /><br /><strong>2、.INF文件的格式</strong><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .INF文件是由许多节（Section）组成，与.INI文件类似，整个.INF文件由几个节组成，节名用方 括号扩起来，如version 节，Manufacturer节，和Strings节等等。而每个节又由一系列的条目组成，每个条目都是是由一个键（Key）与一个值（Value）组 成，都是&#8220;Key=Value&#8221;这样的形式，在这些节中定义的项目可以完成硬件的自动检测和软件（包括驱动程序）的安装。.INF文件中分号后面的字符串 是注释。在一个 .INF文件中，所有跟随在分号（;）后的文字都会被视为注释。注释并不一定要在新行开始，可以在一行文字後面加入注释。<br /><br /><strong>3、.INF文件案格式和作用如下所示：</strong><br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Version] 节： 包含对此 .INF文件的简短描述与此 .INF文件支持的设备类型的信息。用于确认 .INF文件的基本版本信息。任何.INF文件都必须 包括这个节。 [Version] 节中的语句 Signature="$CHICAGO$"表明这个.INF文件可以用于windows 95以后的所有平台上，目前来说就是win98，winme，winnt4，win2k，winxp，win2k3。这种.INF文件比较多见。&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果 [Version]节中的签名条目是 Signature="$Windows 95$"，表明这个.INF文件可以用于windows 95以后的Win9x平台上，目前来说就是win98，winme。 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果[Version]节中的签名条目是 Signature="$WINDOWS NT$"，表明这个.INF文件可以用于WindowsNT系列的平台上，包括winnt4，win2k，winxp，win2k3。WIN9x系列的操 作系统的SetupAPI将会拒绝执行这种类型的.INF文件。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Manufacturer] 与 [Manufacturer Name] 节： 列出此 .INF文件可辨识的所有硬件设备，并列出了开发该硬件的厂商名称，主要用于硬件设备的安装。在设备驱动程序的.INF文件中必须包括 [Manufacturer]节与 [Manufacturer Name] 节。[Manufacturer]节和[Microsoft]节，安装新硬件的向导中列出来的厂商名字和设备名 称就是来自这两个节。这行语句&#8220;%MfgName%=Microsoft&#8221; 的等号右边的名字指明设备制造商是Microsoft，等号左边是厂商的名字，厂商名称是"Microsoft"（在后面讲到的[Strings] 节中）。在一个.INF文件中可以有多个设备制造商的名字，都必须放在[Manufacturer]节中，同时[Manufacturer Name] 节可以列出该厂商的多种设备。 <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Manufacturer Name] 节区含有对要安装的设备的描述，并为要安装的设备指出 [Install] 节区。<br /><br />[Manufacturer Name]节语法：<br />[manufacturer-name]<br />device-description=install-section-name,device-id[,compatible-device-id]...<br />device -description就是对要安装的设备的描述。install-section-name就是此设备的 [Install] 节区名称，manufacturer-name 节区名称必须已在 [Manufacturer] 节区中被定义。device-id是此设备的硬件标志符，每个厂家的不同的硬件对应着不同的设备标志符。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 普通的安装软件的.INF文件中，不包括[Manufacturer] 与 [Manufacturer Name] 节，即使包含，也不会执行它们，这2个节仅仅用于硬件的设备驱动中起作用。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Install] 节： 描述设备驱动程序与硬件设备的实际属性。它也定义了所有 [Install] 节的名称，在此定义的节中包含了安装该设备的信息和命令。默认情况下，会执行 [DefaultInstall] 节，Install操作就会执行[DefaultInstall] 节。[DefaultInstall] 节中包含指向其他节的指针，该节可用于指定要复制和删除的文件、注册表的更新、.INF文件的更新等。<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果是安装普通的软件的.INF文件，可以通过鼠标右键菜单上的&#8220;安装&#8221;命令来安装，这个时候.INF文件必须包括 [DefaultInstall]节，也可以包括其他的[Install]节，用RUNDLL32.EXE ETUPAPI.DLL,InstallHinfSection [Install]节的名字，这样类似的命令来安装。<br /><br />[Install]节的语法：<br />[install-section-name]<br />Copyfiles=file-list-section[,file-list-section]...<br />LogConfig=log-config-section-name<br />Renfiles=file-list-section[,file-list-section]...<br />Delfiles=file-list-section[,file-list-section]...<br />UpdateInis=update-ini-section[,update-ini-section]...<br />UpdateIniFields=update-inifields-section[,update-inifields-section]...<br />AddReg=add-registry-section[,add-registry-section]...<br />DelReg=del-registry-section[,del-registry-section]...<br />Ini2Reg=ini-to-registry-section[,ini-to-registry-section]...<br />UpdateCfgSys=update-config-section<br />UpdateAutoBat=update-autoexec-section<br />[Install] 节定义了安装程式与硬体驱动需要的资源，以便安装新的驱动程式或者软件。<br />此节中每个条目都有其特定格式与意义，并非每个条目都是必要的。无论是安装驱动程序还是普通的软件，最后都要从某一个[Install] 节开始执行。 <br /><br />[DestinationDirs] 节： 指定硬盘上复制、删除或重命名节文件的位置（例如 \Windows 或 Windows\System）。 .INF文件通过[DestinationDirs]节来指定操作的目标路径，语法如下：<br />[DestinationDirs] <br />file-list-section =ldid[, subdir ] <br />DefaultDestDir=ldid[, subdir ]<br />[DestinationDirs] 节定义了 [file-list-section] 节中指定的操作（可以是 CopyFiles、RenFiles 或 DelFiles 节）的目标目录。DefaultDestDir命令可以为 .INF文件中的任何没有明确在[DestinationDirs]节中命名的CopyFiles、RenFiles或DelFiles节指定默认目标文 件夹。<br /><br />[FileCopy/Delete/RenameSection(s)] 节：列出要复制、删除或重命名的文件。 节的名字是CopyFiles，DelFiles，RenFiles。<br />[RegistryUpdateSection(s)] 节：指定在注册表中添加或删除的项目。 节的名字是AddReg，DelReg。<br />[IniFileUpdateSection(s)] 节：指定 .ini 文件的更新。链接将在该节中创建。 节的名字是 UpdateInis。<br /><br />在.INF文件中，使用逻辑磁盘标识符 (LDID) 来表示路径，如下表：<br />00 Null LDID - 可用于创建新的 LDID <br />01 Source Drive:\pathname <br />10 Windows 文件夹（等价于%windir%目录 ） <br />11 System 文件夹 <br />12 IOSubsys 文件夹 <br />13 Command 文件夹 <br />17 Inf 文件夹 <br />18 Help 文件夹 <br />20 Fonts <br />21 Viewers <br />22 VMM32 <br />23 Color 文件夹 <br />24 包含 Windows 文件夹的驱动器根目录 <br />28 Host Winboot <br />30 启动盘的根文件夹 <br />31 虚拟启动盘的主驱动器根文件夹<br /><br />[ClassInstall] 节：为设备定义一个新的类别（Class）。主要用于硬件设备的安装。<br />[SourceDisksNames] 节： 列出包含文件的磁盘。 <br />[SourceDisksFiles] 节： 列出每个文件所在的特定磁盘。 <br /><br />其他的节还有许多，如：Update .ini Fields 节 (UpdateIniFields)，Add Ini File to Registry 节 (Ini2Reg)，Update Config.sys 节 (UpdateCfgSys)，Update Autoexec.bat 节 (UpdateAutoBat)，Optional Components 节，等等。 <br /><br />.INF文件 是由Windows的SetupAPI解释执行的脚本文件，它的运行过程很简单，是一种线性的执行，线性的意思就是.INF文件的运行过程不存在分支语 句，也就是没有条件语句，一旦开始执行，就是沿着固定的路线运行。它的运行是按照节为单位来执行的，从某一个[Install]节开始执行，从上到下执行 该节中的条目，如果该条目是一个节，那么就一条条执行子节中的条目，如此递归执行。<br /><br />这样的脚本看起来功能很弱，但是对于简单的安装任务已经足够 了，安装不外乎复制文件，添加注册表，修改.INI文件等任务，这些都可以用.INF文件来完成。对于更复杂的安装要求，如要求安装程序的界面漂亮方便， 要求有选择性的安装，要求安装程序自动修复的功能，就只好求助于其他的工具了，如微软的Windows Installer，就具备更加强大的功能。<br /><br /><strong>4、.INF文件的版本</strong><br /><br />.INF文件的语法是统一的分节语法，随着操作系统的更新，微软逐渐增加了一些必要的关键字，但是整个的.INF文件的结构不会变化。从语法上来说，.INF文件都是一个类别。<br />按照.INF文件的版本来分类，可以分为2类：<br />1 AdvancedINF，在[Version]节中有&#8220;AdvancedINF=2.5,"您需要新版本的 AdvPack.dll" &#8221;这么一行语句，表明此.INF文件需要AdvPack.dll这个动态链接库来解释执行，AdvancedINF有一些高级特性，但是在目前用的不太 多。<br />2 普通INF，没有指明需要AdvPack.dll的.INF文件，使用SetupAPI.DLL来解释执行（win9x系列的操作系统使用Setupx.dll），系统中缺省使用的就是这种普通INF。<br /><br /><strong>5、.INF文件的功能</strong><br /><br />从前面的介绍，能够看出使用 .INF文件可以完成如下功能：<br />1 复制文件、删除文件、或重新命名文件。<br />2 新增或删除注册表（Registry）中的项目。<br />3 修改重要的系统设置文件（如 Autoexec.bat、Config.sys、.INI 等）。<br />粗 看起来，似乎上面提到的第一个功能可以用批处理文件.BAT来完成，第二个功能可以通过注册表文件.REG来完成，第三个功能现在用得不多，不经常处理. INI文件了。这样说起来似乎.INF文件也没有太大的优势啊。这种看法是有问题的，下面我将会着重指出.INF在在这几个方面的应用，而不会仔细讲解. INF的语法。 下面要提到.INF文件优于别的工具的方面。 <br /><br /><strong>6、用.INF文件编辑注册表</strong><br /><br />.INF文件可以编辑操纵注册表，相应的的命令是AddReg和DelReg，<br />语法如下：<br />AddReg = add-registry-section[,add-registry-section]...<br />[ add-registry-section] <br />reg-root-string, [subkey], [value-name], [flag], [value] <br />[add-registry-section]节定义了将要添加的注册表子键或值名，可以有选择地设置它的值。<br />DelReg = del-registry-section[,del-registry-section]...<br />[ del-registry-section] <br />reg-root-string, subkey, [value-name] <br />[ del-registry-section]节定义了将要从注册表中删除子键subkey或值名value-name。<br />有时候上网遇到某些网站，修改了IE的主页，然后禁用了REGEDIT工具，这个时候很多人只好借助于其他第三方工具如超级兔子等来恢复注册表的编辑权限，其实这个时候仅仅依靠.INF文件就可以恢复使用REGEDIT工具的权限。 <br />把下面的这段代码复制到记事本里，另存为jiereg.inf,然后右击它选择&#8220;安装&#8221;，就可以了。<br />[Version] <br />Signature= "$CHICAGO$" <br />[DefaultInstall] <br />AddReg=My.Add.Reg <br />[My.Add.Reg] <br />HKCU, "SOFTWARE\Microsoft\windows\currentversion\policies\system","disableregistrytools", 0x00010001,"0" <br />HKLM,"SOFTWARE\Microsoft\windows\currentversion\policies\system","disableregistrytools", 0x00010001,"0" <br />当然还可以用其他的方法，原理也是一样的，这里仅仅是演示一下.INF文件的用途。<br /><br />直接使用注册表文件，有时候还会遇到另外一个缺点，就是.REG文件里面使用16进制代码表示UNICODE字符串的，如下面的两条注册表语句：<br />[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders]<br />"Cookies"=hex(2):25,00,55,00,53,00,45,00,52,00,50,00,52,00,4f,00,46,00,49,00,4c,00,45,00,25,00,<br />5c,00,43,00,6f,00,6f,00,6b,00,69,00,65,00,73,00,00,00,00,00<br /><br />[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SoftEther]<br />"ImagePath"=hex(2):22,00,44,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,20,<br />00,46,00,69,00,6c,00,65,00,73,00,5c,00,53,00,6f,00,66,00,74,00,45,00,74,00,68,00,65,00,<br />72,00,5c,00,53,00,6f,00,66,00,74,00,45,00,74,00,68,00,65,00,72,00,2e,00,65,00,78,00,65,<br />00,22,00,20,00,73,00,65,00,72,00,76,00,69,00,63,00,65,00,00,00,00,00<br /><br />这上面两条语句难以阅读和修改，手工修改的话很麻烦，其中<br />"Cookies"的值实际上就是&#8220;%USERPROFILE%\Cookies&#8221;字符串的UNICODE表示。<br />"ImagePath"的值实际上就是"D:\Program Files\SoftEther\SoftEther.exe" service<br />如果用.INF文件的形式的话，就很简单明白了:<br />[Version]<br />Signature="$CHICAGO$"<br />[DefaultInstall]<br />AddReg=Folders_AddReg<br />[Folders_AddReg]<br />HKCU,"Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders","Cookies",0x00020000,"%USERPROFILE%\Cookies"<br />HKLM,"SYSTEM\CurrentControlSet\Services\SoftEther","ImagePath",0x020000,"""D:\Program Files\SoftEther\SoftEther.exe"" service"<br />这个时候可以手工修改里面的路径信息，而.REG文件则很难做到这一点。<br /><br /><strong>7、用.INF文件编辑.INI文件</strong><br /><br />.INF文件可以使用UpdateInis命令修改.INI文件的功能也经常需要，UpdateInis的语法如下：<br />UpdateInis = update-ini-section[,update-ini-section]...<br />[ update-ini-section-name] <br />ini-file, ini-section, [old-ini-entry], [new-ini-entry], [flags] <br />[update-ini-section-name]节给出的 .INF 文件中替换、删除或添加的全部条目。ini-file 包含要更改条目的 .ini 文件名。 ini-section 包含要更改条目的节名。 old-ini-entry 可选，常用形式为 Key=Value。new-ini-entry 可选，常用形式为 Key=Value。flags 是可选操作标记。<br />使用逻辑磁盘标识符 (LDID)，可以方便的修改.INI文件中的路径信息，当然其他的条目也同样。举例如下： Total Commander ，是一套极佳的文件管理员，内含各种压缩与解压缩，类似NC般非常好用的工具，对于文件与路径的寻找，除一般的复制、删除、搬移、编辑等功能外，还有 FTP功能(具有续传、背景传输)与解决了中文乱码问题，"专题"再也不会变成"彩题"、新增文件分割、文件合并、文件编码、文件解码(MIME， UUE，XXE)及新的操作介面(浮动工具列)。真是一套相当强悍可完全取代文件总管的工具程序。Total Commander依靠wincmd.ini来配置相应的操作特性和功能，于是可以使用如下的.INF文件修改wincmd.ini中的路径信息：<br />[version]<br />Signature="$CHICAGO$"<br />[DefaultInstall]<br />AddReg=AddGhister<br />UpdateInis=UpdateInicmd <br />[AddGhister]<br />HKCU,"Software\Ghisler\Total Commander","IniFileName",0,".\\wincmd.ini"<br />HKCU,"Software\Ghisler\Total Commander","FtpIniName",0,".\\wcx_ftp.ini" <br />[UpdateInicmd]<br />%01%\wincmd.ini, Configuration,,"InstallDir=%01%"<br />%01%\wincmd.ini, Configuration,,"Mainmenu=%01%\LANGUAGE\TCExtMenu.mnu"<br />其 中 [AddGhister]节负责修改注册表中记录的wincmd.ini的路径，而[UpdateInicmd]节负责修改wincmd.ini文件红的 相应路径信息，可以看出，一个简单的.INF文件就完成了修改Total Commander的路径的任务，在Total Commander5.x版本中，必须修改wincmd.ini文件中的路径信息，这个时候使用.INF文件是非常方便的。<br /><br /><strong>8、用.INF文件删除正在使用的文件</strong><br /><br />有时候由于某些文件正被操作系统使用而无法删除，如某些用于资源管理器的OCX控件，可以使用.INF文件的DelFiles命令删除，该命令如果发现要删除的文件被锁定，就会把文件放到系统删除队列中排队，等系统重启动的时候，该文件就自动被删除了。<br />Delfiles的语法是：<br />Delfiles= file-list-section [,file-list-section]...<br />[file-list-section]<br />filename,,,1<br />[file -list-section]节定义了将要删除的文件列表,filename后面的1是一个标志，指明如果文件当前无法删除，就等到系统重启动后删除。把 下面的这段代码复制到记事本里，另存为delinuse.inf,把这个文件拷贝到无法删除的文件的目录，然后右击它选择&#8220;安装(I)&#8221;，就可以删除当前 目录下正在使用的setup.exe和setup2.exe。<br />[Version]<br />Signature="$Chicago$"<br /><br />[DestinationDirs]<br />DefaultDestDir = 01 ; 当前目录 ，定义了将要删除的文件的路径 <br />[DefaultInstall] <br />DelFiles = DeleteLIST <br />[DeleteLIST]<br />setup.exe ,,,1; 将要删除的文件<br />setup2.exe ,,,1; 将要删除的文件<br />同样可以使用Copyfiles命令替换系统正在访问的文件。这些功能通过普通的del和copy命令都无法实现，如果不使用.INF文件，必须使用第三方的软件来完成。<br /><br /><strong>9、辅助制作绿色软件</strong><br /><br />在注册表.REG文件中，不可以使用变量，这个缺陷对于需要设置路径的安装程序来说，是非常致命的弱点，.REG文件中的路径都是静态的，一旦写好，就不会 随着系统的变化而变化，如果需要把安装的程序从C盘改变到D盘，而在.REG文件中记录了安装路径的话，这个时候直接倒入注册表文件.REG是不行的，导 入的注册表仍然是原来写入的路径。而在.INF文件中，可以使用变量来操纵路径，从而可以跟踪安装程序所需要的路径的变化。<br />对于绿色软件的制作，其中核心的一点就是获得原来软件的安装程序所做出的注册表的改变，然后把该变化导出为一个注册表文件. REG来进一步分析。如果导出的.REG文件不包括绝对路径，那么可以把该注册表文件和提取出的软件打包在一起做成绿色软件。如果导出的.REG文件中包 含当前软件的绝对安装路径，那么就必须每次都要手工修改注册表中路径，是很麻烦的，降低了做成绿色软件的意义所在。而使用.INF文件，使用AddReg命令可以轻松容易的解决这个注册表路径的难题。<br /><br />对于绿色软件的安装制作来说，最重要的就是01，也就是源文件夹。下面举一个例子：<br />Registry Crawler V4.5 ，是强大的用户和开发者快速定位并配置注册表的工具软件。一个强大的搜索引擎允许你基于搜索标准查找注册信息。（可以从天空软件站下载http: //www4.skycn.com/soft/2963.html），安装完成后导出的注册表包含着如下一条语句：<br />[HKEY_LOCAL_MACHINE\SOFTWARE\4Developers\RCrawler\AppPath]<br />@="F:\\tools\\Reg\\RCrawler"<br />其中&#8220;F:\tools\Reg\RCrawler&#8221;是安装路径。<br />而改写为.INF文件就是：<br />[version]<br />Signature="$CHICAGO$"<br />[DefaultInstall]<br />AddReg=Add<br />[Add]<br />HKLM,"SOFTWARE\4Developers\RCrawler\AppPath","",0,"%01%"<br />注意里面的%01%就代表当前的安装路径。<br /><br />当我们把F:\tools\Reg\RCrawler这个目录下的文件移动到其他目录时候，要用.REG文件的方式的话，就必须手工修改注册表文件，而使用 前面的.INF文件的时候，什么都无需修改，只要在文件管理器explorer里面，用鼠标右键执行&#8220;安装&#8221;命令的时候，%01%就自动被替换为当前所在 的目录了。 <br /><br />最后，总结一句，注册表文件.REG非常方便和直观，但是对于处理路径的变化的情况，非常笨拙和不方便，而.INF不如注册表文件直观，但是可以方便的处理路径信息（包括UNICODE的路径信息），所以应该把.REG文件和.INF文件结合使用，互相弥补各自的缺点。<img src ="http://www.cppblog.com/Walker/aggbug/146850.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-21 09:21 <a href="http://www.cppblog.com/Walker/articles/146850.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Driver post-Developement Tech</title><link>http://www.cppblog.com/Walker/articles/146849.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Sat, 21 May 2011 01:16:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146849.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146849.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146849.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146849.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146849.html</trackback:ping><description><![CDATA[<h4 style="margin-bottom: 0px" class="TextColor1">Driver post-Developement Tech</h4>
<div>
<h3>编译时检查</h3>
<h4>使用 /Wall及/WX参数编译</h4>
<p>/Wall：打开所有的警告 </p>
<p>/WX：将警告视为错误 </p>
<p>但是DDK本身的头文件中还包括警告信息，可使用关闭警告的功能来克服这个问题，这些警告大多都是由微软对标准语言的扩展引起的，不影响可靠性，我们可以在源文件中关闭这些警告： </p>
<p>MSC_WARNING_LEVEL=/Wall /W4 /WX /wd4115 /wd4127 /wd4200 /wd4201 /wd4214 /wd4255 /wd4514 /wd4619 </p>
<p>/wd4668 /wd4820 </p>
<p>更好的方法是仅在引起这些警告的MS包含文件部分关闭这些警告，如下： </p>
<p>Source文件中为： </p>
<p>MSC_WARNING_LEVEL=/Wall /WX </p>
<p>使用如下语句包围引起警告的MS包含文件： </p>
<p>#pragma warning (disable: 4115 4127 4200 4201 4214 4255 4619 4668 4820) </p>
<p>... </p>
<p>#pragma warning (default: 4115 4200 4214 4255 4619 4668 4820) </p>
<p>注意：不同DDK版本出现的警告不同 </p>
<h4>使用C++编译器</h4>
<p>在驱动开发中使用C++会带来一些问题，但使用C++编译器将有一些额外的好处，因为C++编译器会做更多的检查，使用C开始同时使用C++编译器编译的方法如下： </p>
<p>使用/TP编译开关，这个开关让编译器将.c文件作为.cpp文件来编译，这种技术通常在check版本中使用，在source文件中需要加入如下代码： </p>
<p>!if !$(FREEBUILD) </p>
<p>USER_C_FLAGS= /TP </p>
<p>!endif </p>
<p>同时，需要对DriverEntry进行标记： </p>
<p>#ifdef __cplusplus </p>
<p>extern "C" </p>
<p>#endif </p>
<p>NTSTATUS DriverEntry( </p>
<p>IN PDRIVER_OBJECT DriverObject, </p>
<p>IN PUNICODE_STRING RegistryPath); </p>
<p>如果我们使用了WPP，还需要标记WPP的包含文件。 </p>
<h4>PREfast</h4>
<p>PREfast按照显示规则对代码进行静态分析（DDK自带） </p>
<h4>定义DEPRECATE_DDK_FUNCTIONS</h4>
<p>在source 文件的C_DEFINES定义DEPRECATE_DDK_FUNCTIONS，将使编译器在编译是检查我们的代码是否使用了DDK已经过时的例程。但是 DEPRECATE_DDK_FUNCTIONS并不能检查出所有的过时例程，唯一的办法是检查DDK文档。在驱动中使用 MmGetSystemRoutineAddress例程可以用来判断我们使用的例程是否还被支持。 </p>
<h4>使用编译时处理</h4>
<h5>C_ASSERT</h5>
<p>C_ASSERT是一个编译时断言 </p>
<h5>条件测试</h5>
<p>使用#error MSG可以产生一个比C_ASSERT可读性更好的编译时错误： </p>
<p>#define BLK_SHIFT 9 </p>
<p>#define BLK_SIZE 512 // Must be 1 &lt;&lt; BLK_SHIFT </p>
<p>#if !(BLK_SIZE = (1 &lt;&lt; BLK_SHIFT)) </p>
<p>#error !(BLK_SIZE == (1 &lt;&lt; BLK_SHIFT)) </p>
<p>#endif </p>
<h4>编译32位&amp;64位驱动版本</h4>
<p>编译64位版本可发现如下的问题： </p>
<h5>指针问题</h5>
<p>使用32位值存储指针、数据结构因为包含指针而改变大小 </p>
<h5>内联汇编</h5>
<p>在驱动中不应该使用内联汇编 </p>
<h5>编译器差异</h5>
<p>64位结构上的代码兼容性 </p>
<h4>ChkINF</h4>
<p>安装前使用ChkINF检查INF文件（DDK自带） </p>
<h3>调试及运行时检查</h3>
<p>1、使用所有的检查选项调试 </p>
<p>2、逐个关闭检查选项调试 </p>
<p>3、关闭所有调试选项，在目标机运行，使用性能检测软件监视 </p>
<h4>内核调试器</h4>
<p>KD：命令行调试器 </p>
<p>WinDbg：带有图形前端的调试器 </p>
<h4>Check版Windows</h4>
<p>使用Check版Window调试 </p>
<h4>驱动验证（Driver Verifier）</h4>
<p>主动测试工具（Windows自带），同常设置如下检查： </p>
<ul><li>自动检查（必选） </li><li>特殊内存检查 </li><li>IRQL检查 </li><li>内存池跟踪 </li><li>I/O验证 </li><li>DMA验证 </li><li>死锁检测</li></ul>
<p>其中有一项<strong>低资源模拟</strong>测试将在驱动中注入一些资源请求失败错误，这项检查应该在排除了其它错误后，最后来测试 </p>
<h4>调用验证（CUV）</h4>
<p>使用调用验证需要更改source文件并重新编译驱动： </p>
<p>VERIFIER_DDK_EXTENSIONS=1 </p>
<p>CUV支持如下类型的检查： </p>
<ul><li>初始化检查：使用一个内核类型前对其进行验证 </li><li>一致性检查：检查内核例程调用的一致性 </li><li>分页内存检查：检查不正确的分页内存使用 </li><li>IRP堆栈检查：验证IRP堆栈的使用</li></ul>
<p>使用CUV支持编译后的驱动在启动后将向调试器报告错误信息 </p>
<h4>缓冲池标记</h4>
<p>缓冲池标记可以在分配缓冲区前加上4个字符的标记，Windows2000及XP仅在check版本上打开了缓冲池标记，free版本可使用gflags /r +ptg命令打开，2003所有的版本都打开了缓冲池标记。可以通过3种途径来查看这些缓冲池标记： </p>
<ul><li>调试器 </li><li>Poolmon（Windows自带） </li><li>Pooltag（DDK自带）</li></ul>
<p>从XP以后，我们都应该使用PROTECTED_POOL标记调用ExFreePoolWithTag来释放我们分配的内存，当驱动验证的特殊内存检查开启时，缓冲池标记不能被开启 </p>
<h4>代码覆盖</h4>
<p>代码覆盖包括行覆盖及路径覆盖，可通过如下途径来提高代码覆盖率： </p>
<ul><li>测试所有有效的请求 </li><li>测试所有不正确的输入 </li><li>使用驱动验证中的低资源模拟 </li><li>随机的注入错误</li></ul>
<h4>性能监测</h4>
<p>使用KrView及KernRate可以检测驱动的CPU占用率，我们应该通过两种方法来测试驱动的负载： </p>
<ul><li>在整个系统上运行性能监测，即使驱动负载较大，在整个系统负载中，它应该只占一个比较小的比例 </li><li>在驱动上运行性能监测，分析驱动中最花时间的部分</li></ul>
<h4>内核日志</h4>
<p>通过Trace View（DDK自带），我们可以记录9种类型的数据： </p>
<ul><li>进程创建及终止 </li><li>线程创建及终止 </li><li>文件I/O </li><li>磁盘I/O </li><li>映象文件加载及卸载 </li><li>注册表访问</li></ul>
<h4>安装日志</h4>
<p>通过使能安装日志，我们可以跟踪驱动在安装时的动作，在注册表种设置如下键值： </p>
<p>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurentVersion\Setup </p>
<p>为0x3000FFFF可以使能详细安装日志，在完成调试后，需要关闭这个日志以保证性能，日志文件在%systemroot%\setupapi.log </p>
<h4>杂项工具</h4>
<ul><li>DriverQuery（Windows自带）：使用这个工具可以查询系统中驱动的相关信息 </li><li>DeviceTree：显示设备及驱动的相关数据 </li><li>DevCon：命令行工具，提供所有设备管理的功能，部分DriberQuery、DeviceTree功能，DDK的src\setup\devcon有源码，可用于编写驱动控制工具 </li><li>RegEdit32：注册表编辑工具</li></ul>
<h3>诊断</h3>
<h4>断言</h4>
<p>通常使用的断言宏有: </p>
<p>ASSERT ( expression ) </p>
<p>ASSERTMSG ( message, expression ) </p>
<p>在Win2000以前的系统中,这两个宏不返回任何值,以后的系统中,这两个宏返回expression的值,这两个断言仅在check版本中有效,通常在如下情况下使用断言: </p>
<ul><li>在进入和退出非静态函数时要对静态数据成员进行检查 </li><li>在进行一个处理前,校验输入参数是否正确 </li><li>在进入和退出循环前, 校验输入参数是否正确 </li><li>在使用指针时,对指针及指针指向的数据进行校验;在使用全局变量前,对其进行检验</li></ul>
<p>校验的方式: </p>
<ul><li>检查指针是否有效</li></ul>
<p>除了检查指针是否为NULL外,我们还可以做进一步检查.例如检查数据的类型: </p>
<p>ASSERTMSG ( &#8220;Invalid IRP pointer&#8221;, </p>
<p>irp != NULL &amp;&amp; irp-&gt;Type == IO_TYPE_IRP) </p>
<ul><li>检查获得的值是否在范围以内 </li><li>检查双向链表的一致性</li></ul>
<p>ASSERTMSG ( &#8220;Corrupted List&#8221;, </p>
<p>listPtr != NULL &amp;&amp; listPtr-&gt;Flink != NULL &amp;&amp; </p>
<p>listPtr-&gt;Flink-&gt;Blink == ListPtr ) </p>
<ul><li>检查字符串的有效性</li></ul>
<p>ASSERTMSG ( &#8220;Invalid Counted String&#8221;, string != NULL &amp;&amp; </p>
<p>string-&gt;MaxLength &gt; string-&gt;Length &amp;&amp; </p>
<p>string != NULL ? (string-&gt;Length != 0 ) : TRUE )) </p>
<ul><li>检查函数执行时的IRQL </li><li>避免检查的侧效</li></ul>
<p>ASSERT ( --CurrentCount &gt; 0 ) </p>
<ul><li>注意区分断言检查和错误检查的区别</li></ul>
<p>p = ExAllocatePoolWithTag ( NonPagedPool, BLK_SIZE, <br />BLK_TAG ); <br />ASSERT ( p != NULL )<br />在分配内存时,因为系统资源不足而引起的分配失败是很正常的事情,这里不应该使用断言 </p>
<h4>调试打印</h4>
<p>通常使用的调试打印有如下四种: </p>
<p><strong>KdPrint (</strong> format, <strong>... );</strong> </p>
<p><strong>KdPrintEx (</strong> componentId<strong>,</strong> level<strong>,</strong> format<strong>, ... );</strong> </p>
<p><strong>DbgPrint (</strong> format<strong>,</strong> <strong>... );</strong> </p>
<p><strong>DbgPrintEx (</strong> componentId<strong>,</strong> level<strong>,</strong> format<strong>, ... );</strong> </p>
<p>头两个调用仅在check版本有效,后两个在所有版本都有效, componentId 和 level用来过滤调试输出 </p>
<p>通常应该打印输出的信息如下: </p>
<ul><li>打印文字而不是数字</li></ul>
<p>例如用IRP_MJ_CLOSE代替0x2 </p>
<ul><li>打印描述数据结构的信息,而不是指向数据结构的指针</li></ul>
<p>例如打印出IRP包的主功能码 </p>
<ul><li>使用标准格式打印 </li><li>打印调用栈 </li><li>更多的输出信息将有助于调试,例如文件名,行号,当前执行线程,IRQL </li></ul>
<p>格式化的规则: </p>
<ul><li>如果要打印指针,使用%p格式符,这样可以使程序在32位及64位平台上均可运行 </li><li>如果打印一个计数的字符串,使用%Z (ANSI)或者%wZ (Unicode),它可以正确打印出非空结束的字符串 </li><li>如果需要打印Unicode值,必须确认程序运行在DISPATCH_LEVEL级别之下 </li><li>每个输出调用限制在512字节内</li></ul>
<h4>WPP软件跟踪</h4>
<p>WPP软件提供了一种低开销的日志数据机制，它创建一个二进制数据日志文件存放信息，它的显著优点是可以从驱动收集最终用户不可理解的数据。 </p>
<p>使用WPP的最简单方法是在SOURCE文件中添加如下语句： </p>
<p>RUN_WPP=$(SOURCES) &#8211;km </p>
<p>此外，还需设置一些定义及包含文件，WPP的用法如下： </p>
<p>DoTraceMessage(IN TraceFlag, IN Format, ...) </p>
<p>我们也可以通过如下定义将WPP转变为调试打印： </p>
<p>#define WPP_DEBUG(args) DbgPrint args; </p>
<p>WPP支持一些DbgPrint没有提供的格式符宏。 </p>
<p>由于WPP目的为低开销，所以于调试打印的规则略有不同： </p>
<ul><li>无需将数字转换成有意义的字符串 </li><li>无需输出文件及行号数据 </li><li>&#8230;&#8230;</li></ul>
<h4>事件日志</h4>
<p>系统事件日志用来报告驱动运行过程中发生的事情，主要是发生的问题，每个日志项的大小限制为256字节，使用系统事件日志主要考虑以下四点： </p>
<ul><li>事件日志的大小有限</li></ul>
<p>系统日志的大小及覆盖规则有管理员来指定，驱动加入的每一个事件日志都应当有充分的理由，管理员并不关系驱动什么时候启动或者关闭，而仅关系驱动发生了什么问题 </p>
<ul><li>每个错误指定一个唯一的错误代码，每个事件仅包含错误，便于管理员查询 </li><li>不要全部使用一样的错误信息，对每个错误使用实际的错误信息 </li><li>注意消息分类文件的处理</li></ul>
<h4>性能监测</h4>
<p>一个好的驱动应该给用户提供性能信息，通常通过WMI来提供这些性能数据，通常我们可以提供如下数据来衡量驱动的性能： </p>
<ul><li>IRP计数 </li><li>处理数据计数 </li><li>出错率或者出错个数 </li><li>安全事件计数</li></ul>
<p>WMI的例子见src\wdm\wmi\wmifilt </p>
<h4>自定义信息转储</h4>
<p>驱动可以通过自定义信息转储来提高驱动的调试效率，在Win2000中使用<strong>KeRegisterBugCheckCallback</strong>实现自定义信息转储，在XP及以后的系统中使用<strong>KeRegisterBugCheckReasonCallback</strong>实现自定义信息转储。 </p>
<h4>版本块</h4>
<p>使用版本信息将有利于用户报告驱动的问题，在资源描述文件中，使用VERSIONINFO定义来描述驱动的版本信息。 </p>
<h3>测试工具</h3>
<p>测试的基本原则： </p>
<ul><li>易于复现 </li><li>易于确认测试结果 </li><li>使用不同的平台测试 </li><li>修复BUG后做回归测试</li></ul>
<h4>Device Path Exerciser</h4>
<p>Device Path Exerciser（dc2.exe，DDK自带）使用不同的设备控制I/O调用来检测驱动的稳定性。这个工具的调用格式如下： </p>
<p>dc2 [options] /dr driver [driver &#8230;] （XP and later, support to 10 drivers） </p>
<p>dc2 [options] device （all, support one dirver） </p>
<h5>日志部分</h5>
<p>dc2.exe共产生四个日志文件：DC2.log，Diags.log，CrashN.log，Crash.log </p>
<h5>打开设备部分</h5>
<p>dc2.exe试图使用多种手段来打开设备进行测试。 </p>
<h5>测试部分</h5>
<p>dc2.exe包括很多测试的内容，常用的如下： </p>
<ul><li>杂项测试（读/写/取消请求） </li><li>零长度缓冲区测试 </li><li>随机测试</li></ul>
<h4>PNP驱动测试</h4>
<p>PNP测试（pnpdtest.exe，DDK自带）主要包括如下部分： </p>
<ul><li>可移除性 </li><li>重新分配资源 </li><li>意外移除设备 </li><li>压力测试（包括5分钟的重新分配资源及移除测试，最后一个意外移除）</li></ul>
<h4>休眠及ACPI测试</h4>
<p>使用sleeper.exe（DDK自带）测试驱动的电源管理与系统的电源管理的配合情况，使用pmte.exe（DDK自带）测试驱动的电源管理功能。 </p>
<h4>硬件兼容性测试（HCT）</h4>
<p>HCT是一系列Windows Logo Program指定的必须通过的测试，它的主要目的是测试硬件，但有助于驱动开发，主要包括如下几类测试： </p>
<p>Audio </p>
<p>Bus Controllers </p>
<p>Display </p>
<p>Anti-Virus/File System Filter Drivers </p>
<p>Imaging </p>
<p>Input and HID </p>
<p>Modems </p>
<p>Network Devices </p>
<p>Storage Controllers and Devices </p>
<p>Streaming Media and Broadcast </p>
<p>Systems </p>
<p>Unclassified </p>
<h3>总结</h3>
<p>通常，驱动开发应该遵守以下规则： </p>
<ul><li>使用最新的DDK编译驱动 </li><li>在编译是发现问题</li></ul>
<p>使用/Wall, C++编译，使用PREfast，使用DEPRECATE_DDK_FUNCTIONS 及C_ASSERT宏，使用ChkINF检查INF文件 </p>
<ul><li>使用运行时检查及日志手段 </li><li>提供调试诊断信息来快速定位错误 </li><li>使用HCT及DDK中提供的工具进行测试，使用自定义的test case进行测试</li></ul></div><img src ="http://www.cppblog.com/Walker/aggbug/146849.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-21 09:16 <a href="http://www.cppblog.com/Walker/articles/146849.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>驱动测试工具PnPUtil </title><link>http://www.cppblog.com/Walker/articles/146844.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Fri, 20 May 2011 23:53:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146844.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146844.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146844.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146844.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146844.html</trackback:ping><description><![CDATA[<p>PnPUtil (<em>PnPUtil.exe</em>) is a command line tool that lets an administrator perform the following actions on <a href="http://msdn.microsoft.com/en-us/library/ff544840(v=VS.85).aspx"><font color="#1364c4">driver packages</font></a>:</p>
<ul><li>
<p>Adds a driver package to the <a href="http://msdn.microsoft.com/en-us/library/ff544868(v=VS.85).aspx"><font color="#1364c4">driver store</font></a>.</p></li><li>
<p>Deletes a driver package from the driver store. </p></li><li>
<p>Enumerates the driver packages that are currently in the driver store. Only driver packages that are not in-box packages are listed. An <em>in-box</em> driver package is one which is included in the default installation of Windows or its service packs.</p></li></ul>语法：&nbsp;<br />PnPUtil [/a [/i] InfFileName] [/d [/f] PublishedInfFileName] [/e] [/?] <br />例子：<br />C:\&gt;pnputil /a m:\MyDriver.inf<br />C:\&gt;pnputil /e<br />C:\&gt;pnputil /d oem22.inf<br /><br /><img src ="http://www.cppblog.com/Walker/aggbug/146844.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-21 07:53 <a href="http://www.cppblog.com/Walker/articles/146844.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】转一篇内核与IRQL的文章，写得很good</title><link>http://www.cppblog.com/Walker/articles/146723.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Wed, 18 May 2011 23:45:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146723.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146723.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146723.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146723.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146723.html</trackback:ping><description><![CDATA[通过IRQL看NT内核<br />linux强调的是进程自主性，windows则是对象自主性，其中线程本身也是一个对象，进程也是，所以一个进程可以操作另外一个进程的地址空间也就不足为奇了，windows的通信实际上是对象间通信，而linux因为一切围着进程转，最新的内核中断也被线程化了，因此通信就是进程间通信，linux 中进程作为超级容器的意义要比windows的更大些，windows中进程是一个容器，也是一个对象，某种意义上它作为容器的意义是容纳别的对象。 windows的模块化思想更加鲜明。<br />windows中将缺页，调度等概念从线程，进程中分离，专门安排一个irql级别来处理之，而linux下相应的概念则永远和进程相绑定，看看缺页中断的处理代码，考虑的一直都是进程的概念，在windows中，考虑的就是对象的概念了，比如文件对象等等，但是缺页本质上真的是和进程相关的，所以在 windows中没有进程/线程上下文的的执行绪当然就不能缺页了，那么谁没有进程/线程上下文呢（也就是ms说的&#8220;任意上下文&#8221;）？答案是 dispatch_level之上（包括dispatch）的没有进程/线程上下文，所以，相应级别的执行中就不能访问分页内存（可能被换出而导致缺页的内存）了。windows下进程/线程是和其他对象并列的概念，没有什么特殊之处，但是在linux（unix）中，它们却是终极重要的概念，贯穿整个系 统。windows为了实现完全异步和为了配合可移植性，将一切抽象成了&#8220;中断&#8221;，所以我们没有必要特别说什么上下文的概念，上下文只是一个进程/线程的 容器，没有又如何，代码照样执行，之所以强调什么时候要有上下文什么时候没有必要完全是为了配合进程/线程的，为了使进程/线程好管理。<br />我比较喜欢将一大堆的结果归结为一个原因，就像一切宏观运动都是牛顿定律的结果，一切微观运动都是量子力学的结果，一切宇观运动都是相对论的结果一样，从这一点上看，研究windows是一个好的选择。<br />我们看一下两个操作系统的缺页，先说linux。 <br />在linux中，发生缺页后执行缺页中断处理，当时的上下文还是引起缺页的进程线程上下文，除非当前在中断或软中断中，然后内核挂起当前线程，执行磁盘 io，将页面取回或者分配一个匿名页面，实际上这里挂起当前线程的意思是执行的缺页中断处理并不属于当前线程和在磁盘io或分配匿名页面的过程中可能要等 待，等待意味着线程切换，一直到线程实际切换，上下文一直是当前线程上下文。 在前面说的中断或软中断中出现缺页的话就不能随便进行线程挂起了，因为此时是linux唯一可能在任意上下文的情况，即使在内核发生缺页也没有关系，缺页 处理只认上下文，不管在什么态（因为内核数据结构常常在中断中被访问，所以，一般还是别在内核用分页内存了，所以linux又规定，内核数据结构从 slab或类似的连续区分配分配，并且常驻内存，你完全可以修改内核，在内核分配一个页面，映射到内核空间或用户空间，然后修改缺页处理程序，使得可以处理映射到内核导致的缺页，最后保证你永远不在中断中访问这个地址，很麻烦，但可能！），所以linux硬性规定：中断不能引起缺页（原因仅仅一个，上下文不确定）。（附：本质意义上，内核空间并不属于任何进程的地址空间，只是一个所有进程和资源的管理空间，所以内核空间大家公用，所以linux很是巧妙的将物理内存线性映射到内核地址空间，内核数据结构大部分在此分配，一旦满足不了要求，可以从vmalloc区域分配，并且只更新0号进程页表，由缺页中断 来解决别的进程页表和0号进程页表同步问题，所以linux内核空间地址的中断并不需要真正分配内存页面，只需要修改个页表就行。linux内核空间地址不许缺页并没有实质性的规定，只是为了内核开发的方便，所以linux仅仅解决用户空间的缺页和内核vmalloc页表缺页。）回到前面正常情况，在有上 下文的情况下进行磁盘io或分配匿名页以及可能引起的当前线程睡眠切换有问题吗？一点问题也没有，因为linux/unix尽量使所有执行绪都有特定上下 文，不允许的操作作为稀有的特例已经被硬性明文禁止了，这样的弊端在于，留给程序员的只有好好把这个原理研究透或者把那些规定背熟了。在windows中 的情况呢？上下文没有那么特殊了，一切都是异步的中断，很多执行绪没有特定上下文，那么发生缺页后呢？可能缺页发生在用户线程，而用户线程运行在被动中断请求级别，这是没有任何问题的，所有别的级别的执行绪都可以中断它，最重要的，io开始例程在dispatch级别运行，缺页处理运行在 dispatch，调度也运行在dispatch。首先，为何调度在dispatch运行呢？因为在dispatch或之上的执行绪就是任意上下文环境 了，它们是不允许发生线程切换的，这怎么保证呢？如果在dirql级别的要切换，那么就要将irql提升到dispatch，因为当前dirql比 dispatch高，于是蓝乎，如果在dispatch的dpc要调度，那么它要提升irql到dispatch，因为它已经在了，于是亦蓝乎，这就是原 因，调度运行在dispatch实际上是为了保证没有上下文的执行绪不能发生线程切换（类比linux，linux中也是没有上下文的不能切换）；那么 io开始为何也在dispatch呢？这是因为windows是异步的，一个线程发起一个io不一定在当前上下文就可以执行，可能在一个硬件中断发生后有 了执行的条件（考虑irp排队），然后此中断派发一个dpc开始io动作，中断没有确定上下文，它派发的dpc同样没有上下文，所以io开始只能在没有上 下文的dispatch执行；io分派例程比如read，write之类的必须在有上下文的被动级别运行，因为它们可能要睡眠，而且不能打扰io开始和完成例程；现在考虑缺页，看看它能在哪里执行，咱们从低irql到高考虑，我们假设被动irql也参与到中断模拟，如果缺页处理运行在最低的被动irql， 那么就完了，试想一个同样在被动irql的线程发生缺页，缺页运行前必先提升irql到被动级别，但是不巧，已经是被动级别了，出错，返回，蓝！但是事实上，被动级别并不参加中断模拟，这说明缺页处理运行在被动级别是可以的，但是考虑apc级别，它可使参与中断模拟了，apc比被动级别高，于是如果缺页处 理在被动级别，那么apc就不能缺页了，这说明apc不能用分页内存，这很影响大家伙的信心，windows怎么能这么限制程序员呢；那么我们就将缺页处理运行级别往上提高一级，到apc级别，还是不行，apc还是不能缺页，因为它就是在apc级别的，虚拟中断只能被irql比它高的中断掉；于是再往上 走，到dispatch吧，这下好了，apc可以使用分页内存了，但是提升到这个级别仅仅为了让apc使用分页内存，如果又引起别的问题，那么还是别让 apc使用分页内存了。考虑缺页需要哪些操作，无非就是分配内存页面，或者从磁盘读取页文件，考虑后者，肯定要开始一个磁盘io过程，而磁盘io就是在 dispatch进行的，这可以，考虑前者，分配页面可能导致线程睡眠，现在缺页正在dispatch运行，想挂起一个线程也没有问题，正好调度也是在这个级别运行，仍然没有问题，现在我们可以说缺页处理可以在这个级别了，但是，人是贪心的，一些限制总是使人能有所创新，只可惜，这次的创新在 windows完美主义下失败了，我们不甘心dispatch以及之上的内存不能使用分页内存，于是再往上提一级，提到哪级姑且不考虑，如果 dispatch这种没有上下文的级别产生缺页就有可能睡眠或请页，但是io开始和调度都是确定性的在下面的级别运行，于是不能再往上了，就这样了，缺页中断处理程序只能在dispatch级别运行。<br />这下有意思吧，windows的大框架已经定死，不需要硬性规定，一两条原则就可以决定这么多事情：1.中断虚拟化的irql机制使windows成为完全异步os内核；2.对象化模块化使windows不过分依赖于进程/线程的概念，使得1的实现简单化。网上很多人比如赫赫有名的毛德操教授说 windows的进程可以操作别的进程空间是windows不安全的原因之一，我认为这句话没错，但是毛教授似乎没有理解windows为何这么做，它不安全是因为细节没有把握好，而大的框架我很欣赏，也很坚信，没有错！linux的软中断和任务队列就是windows的借鉴，只可惜，linux是善变 的，发现工作队列后毅然抛弃了任务队列。<br />linux更像是一件艺术作品而windows更像一件工业产品。举个例子：一个高大的全钢架结构建筑矗立在广场，看似永远都没有完过工，巨大的钢架抬头 可见，耗资巨大，周围只有钢架，没有封顶，人在里面难免风吹雨淋，人们会在底楼租一间盖好的屋子，但难免还会被漏下来的建筑石料将屋顶砸破，头破血流，目前开发商大势已去，真不知能否完工，但是框架结构绝对一流，缺的不是技术，而是激情；在郊外的草地上，几个满怀激情的人在自己修建一座别墅，没有开发商， 没有人观望，只有他们自己，别墅异常豪华，没有什么框架，因为不需要，他们有技术，有激情，缺的是大家的支持。前者是windows，后者是linux， 而unix可能就是金字塔吧。<br />现在的硬件也在提供更多的功能，从而使一些软件策略更加容易实现，比如apic就实现了软中断功能，这就更加使得windows的irql如鱼得水了，我认为linux马上也将用最新的apic软中断功能来实现softirq，这一向是linux的作风：绝不落伍！<br />感叹：现在的硬件也在越来越快的发展了，快赶超软件发展速度了，看，什么都在封装，以前用引脚传递硬件信息，现在用消息了（看看pci和pcie吧），硬 件和软件都在两极分化，一帮人搞的东西越来越简单，越傻瓜，另一帮人却越来越高深，越深不见底！呜呼，我，何去何从？！<br /><br /><br /><img src ="http://www.cppblog.com/Walker/aggbug/146723.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-19 07:45 <a href="http://www.cppblog.com/Walker/articles/146723.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>IRP的CancelRoutine （转）</title><link>http://www.cppblog.com/Walker/articles/146722.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Wed, 18 May 2011 23:36:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146722.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146722.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146722.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146722.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146722.html</trackback:ping><description><![CDATA[正确的退出要求线程从wait函数返回后执行CancelIo操作取消掉你的IRP，这需要驱动配合在IRP里设置CancelRoutine。假如没有CancelRoutine，那么CancelIo操作是失败的，前面讲的那些恐怖故事还是会发生。关于这一点，我打算下次再说。<br /><br />当一个IRP的CancelRoutine没有被设置时，CancelIo操作会失败，系统中有可能会留下永远都不会被complete的IRP。在Threaded IRP和non-threaded IRP一节中我们有谈到irp分为线程相关和非线程相关两种。倘若一个永远不complete的irp是非线程相关的，情况会稍微好一点，顶多系统中泄露了一个资源。倘若该irp是线程相关的，那事情就大了。thread IRP由IoManager生成并保留在线程的IRP队列里，负责处理该IRP的驱动在收到下层驱动的Complete事件后不会主动收回IRP的资源而是继续complete给IoManager，由IoManager负责回收，并从线程IRP列表中删除该IRP。一个线程在退出前会遍历等待IRP队列里所有的IRP，直到它们全部被complete为止。倘若其中有一个irp永远不complete，那么线程就永远不退出，无论是ExitThread也好还是_endthreadex也好还是什么邪恶的暴力擦除数据强退也好，全都不顶用。线程不退出，进程也不能销毁(题外话：进程资源的回收动作由最后一个线程退出后发起，所谓的杀进程，其实是用apc给所有线程发起退出操作）。更糟糕的是，操作系统的关机过程都会被堵住，除了关电源，没有其他办法恢复，这一点简直比BSOD还糟糕。我们知道由user mode发起的IO操作最后都会翻译成threaded irp，这就是为什么我在7.1大谈特谈user mode线程的原因：这个陷阱连user mode程序也会掉进去。Bad dog!<br />要解决这一点方法很简单目标很明确，那就是防止&#8220;永远不complete的irp&#8221;这种东西出现。一般的做法是加个线程或者timer并设置超时时间，时间一到就cancel这个irp。如果irp由user mode程序发起，那么就调用CancelIo；如果irp由驱动发起，则是调用IoCancelIrp。所有这些动作要生效的大前提是你的irp有CancelRoutine的存在，否则一切都是白搭。所以这里我有个经验要跟大家分享：<span style="color: #0000ff">任何时候都给你的irp设置CancelRoutine，并在CancelRoutine里Complete你的IRP!</span>为方便起见我们选non-threaded irp做个例子，所有的代码都在内核态，免得各位看官看示例代码还要做上下文切换。<br /><br /><br />
<table cellspacing="0" cellpadding="5" width="100%">
<tbody>
<tr bgcolor="#ffffff">
<td class="tpc_content" colspan="2">通过实验,我现在已基本搞清了原因. 与大家交流 <br /><br />1.普通WIN32用户态程序与驱动程序交互: <br />(1).当用户程序打开设备时, IO管理器向驱动程序发IRP_MJ_CREATE. <br />(2) 用户程序退出时,不论此时是否有未完成的IRP, IO管理器都会首先向驱动发IRP_MJ_CLEANUP. <br />(3) 如果在发送了IRP_MJ_CLEANUP之后,仍有未完成的IRP(例如驱动程序未提供CleanUp例程或在CleanUp例程中未清除所有未完成的IRP,就会造成这种情况), 则IO管理器会依次调用这些IRP的OnCancel例程(如果驱动程序既未提供CleanUp例程又未挂接Cancel例程就糟了,这些IRP将无法得到清除).最后,IO管理器向驱动发IRP_MJ_CLOSE. <br /><br />2.上层驱动程序与下层驱动程序的交互. <br />(1).当上层驱动调用IoGetDeviceObjectPointer()时,该函数会向下层驱动发送IRP_MJ_CREATE和IRP_MJ_CLEANUP. <br />(2).当上层驱动调用ObDereferenceObject()时,该函数只会向下层驱动发送IRP_MJ_CLOSE. <br /><br />3.故障原因: <br />我的上层驱动在Unload例程中调用了ObDereferenceObject(), 但是由于仍有未完成的IRP,因此即使我的上层驱动卸载了,但这些IRP仍然留在下层驱动程序的队列中未得到清除,以后当下层驱动一旦去完成这些IRP时就会造成BUG_CHECK, 因为IRP中的FileObject域指的文件对象已不存在. <br /><br /><br /><br /></td></tr></tbody></table><br />
<table class="i_table" cellspacing="1" cellpadding="1" width="960" align="center">
<tbody>
<tr>
<td>
<table cellspacing="0" cellpadding="5" width="100%">
<tbody>
<tr class="head">
<td><strong>dazzy</strong></td>
<td class="smalltxt" align="right">2001-07-27 08:45</td></tr>
<tr bgcolor="#ffffff">
<td class="tpc_content" colspan="2">分析透彻！一般的驱动都要注册并实现CLEANUP的dispatch. <br /></td></tr></tbody></table></td></tr></tbody></table><img src ="http://www.cppblog.com/Walker/aggbug/146722.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-19 07:36 <a href="http://www.cppblog.com/Walker/articles/146722.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>驱动程序与应用程序的接口</title><link>http://www.cppblog.com/Walker/articles/146721.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Wed, 18 May 2011 23:26:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146721.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146721.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146721.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146721.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146721.html</trackback:ping><description><![CDATA[<h1 class="block_title"><a id="ctl01_TitleUrl" href="http://www.cnblogs.com/suiyingjie/archive/2007/10/19/930680.html"><font color="#ff6600">WDM驱动程序与应用程序的接口</font></a></h1>
<div class="post">
<div class="postcontent">
<div id="cnblogs_post_body">
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">有两种方式可以让设备和应用程序之间联系：</span></p>
<p style="text-indent: -21pt; margin-left: 42.75pt; tab-stops: list 42.75pt">1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">通过为设备创建的一个符号链；</span></p>
<p style="text-indent: -21pt; margin-left: 42.75pt; tab-stops: list 42.75pt">2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-family: 宋体">通过输出到一个接口</span></p>
<p style="text-indent: 21.75pt">WDM<span style="font-family: 宋体">驱动程序建议使用输出到一个接口而不推荐使用创建符号链的方法。这个接口保证</span>PDO<span style="font-family: 宋体">的安全，也保证安全地创建一个惟一的、独立于语言的访问设备的方法。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">一个应用程序使用</span>Win32APIs<span style="font-family: 宋体">来调用设备。在某个</span>Win32 APIs<span style="font-family: 宋体">和设备对象的分发函数之间存在一个映射关系。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">获得对设备对象访问的第一步就是打开一个设备对象的句柄。</span></p>
<p><strong><span style="font-family: 黑体; color: navy; font-size: 12pt">用符号链打开一个设备的句柄</span></strong></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">为了打开一个设备，应用程序需要使用</span><strong>CreateFile<span style="font-family: 宋体">。</span></strong><span style="font-family: 宋体">如果该设备有一个符号链出口，应用程序可以用下面这个例子的形式打开句柄：</span></p>
<p style="text-indent: 0.75pt"><span style="font-family: 宋体">hDevice = CreateFile(<em>""""".""OMNIPORT3"</em>,<br />&nbsp;&nbsp;GENERIC_READ | GENERIC_WRITE,FILE_SHARE_READ,<br />&nbsp;&nbsp;NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL ,NULL<br />);</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">文件路径名的前缀&#8220;</span>""."<span style="font-family: 宋体">&#8221;告诉系统本调用希望打开一个设备。这个设备必须有一个符号链，以便应用程序能够打开它。有关细节查看有关</span>Kdevice<span style="font-family: 宋体">和</span>CreateLink<span style="font-family: 宋体">的内容。在上述调用中第一个参数中前缀后的部分就是这个符号链的名字。</span></p>
<p style="text-indent: 21.75pt"><strong><em><span style="font-family: 宋体">注意：</span>CreatFile<span style="font-family: 宋体">中的第一个参数不是</span>Windows 98/2000<span style="font-family: 宋体">中驱动程序</span>(.sys<span style="font-family: 宋体">文件</span>)<span style="font-family: 宋体">的路径。是到设备对象的符号链。</span></em></strong></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">如果使用</span>DriverWizard<span style="font-family: 宋体">产生驱动程序，它通常使用类</span>KunitizedName<span style="font-family: 宋体">来构成设备的符号链。这意味着符号链名有一个附加的数字，通常是</span>0<span style="font-family: 宋体">。例如：如果链接名称的主干是</span>L<span style="font-family: 宋体">&#8220;</span>TestDevice<span style="font-family: 宋体">&#8221;那么在</span>CreateFile<span style="font-family: 宋体">中的串就该是&#8220;</span>"""".""TestDevice0<span style="font-family: 宋体">&#8221;。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">如果应用程序需要被覆盖的</span>I/O<span style="font-family: 宋体">，第六个参数</span>(Flags)<span style="font-family: 宋体">必须或上</span>FILE_FLAG_OVERLAPPED<span style="font-family: 宋体">。</span></p>
<p><span style="font-family: 黑体; color: navy; font-size: 12pt">使用一个输出接口打开句柄</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">用这种方式打开一个句柄会稍微麻烦一些。</span>DriverWorks<span style="font-family: 宋体">库提供两个助手类来使获得对该接口的访问容易一些，这两个类是</span>CDeviceInterface, <span style="font-family: 宋体">和</span> CdeviceInterfaceClass<span style="font-family: 宋体">。</span></p>
<p style="text-indent: 21pt">CdeviceInterfaceClass<span style="font-family: 宋体">类封装了一个设备信息集，该信息集包含了特殊类中的所有设备接口信息。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">应用程序能有用</span>CdeviceInterfaceClass<span style="font-family: 宋体">类的一个实例来获得一个或更多的</span>CdeviceInterface<span style="font-family: 宋体">类的实例。</span>CdeviceInterface<span style="font-family: 宋体">类是一个单一设备接口的抽象。它的成员函数</span>DevicePath()<span style="font-family: 宋体">返回一个路径名的指针，该指针可以在</span>CreateFile<span style="font-family: 宋体">中使用来打开设备。</span></p>
<p style="text-indent: 21pt"><span style="font-family: 宋体">下面用一个小例子来显示这些类最基本的使用方法：</span></p>
<p>extern GUID TestGuid;<br />HANDLE OpenByInterface(<br />&nbsp;&nbsp;GUID* pClassGuid,<br />&nbsp;&nbsp;DWORD instance,<br />&nbsp;&nbsp;PDWORD pError<br />)<br />{<br />&nbsp;&nbsp;CDeviceInterfaceClass DevClass(pClassGuid, pError);<br />&nbsp;&nbsp;if (*pError != ERROR_SUCCESS)<br />&nbsp;&nbsp;&nbsp;&nbsp;return INVALID_HANDLE_VALUE;<br />&nbsp;&nbsp;CDeviceInterface DevInterface(&amp;DevClass, instance, pError);<br />&nbsp;&nbsp;if (*pError != ERROR_SUCCESS)<br />&nbsp;&nbsp;&nbsp;&nbsp;return INVALID_HANDLE_VALUE;<br />&nbsp;&nbsp;cout &lt;&lt; "The device path is "<br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;&lt; DevInterface.DevicePath()<br />&nbsp;&nbsp;&nbsp;&nbsp;&lt;&lt; endl;<br /><br />&nbsp;&nbsp;HANDLE hDev;<br />&nbsp;&nbsp;hDev = CreateFile(<br />&nbsp;&nbsp;&nbsp;DevInterface.DevicePath(),<br />&nbsp;&nbsp;&nbsp;&nbsp;GENERIC_READ | GENERIC_WRITE,<br />&nbsp;&nbsp;&nbsp;&nbsp;FILE_SHARE_READ | FILE_SHARE_WRITE,<br />&nbsp;&nbsp;&nbsp;&nbsp;NULL,<br />&nbsp;&nbsp;&nbsp;&nbsp;OPEN_EXISTING,<br />&nbsp;&nbsp;&nbsp;&nbsp;FILE_ATTRIBUTE_NORMAL,<br />&nbsp;&nbsp;&nbsp;&nbsp;NULL<br />&nbsp;&nbsp;);<br />&nbsp;&nbsp;if (hDev == INVALID_HANDLE_VALUE)<br />&nbsp;&nbsp;&nbsp;&nbsp;*pError = GetLastError();<br />&nbsp;&nbsp;return hDev;<br />} </p>
<p><span style="font-family: 黑体; color: navy; font-size: 12pt">在设备中执行</span><span style="color: navy; font-size: 12pt">I/O</span><span style="font-family: 黑体; color: navy; font-size: 12pt">操作</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">一旦应用程序获得一个有效的设备句柄，它就能使用</span>Win32 APIs<span style="font-family: 宋体">来产生到设备对象的</span>IRPs<span style="font-family: 宋体">。下面的表显示了这种对应关系。</span></p>
<table border="1" cellspacing="1" cellpadding="0">
<tbody>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt"><strong>Win32 API</strong></p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt"><strong>DRIVER_FUNCTION_<em>xxx</em><br />IRP_MJ_<em>xxx</em></strong></p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt"><strong>KDevice subclass member function</strong></p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">CreateFile </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">CREATE </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">Create </p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">ReadFile </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">READ </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">Read </p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">WriteFile </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">WRITE </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">Write </p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">DeviceIoControl </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">DEVICE_CONTROL </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">DeviceControl </p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">CloseHandle </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">CLOSE<br />CLEANUP </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">Close<br />CleanUp </p></td></tr></tbody></table>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">需要解释一下设备类成员的</span>Close<span style="font-family: 宋体">和</span>CleanUp<span style="font-family: 宋体">：</span>CreateFile<span style="font-family: 宋体">使内核为设备创建一个新的文件对象。这使得多个句柄可以映射同一个文件对象。当这个文件对象的最后一个用户级句柄被撤销后，</span>I/O<span style="font-family: 宋体">管理器调用</span>CleanUp<span style="font-family: 宋体">。当没有任何用户级和核心级的对文件对象的访问的时候，</span>I/O<span style="font-family: 宋体">管理器调用</span>Close<span style="font-family: 宋体">。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">如果被打开的设备不支持指定的功能，则调用相应的</span>Win32<span style="font-family: 宋体">将引起错误（无效功能）。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">以前为</span>Windows95<span style="font-family: 宋体">编写的</span>VxD<span style="font-family: 宋体">的应用程序代码中可能会在打开设备的时候使用</span>FILE_FLAG_DELETE_ON_CLOSE<span style="font-family: 宋体">属性。在</span>Windows NT/2000<span style="font-family: 宋体">中，建议不要使用这个属性，因为它将导致没有特权的用户企图打开这个设备，这是不可能成功的。</span></p>
<p style="text-indent: 21.75pt">I/O<span style="font-family: 宋体">管理器将</span>ReadFile<span style="font-family: 宋体">和</span>WriteFile<span style="font-family: 宋体">的</span>buff<span style="font-family: 宋体">参数转换成</span>IRP<span style="font-family: 宋体">域的方法依赖于设备对象的属性。当设备设置</span>DO_DIRECT_IO<span style="font-family: 宋体">标志，</span>I/O<span style="font-family: 宋体">管理器将</span>buff<span style="font-family: 宋体">锁住在存储器中，并且创建了一个存储在</span>IRP<span style="font-family: 宋体">中的</span>MDL<span style="font-family: 宋体">域。一个设备可以通过调用</span>Kirp::Mdl<span style="font-family: 宋体">来存取</span>MDL<span style="font-family: 宋体">。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">当设备设置</span>DO_BUFFERED_IO<span style="font-family: 宋体">标志，设备对象分别通过</span>KIrp::BufferedReadDest<span style="font-family: 宋体">或</span> KIrp::BufferedWriteSource<span style="font-family: 宋体">为读或写操作获得</span>buff<span style="font-family: 宋体">地址。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">当设备不设置</span>DO_BUFFERED_IO<span style="font-family: 宋体">标志也不设置</span>DO_DIRECT_IO<span style="font-family: 宋体">，内核设置</span>IRP <span style="font-family: 宋体">的</span>UserBuffer<span style="font-family: 宋体">域来对应</span>ReadFile<span style="font-family: 宋体">或</span>WriteFile<span style="font-family: 宋体">中的</span>buff<span style="font-family: 宋体">参数。然而，存储区并没有被锁住而且地址只对调用进程有效。驱动程序可以使用</span>KIrp::UserBuffer<span style="font-family: 宋体">来存取</span>IRP<span style="font-family: 宋体">域。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">对于</span>DeviceIoControl<span style="font-family: 宋体">调用，</span>buffer<span style="font-family: 宋体">参数的转换依赖于特殊的</span>I/O<span style="font-family: 宋体">控制代码，它不在设备对象的特性中。宏</span>CTL_CODE<span style="font-family: 宋体">（在</span>winioctl.h<span style="font-family: 宋体">中定义）用来构造控制代码。这个宏的其中一个参数指明缓冲方法是</span>METHOD_BUFFERED, METHOD_IN_DIRECT, METHOD_OUT_DIRECT, <span style="font-family: 宋体">或</span>METHOD_NEITHER<span style="font-family: 宋体">。下面的表显示了这些方法和与之对应的能获得输入缓冲与输出缓冲的</span>KIrp<span style="font-family: 宋体">中的成员函数：</span></p>
<table border="1" cellspacing="1" cellpadding="0">
<tbody>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt"><strong>Method</strong></p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt"><strong>Input Buffer Parameter</strong></p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt"><strong>Output Buffer Parameter</strong></p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">METHOD_BUFFERED </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">KIrp::IoctlBuffer </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">KIrp::IoctlBuffer </p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">METHOD_IN_DIRECT </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">KIrp::IoctlBuffer </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">KIrp::Mdl </p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">METHOD_OUT_DIRECT </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">KIrp::IoctlBuffer </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">KIrp::Mdl </p></td></tr>
<tr>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">METHOD_NEITHER </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">KIrp::IoctlType3InputBuffer </p></td>
<td style="padding-bottom: 1.5pt; padding-left: 1.5pt; padding-right: 1.5pt; padding-top: 1.5pt" valign="top">
<p style="margin-bottom: 0pt">KIrp::UserBuffer </p></td></tr></tbody></table>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">如果控制代码指明</span>METHOD_BUFFERED<span style="font-family: 宋体">，系统分配一个单一的缓冲来作为输入与输出。驱动程序必须在向输出缓冲放数据之前拷贝输入数据。驱动程序通过调用</span>KIrp::IoctlBuffer<span style="font-family: 宋体">获得缓冲地址。在完成时，</span>I/O<span style="font-family: 宋体">管理器从系统缓冲拷贝数据到提供给</span>Ring 3<span style="font-family: 宋体">级调用者使用的缓冲中。驱动程序必须在结束前存储拷贝到</span>IRP<span style="font-family: 宋体">的</span>Information<span style="font-family: 宋体">成员中的数据个数。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">如果控制代码不指明</span>METHOD_IN_DIRECT<span style="font-family: 宋体">或</span>METHOD_OUT_DIRECT<span style="font-family: 宋体">，则</span>DeviceIoControl<span style="font-family: 宋体">的参数呈现不同的含义。参数</span>InputBuffer<span style="font-family: 宋体">被拷贝到一个系统缓冲，这个缓冲驱动程序可以通过调用</span>KIrp::IoctlBuffer<span style="font-family: 宋体">。参数</span>OutputBuffer<span style="font-family: 宋体">被映射到</span>KMemory<span style="font-family: 宋体">对象，驱动程序对这个对象的访问通过调用</span>KIrp::Mdl<span style="font-family: 宋体">来实现。对于</span>METHOD_OUT_DIRECT<span style="font-family: 宋体">，调用者必须有对缓冲的写访问权限。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">注意，对</span>METHOD_NEITHER<span style="font-family: 宋体">，内核只提供虚拟地址；它不会做映射来配置缓冲。虚拟地址只对调用进程有效。</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">这里是一个用</span>METHOD_BUFFERED<span style="font-family: 宋体">的例子：</span></p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">首先，使用宏</span>CTL_CODE<span style="font-family: 宋体">来定义一个</span>IOCTL<span style="font-family: 宋体">代码：</span></p>
<p style="text-indent: 21.75pt">#define IOCTL_MYDEV_GET_FIRMWARE_REV "</p>
<p style="text-indent: 21.75pt">CTL_CODE (FILE_DEVICE_UNKNOWN,0,METHOD_BUFFERED,FILE_ANY_ACCESS)</p>
<p style="text-indent: 21.75pt"><span style="font-family: 宋体">现在使用一个</span>DeviceIoControl<span style="font-family: 宋体">调用：</span></p>
<p>BOOLEAN b;<br />CHAR FirmwareRev[60];<br />ULONG FirmwareRevSize;<br />b = DeviceIoControl(hDevice, IOCTL_MYDEV_GET_VERSION_STRING,<br />&nbsp;&nbsp;NULL, // no input&nbsp;<span style="font-family: 宋体">注意，这里放的是包含有执行操作命令的字符串指针</span><br />&nbsp;&nbsp;0, </p>
<p>FirmwareRev, &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//<span style="font-family: 宋体">这里是</span>output<span style="font-family: 宋体">串指针，存放从驱动程序中返回的字符串。</span></p>
<p>sizeof(FirmwareRev),&amp; FirmwareRevSize,<br />&nbsp;&nbsp;NULL // not overlapped I/O<br />&nbsp;);</p>
<p><span style="font-family: 宋体">如果输出缓冲足够大，设备拷贝串到里面并将拷贝的资结束设置到</span>FirmwareRevSize<span style="font-family: 宋体">中。</span></p>
<p><span style="font-family: 宋体">在驱动程序中，代码看起来如下所示：</span></p>
<p>const char* FIRMWARE_REV = "FW 16.33 v5";<br />NTSTATUS MyDevice::DeviceControl( KIrp I )<br />{<br />&nbsp;&nbsp;ULONG fwLength=0;<br />&nbsp;&nbsp;switch ( I.IoctlCode() )<br />&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;case IOCTL_MYDEV_GET_FIRMWARE_REV:<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fwLength = strlen(FIRMWARE_REV)+1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if (I.IoctlOutputBufferSize() &gt;= fwLength)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;strcpy((PCHAR)I.IoctlBuffer(),FIRMWARE_REV);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;I.Information() = fwLength;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return I.Complete(STATUS_SUCCESS);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;else</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br />&nbsp;&nbsp;&nbsp;&nbsp;case . . .<br />&nbsp;&nbsp;&nbsp;}<br />&nbsp;} </p></div></div></div><img src ="http://www.cppblog.com/Walker/aggbug/146721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-19 07:26 <a href="http://www.cppblog.com/Walker/articles/146721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WDFREQUEST</title><link>http://www.cppblog.com/Walker/articles/146686.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Wed, 18 May 2011 11:11:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146686.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146686.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146686.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146686.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146686.html</trackback:ping><description><![CDATA[<div>1 Request 进入队列之后是不能complte的，必须从Queue当中取出再Complete</div><img src ="http://www.cppblog.com/Walker/aggbug/146686.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-18 19:11 <a href="http://www.cppblog.com/Walker/articles/146686.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>windbg</title><link>http://www.cppblog.com/Walker/articles/146523.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Mon, 16 May 2011 13:47:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146523.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146523.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146523.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146523.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146523.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 不要再假装自己写的程序没bug了，不可能的，debug工具你早晚得用上。最常见的debug工具非printf（windows上用OutputDebugString函数）莫属，简单方便易学易用，但局限性也是显而易见的，首先它对debugee的影响很大，某些race condition的bug你要多加几个log它就重现不出来了，然后你把log去了发布给客户，结果又成了必现的bug，这种烂事咱们都碰到过...&nbsp;&nbsp;<a href='http://www.cppblog.com/Walker/articles/146523.html'>阅读全文</a><img src ="http://www.cppblog.com/Walker/aggbug/146523.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-16 21:47 <a href="http://www.cppblog.com/Walker/articles/146523.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>inf文件分析</title><link>http://www.cppblog.com/Walker/articles/146269.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Thu, 12 May 2011 10:54:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/146269.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/146269.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/146269.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/146269.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/146269.html</trackback:ping><description><![CDATA[&nbsp;
<p><span>;/*++</span></p>
<p><span>;</span></p>
<p><span>;Copyright (c) 1990-1999 Microsoft Corporation All rights Reserved</span></p>
<p><span>;</span></p>
<p><span>;Module Name:</span></p>
<p><span>;</span></p>
<p><span>;<span>&nbsp;&nbsp;&nbsp; </span>STATBUS.INF</span></p>
<p><span>;</span></p>
<p><span>;Abstract:</span></p>
<p><span>;<span>&nbsp;&nbsp;&nbsp; </span>INF file for installing toaster bus enumerator driver</span></p>
<p><span>;</span></p>
<p><span>;Installation Notes:</span></p>
<p><span>;<span>&nbsp;&nbsp;&nbsp; </span>Using Devcon: Type "devcon install statbus.inf root\statbus" to install</span></p>
<p><span>;</span></p>
<p><span>;--*/</span></p>
<p>&nbsp;</p>
<p><span>;Version </span><span>节出现在</span><span>inf</span><span>中的第一个位置，每个</span><span>inf</span><span>文件都必须有。</span></p>
<p><span>[Version]</span></p>
<p><span>;</span><span>指明那个系统上这个</span><span>inf</span><span>是有效的</span></p>
<p><span>Signature="$WINDOWS NT$"</span></p>
<p>&nbsp;</p>
<p><span>;</span><span>标准设备类的名字，在</span><span>devguid.h</span><span>中有定义，最好同时提供</span><span>ClassGuid</span><span>，方便系统查找。</span></p>
<p><span>如果定义新的设备类</span><span>,</span><span>那么必须使用新的名字</span><span>,</span><span>和</span><span>GUID.</span></p>
<p><span>Class=System</span></p>
<p><span>; </span><span>系统类的</span><span>GUID</span></p>
<p><span>ClassGuid={4D36E97D-E325-11CE-BFC1-08002BE10318}</span></p>
<p>&nbsp;</p>
<p><span>;</span><span>指定</span><span>inf</span><span>文件的提供者</span></p>
<p>&nbsp;</p>
<p><span>Provider=%MSFT%</span></p>
<p><span>;</span><span>驱动版本信息</span></p>
<p><span>DriverVer=<st1:chsdate w:st="on" Year="2011" Month="5" Day="10" IsLunarDate="False" IsROCDate="False">05/10/2011</st1:chsdate>,6.1.7600.16385</span></p>
<p><span>指定驱动程序包含的在安装包中的</span><span>catalog</span><span>文件。这些</span> <span>文件由</span><span>WQHL</span><span>提供。</span></p>
<p><span>CatalogFile=KmdfSamples.cat</span></p>
<p>&nbsp;</p>
<p><span>；指定了驱动文件将被拷贝到哪里</span></p>
<table border=0 cellPadding=0 width="90%">
    <tbody>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>01</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><em><span>SourceDrive</span></em><strong><span>:\</span></strong><em><span>pathname</span></em><span> (the directory from which the INF file was installed)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>10</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Windows directory<br>This is equivalent to <em>%windir%</em>. </span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>11</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>System directory <br>This is equivalent to <em>%windir%</em><strong>\</strong><em>system32</em> for NT-based systems, and to <em>%windir%</em><strong>\</strong><em>system</em> for Windows 9x/Me.</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>12</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Drivers directory<br>This is equivalent to <em>%windir%</em><strong>\</strong><em>system32</em><strong>\</strong><em>drivers</em> for NT-based platforms, and to <em>%windir%</em><strong>\</strong><em>system</em><strong>\</strong><em>IoSubsys</em> on Windows 9x/Me platforms. </span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>17</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>INF file directory</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>18</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Help directory</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>20</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Fonts directory</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>21</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Viewers directory</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>23</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Color directory (ICM) (<em>not</em> used for installing printer drivers)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>24</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Root directory of the system disk.<br>This is the root directory of the disk on which Windows files are installed. For example, if <em>dirid</em> 10 is "<em>C:\winnt</em>", then <em>dirid</em> 24 is "<em>C:\</em>".</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>25</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Shared directory</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>30</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Root directory of the boot disk, also known as "ARC system partition," for NT-based systems. (This might or might not be the same directory as the one represented by <em>dirid</em> 24.)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>50</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>System directory for NT-based operating systems<br><br>This is equivalent to <em>%windir%</em><strong>\</strong><em>system</em> (NT-based systems only). </span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>51</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Spool directory (<em>not</em> used for installing printer drivers &#8722; see Printer <em>Dirids</em>)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>52</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Spool drivers directory (<em>not</em> used for installing printer drivers)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>53</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>User profile directory</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>54</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Directory where <em>ntldr.exe</em> and <em>osloader.exe</em> are located (NT-based systems only) </span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>55</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Print processors directory (<em>not</em> used for installing printer drivers)</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top>
            <p align=left><strong><span>-1</span></strong></p>
            </td>
            <td vAlign=top>
            <p align=left><span>Absolute path</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&nbsp;</p>
<p><span>[DestinationDirs]</span></p>
<p><span>DefaultDestDir = 12</span></p>
<p>&nbsp;</p>
<p><span>；指定使用什么分发媒体。</span></p>
<p><span>[SourceDisksNames]</span></p>
<p><em><span>diskid</span></em><span> <strong>=</strong> <em>disk-description</em>[<strong>,</strong>[<em>tag-or-cab-file</em>]<strong>,</strong>[<em>unused</em><strong>,</strong><em>path</em>]<strong>,</strong>[<em>flags</em>][<strong>,</strong><em>tag-file</em>]] <strong><em>Windows XP and later</em></strong></span></p>
<p><strong><em><span>如果有多个地方，需要指定</span></em></strong><strong><em><span>1</span></em></strong><strong><em><span>，</span></em></strong><strong><em><span>2</span></em></strong><strong><em><span>，</span></em></strong><strong><em><span>3</span></em></strong><strong><em><span>，</span></em></strong><strong><em><span>4</span></em></strong><strong><em><span>，。。。</span></em></strong></p>
<p><span>1 = %DiskId1%,,,""</span></p>
<p>&nbsp;</p>
<p><span>；指定文件在那个分发媒体上。</span></p>
<p><span>[SourceDisksFiles]</span></p>
<p><span>statbus.sys&nbsp;= 1,,</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>;*****************************************</span></p>
<p><span>; ToasterStatBus&nbsp;Install Section</span></p>
<p><span>;*****************************************</span></p>
<p>&nbsp;</p>
<p><span>；</span><span>Manufacturer</span><span>章节用于识别这样一些厂商，该厂商的一个或者多个设备能够使用该</span><span>inf</span><span>文件进行安装。</span></p>
<p><strong><span>[Manufacturer]<br></span></strong><em><span>manufacturer-identifier</span></em><span> <br>[<em>manufacturer-identifier</em>] <br>[<em>manufacturer-identifier</em>]</span></p>
<p><em>&nbsp;</em></p>
<p><em><span>manufacturer-identifier</span></em><em><span>如下表示：</span></em><em></em></p>
<p><em><span>manufacturer-name </span></em><span>|<br><strong>%</strong>strkey<strong>%</strong>=<em>models-section-name</em> |<br><strong>%</strong>strkey<strong>%</strong>=<em>models-section-name</em> [<strong>,</strong><em>TargetOSVersion</em>] [<strong>,</strong><em>TargetOSVersion</em>] ... (Windows XP and later versions of Windows)</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>该条目确定设备的厂商，并且在</span><span>inf</span><span>文件中必须有一个相应的同样的名字的</span><span>Models</span><span>章节。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>[Manufacturer]</span></p>
<p><span>%StdMfg%=Standard,NTx86</span></p>
<p><span>; For Win2K because it cannot parse decorated sections.</span></p>
<p><span>[Standard]</span></p>
<p><span>;</span></p>
<p><span>; These are the toaster bus pnp ids</span></p>
<p><span>; ToasterStatBus.DeviceDesc</span><span>是设备描述</span></p>
<p><span>；</span><span>ToasterStatBus_Device</span><span>是</span><span>install-section-name</span></p>
<p><span>; root\statbus</span><span>是硬件</span><span>ID</span></p>
<p><span>硬件</span><span>ID</span><span>有以下一些格式。</span></p>
<p align=left><em><span>enumerator</span></em><strong><span>\</span></strong><em><span>enumerator-specific-device-id</span></em><span> </span></p>
<p align=left><span>Is the typical format for individual PnP devices reported to the PnP manager by a single enumerator. For example, USB\VID_045E&amp;PID_00B identifies the Microsoft HID keyboard device on a USB bus. Depending on the enumerator, such a specification can even include the device's hardware revision number as, for example, PCI\VEN_1011&amp;DEV_002&amp;SUBSYS_00000000&amp;REV_02. </span></p>
<p align=left><strong><span>*</span></strong><em><span>enumerator-specific-device-id</span></em><span> </span></p>
<p align=left><span>Indicates with the asterisk (<strong>*</strong>) that the device is supported by more than one enumerator. For example, *PNP<st1:chmetcnv w:st="on" UnitName="F" SourceValue="0" HasSpace="False" Negative="False" NumberType="1" TCSC="0">0F</st1:chmetcnv>01 identifies the Microsoft serial mouse, which also has a <em>compatible-id</em> specification of SERENUM\PNP<st1:chmetcnv w:st="on" UnitName="F" SourceValue="0" HasSpace="False" Negative="False" NumberType="1" TCSC="0">0F</st1:chmetcnv>01. </span></p>
<p align=left><em><span>device-class-specific-ID</span></em><span> </span></p>
<p align=left><span>Is an I/O bus-specific format, as described in the hardware specification for the bus, for the hardware IDs of all peripheral devices on that type of I/O bus. </span></p>
<p>&nbsp;</p>
<p><span>%ToasterStatBus.DeviceDesc%=ToasterStatBus_Device, root\statbus</span></p>
<p>&nbsp;</p>
<p><span>; For XP and later</span></p>
<p><span>[Standard.NTx86]</span></p>
<p><span>%ToasterStatBus.DeviceDesc%=ToasterStatBus_Device, root\statbus</span></p>
<p>&nbsp;</p>
<p><span>[ToasterStatBus_Device.NT]</span></p>
<p><span>CopyFiles=Drivers_Dir</span></p>
<p>&nbsp;</p>
<p><span>[ToasterStatBus_Device.NT.HW]</span></p>
<p><span>AddReg=ToasterStatBus_Device.NT.AddReg</span></p>
<p>&nbsp;</p>
<p><span>[ToasterStatBus_Device.NT.AddReg]<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></p>
<p><span>HKR,,DeviceCharacteristics,0x10001,0x0100<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>; Use same security checks on relative opens</span></p>
<p><span>HKR,,Security,,"D:P(A;;GA;;;BA)(A;;GA;;;SY)"<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>; Allow generic-all access to Built-in administrators and Local system </span></p>
<p>&nbsp;</p>
<p><span>[Drivers_Dir]</span></p>
<p><span>statbus.sys</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>;-------------- Service installation</span></p>
<p><span>[ToasterStatBus_Device.NT.Services]</span></p>
<p><span>AddService = statbus,%SPSVCINST_ASSOCSERVICE%, Statbus_Service_Inst</span></p>
<p>&nbsp;</p>
<p><span>; -------------- statbus driver install sections</span></p>
<p><span>[Statbus_Service_Inst]</span></p>
<p><span>DisplayName<span>&nbsp;&nbsp;&nbsp; </span>= %statbus.SVCDESC%</span></p>
<p><span>ServiceType<span>&nbsp;&nbsp;&nbsp; </span>= 1&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>; SERVICE_KERNEL_DRIVER</span></p>
<p><span>StartType<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>= 3<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>; SERVICE_DEMAND_START </span></p>
<p><span>ErrorControl<span>&nbsp;&nbsp; </span>= 1<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>; SERVICE_ERROR_NORMAL</span></p>
<p><span>ServiceBinary&nbsp;= %12%\statbus.sys</span></p>
<p><span>LoadOrderGroup = Extended Base</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>;</span></p>
<p><span>;--- ToasterStatBus_Device WDF Coinstaller installation ------</span></p>
<p><span>;</span></p>
<p><span>[DestinationDirs]</span></p>
<p><span>ToasterStatBus_Device_CoInstaller_CopyFiles = 11</span></p>
<p>&nbsp;</p>
<p><span>[ToasterStatBus_Device.NT.CoInstallers]</span></p>
<p><span>AddReg=ToasterStatBus_Device_CoInstaller_AddReg</span></p>
<p><span>CopyFiles=ToasterStatBus_Device_CoInstaller_CopyFiles</span></p>
<p>&nbsp;</p>
<p><span>[ToasterStatBus_Device_CoInstaller_AddReg]</span></p>
<p><span>HKR,,CoInstallers32,0x00010000, "WdfCoInstaller01009.dll,WdfCoInstaller"</span></p>
<p>&nbsp;</p>
<p><span>[ToasterStatBus_Device_CoInstaller_CopyFiles]</span></p>
<p><span>WdfCoInstaller01009.dll</span></p>
<p>&nbsp;</p>
<p><span>[SourceDisksFiles]</span></p>
<p><span>WdfCoInstaller01009.dll=1 ; make sure the number matches with SourceDisksNames</span></p>
<p>&nbsp;</p>
<p><span>[ToasterStatBus_Device.NT.Wdf]</span></p>
<p><span>KmdfService = statbus, statbus_wdfsect</span></p>
<p><span>[statbus_wdfsect]</span></p>
<p><span>KmdfLibraryVersion = 1.9</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>[Strings]</span></p>
<p><span>SPSVCINST_ASSOCSERVICE= 0x00000002</span></p>
<p><span>MSFT = "Microsoft"</span></p>
<p><span>StdMfg = "(Standard system devices)"</span></p>
<p><span>DiskId1 = "Toaster Static Bus Installation Disk #1"</span></p>
<p><span>ToasterStatBus.DeviceDesc = "Toaster Static Bus Enumerator"</span></p>
<p><span>statbus.SVCDESC = "Toaster Static Bus Enumerator"</span></p>
<img src ="http://www.cppblog.com/Walker/aggbug/146269.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2011-05-12 18:54 <a href="http://www.cppblog.com/Walker/articles/146269.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何强制生成dump文件</title><link>http://www.cppblog.com/Walker/articles/133574.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Sun, 14 Nov 2010 01:14:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/133574.html</guid><description><![CDATA[<div style="MARGIN-LEFT: 195px" class="sbody kb_tabs_toggle_open" jQuery1289696904718="31">Windows 包含一种功能，可以用来促使系统停止响应并生成内存转储文件 (Memory.dmp)。在您执行此操作时，可能会收到一条类似以下内容的 Stop 错误消息：
<div class=kb_errormsgbody jQuery1289696904718="32">
<div class=kb_errorcontent jQuery1289696904718="33">
<div class=errormsg jQuery1289696904718="34">*** STOP:0x000000E2 (0x00000000,0x00000000,0x00000000,0x00000000)<br>The end-user manually generated the crashdump.</div>
</div>
</div>
启用此功能后，按住右 Ctrl 键，同时按 Scroll Lock 键两次，即可生成一个内存转储文件。此功能适用于 PS/2 键盘和通用串行总线 (USB) 键盘。PS/2 键盘使用键盘自带的 i8042prt.sys 驱动程序。但是，对于 USB 键盘，则必须为 Kbdhid.sys 驱动程序安装一个修补程序。有关此修补程序的更多信息，请参见&#8220;更多信息&#8221;部分末尾的&#8220;Windows Server 2003 解决方案&#8221;小节。<br><br><strong>注意</strong>：允许使用 USB 键盘生成内存转储过程的 Kbdhid.sys 驱动程序存在一定局限性。这就是，当计算机在高中断请求级别 (IRQL) 停止响应时，Ctrl+Scroll Lock+Scroll Lock 键盘快捷方式不起作用。之所以存在此局限性是因为与 i8042prt.sys 驱动程序相比，Kbdhid.sys 驱动程序运行时的 IRQL 较低。此 USB 键盘功能只在运行 Microsoft Windows Server 2003 的计算机上起作用。
<div class=topOfPage jQuery1289696904718="35"><a href="http://support.microsoft.com/kb/244139#top" jQuery1289696904718="118"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></div>
</div>
<h2 id=tocHeadRef class="subTitle kb_tabs_toggle_open" jQuery1289696904718="17">
<div class="kb_tabs_toggle kb_tabs_toggle_open" jQuery1289696904718="36"></div>
<span><a href="javascript:void(0);" jQuery1289696904718="119">更多信息</a></span>
<div class=sectionpreview_closed jQuery1289696904718="37">警告：如果使用注册表编辑器或其他方法错误地修改了注册表，则可能会出现严重问题。这些问题可能需要重新安装操作系统才能解决。Microsoft 不能保证可以解决这些...</div>
</h2>
<script type=text/javascript>
loadTOCNode(1, 'moreinformation');
</script>
<div style="MARGIN-LEFT: 195px" class="sbody kb_tabs_toggle_open" jQuery1289696904718="38"><strong>警告</strong>：如果使用注册表编辑器或其他方法错误地修改了注册表，则可能会出现严重问题。这些问题可能需要重新安装操作系统才能解决。Microsoft 不能保证可以解决这些问题。修改注册表需要您自担风险。<br><br>默认情况下，禁用此功能。要在使用 PS/2 键盘的计算机上启用此功能，需按本文中的说明修改注册表，然后重新启动计算机。在重新启动计算机后，按住 Ctrl 键，同时按 Scroll Lock 键两次，即可生成一个 Memory.dmp 文件。必须使用空格键右侧的 Ctrl 键。在使用 USB 键盘的计算机上，不必重新启动计算机。只需拔掉键盘然后再将它重新插上。然后，便能生成 Memory.dmp 文件。<br><br>要在使用 PS/2 键盘的计算机上启用此功能，请按照下列步骤操作：
<ol>
    <li>启动注册表编辑器。
    <li>找到以下注册表子项：
    <div class=indent jQuery1289696904718="39">HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\Parameters</div>
    <li>在&#8220;编辑&#8221;菜单上，单击&#8220;添加值&#8221;，然后添加以下注册表项：
    <div class=indent jQuery1289696904718="40"><strong>名称</strong>：CrashOnCtrlScroll<br><strong>数据类型</strong>：REG_DWORD<br><strong>值</strong>：1</div>
    <li>退出注册表编辑器，然后重新启动计算机。 </li>
</ol>
要在使用 USB 键盘的计算机上启用此功能，需安装&#8220;更多信息&#8221;部分末尾的&#8220;Windows Server 2003 解决方案&#8221;小节中提到的修补程序。<br><br>要确保在使用 USB 键盘的计算机上启用此功能，请按照下列步骤操作：
<ol>
    <li>启动注册表编辑器。
    <li>找到以下注册表子项：
    <div class=indent jQuery1289696904718="41">HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\kbdhid\Parameters</div>
    <li>确保启用了以下注册表项：
    <div class=indent jQuery1289696904718="42"><strong>名称</strong>：CrashOnCtrlScroll<br><strong>数据类型</strong>：REG_DWORD<br><strong>值</strong>：1</div>
    <li>退出注册表编辑器。 </li>
</ol>
<div class=topOfPage jQuery1289696904718="43"><a href="http://support.microsoft.com/kb/244139#top" jQuery1289696904718="120"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></div>
<h3 id=tocHeadRef>如何选择内存转储文件选项</h3>
<script type=text/javascript>
loadTOCNode(2, 'moreinformation');
</script>可以生成三种类型的内存转储文件。在手动触发转储文件前，先选择该文件。为此，请按照下列步骤操作：
<ol>
    <li>右键单击&#8220;我的电脑&#8221;，然后单击&#8220;属性&#8221;。
    <li>单击&#8220;高级&#8221;选项卡，然后单击&#8220;启动和故障恢复&#8221;按钮。
    <li>单击&#8220;写入调试信息&#8221;，然后单击以选中&#8220;完全内存转储&#8221;、&#8220;核心内存转储&#8221;或&#8220;小内存转储&#8221;。 </li>
</ol>
有关内存转储文件选项的更多信息，请单击下面的文章编号，以查看 Microsoft 知识库中相应的文章：
<div class=indent jQuery1289696904718="44"><a class=KBlink href="http://support.microsoft.com/kb/254649/" jQuery1289696904718="121">254649</a>&nbsp; <span class=pLink>(http://support.microsoft.com/kb/254649/ ) </span>Windows Server 2003、Windows XP 和 Windows 2000 内存转储文件选项概述 </div>
<strong>注意</strong>：如果服务器中有像某些 Compaq 计算机中所具有的&#8220;自动系统重启&#8221;(ASR) 这样的功能，请禁用它。因为该功能会中断转储过程。在 Compaq 计算机上，您可以通过修改基本输入/输出系统 (BIOS) 设置来禁用 ASR 功能。<br><br><strong>注意</strong>：在具有 2 GB 或更多 RAM 的计算机上，不能进行完全内存转储。要限制 Windows 2000 可以访问的内存，请向 Boot.ini 文件中添加 &lt;MaxMem=2000&gt; 参数。<br><br>如果已经安装了 Microsoft 知识库文章 835732 中描述的安全更新，或安装了包含此安全更新的 Service Pack，请查看下面的 Microsoft 知识库文章：
<div class=indent jQuery1289696904718="45"><a class=KBlink href="http://support.microsoft.com/kb/885117/" jQuery1289696904718="122">885117</a>&nbsp; <span class=pLink>(http://support.microsoft.com/kb/885117/ ) </span>&#8220;启动和故障恢复&#8221;中显示&#8220;核心内存转储&#8221;，但在 Windows 2000 或 Windows Server 2003 中执行完全内存转储 </div>
有关更多信息，请单击下面的文章编号，以查看 Microsoft 知识库中相应的文章：
<div class=indent jQuery1289696904718="46"><a class=KBlink href="http://support.microsoft.com/kb/835732/" jQuery1289696904718="123">835732</a>&nbsp; <span class=pLink>(http://support.microsoft.com/kb/835732/ ) </span>MS04-011：Microsoft Windows 安全更新 </div>
<div class=topOfPage jQuery1289696904718="47"><a href="http://support.microsoft.com/kb/244139#top" jQuery1289696904718="124"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></div>
<h3 id=tocHeadRef>Service Pack 信息</h3>
<script type=text/javascript>
loadTOCNode(2, 'moreinformation');
</script>要解决此问题，请获取最新的 Windows Server 2003 Service Pack。有关更多信息，请单击下面的文章编号，以查看 Microsoft 知识库中相应的文章：
<div class=indent jQuery1289696904718="48"><a class=KBlink href="http://support.microsoft.com/kb/889100/" jQuery1289696904718="125">889100</a>&nbsp; <span class=pLink>(http://support.microsoft.com/kb/889100/ ) </span>如何获取最新的 Windows Server 2003 Service Pack</div>
<div class=topOfPage jQuery1289696904718="49"><a href="http://support.microsoft.com/kb/244139#top" jQuery1289696904718="126"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></div>
<h3 id=tocHeadRef>修补程序信息</h3>
<script type=text/javascript>
loadTOCNode(2, 'moreinformation');
</script>Microsoft 现在提供了一个受支持的修补程序，但是，此修补程序仅用于修复本文所述的问题。请仅将此修补程序应用于出现这一特定问题的系统。此修补程序可能还会接受进一步的测试。因此，如果这个问题没有对您造成严重影响，我们建议您等待包含此修补程序的下一个 Windows Server 2003 Service Pack。<br><br>要解决此问题，请向 Microsoft 在线客户服务提交请求以获取该修补程序。要提交联机请求以获取该修补程序，请访问下面的 Microsoft 网站：
<div class=indent jQuery1289696904718="50"><a href="http://go.microsoft.com/?linkid=6294451" jQuery1289696904718="127">http://go.microsoft.com/?linkid=6294451</a><span class=pLink> (http://go.microsoft.com/?linkid=6294451) </span><span class=kb_space></span></div>
<strong>注意</strong>：如果发生其他问题或需要进行任何疑难解答，则您可能需要创建单独的服务请求。对于此特定修补程序无法解决的其他支持问题和事项，将照常收取支持费用。要创建单独的服务请求，请访问下面的 Microsoft 网站：
<div class=indent jQuery1289696904718="51"><a href="http://support.microsoft.com/contactus/?ws=support" jQuery1289696904718="128">http://support.microsoft.com/contactus/?ws=support</a><span class=pLink> (http://support.microsoft.com/contactus/?ws=support) </span><span class=kb_space></span></div>
<h4 id=tocHeadRef>先决条件</h4>
<script type=text/javascript>
loadTOCNode(3, 'moreinformation');
</script>要应用此修补程序，必须在计算机上安装 Windows Server 2003 或 Windows Server 2003 Service Pack 1。
<h4 id=tocHeadRef>重新启动要求</h4>
<script type=text/javascript>
loadTOCNode(3, 'moreinformation');
</script>应用此修补程序后，必须重新启动计算机。
<h4 id=tocHeadRef>修补程序替代信息</h4>
<script type=text/javascript>
loadTOCNode(3, 'moreinformation');
</script>此修补程序不替代任何其他修补程序。
<h4 id=tocHeadRef>文件信息</h4>
<script type=text/javascript>
loadTOCNode(3, 'moreinformation');
</script>此修补程序的英文版具有下表中列出的文件属性（或更新的文件属性）。这些文件的日期和时间按协调世界时 (UTC) 列出。当您查看文件信息时，该时间将转换为本地时间。要了解 UTC 与本地时间之间的时差，请使用&#8220;控制面板&#8221;中&#8220;日期和时间&#8221;项的&#8220;时区&#8221;选项卡。
<h5 id=tocHeadRef>Windows Server 2003（基于 x86 的 32 位版本）</h5>
<script type=text/javascript>
loadTOCNode(4, 'moreinformation');
</script>
<div class=kb_nowrapper jQuery1289696904718="52">
<div class=kb_nowrapper jQuery1289696904718="53"><span class=kb_collapsetext_close><span class=kb_collapsetext_background></span>收起该表格</span><span class=kb_expandtext><span class=kb_expandtext_background></span>展开该表格</span></div>
<div class=kb_nowrapper jQuery1289696904718="54"></div>
<table class=table cellSpacing=1>
    <tbody>
        <tr>
            <th>文件名</th>
            <th>文件版本</th>
            <th>文件大小</th>
            <th>日期</th>
            <th>时间</th>
            <th>平台</th>
            <th>SP 要求</th>
            <th>服务分支</th>
        </tr>
        <tr>
            <td>Kbdhid.sys</td>
            <td>5.2.3790.493</td>
            <td>16,896</td>
            <td>28-Feb-2006</td>
            <td>00:03</td>
            <td>x86</td>
            <td>无</td>
            <td>RTMQFE</td>
        </tr>
        <tr>
            <td>Kbdhid.sys</td>
            <td>5.2.3790.2649</td>
            <td>17,408</td>
            <td>28-Feb-2006</td>
            <td>03:11</td>
            <td>x86</td>
            <td>SP1</td>
            <td>SP1QFE</td>
        </tr>
    </tbody>
</table>
</div>
<h5 id=tocHeadRef>Windows Server 2003（基于 x64 的版本）</h5>
<script type=text/javascript>
loadTOCNode(4, 'moreinformation');
</script>
<div class=kb_nowrapper jQuery1289696904718="55">
<div class=kb_nowrapper jQuery1289696904718="56"><span class=kb_collapsetext_close><span class=kb_collapsetext_background></span>收起该表格</span><span class=kb_expandtext><span class=kb_expandtext_background></span>展开该表格</span></div>
<div class=kb_nowrapper jQuery1289696904718="57"></div>
<table class=table cellSpacing=1>
    <tbody>
        <tr>
            <th>文件名</th>
            <th>文件版本</th>
            <th>文件大小</th>
            <th>日期</th>
            <th>时间</th>
            <th>平台</th>
        </tr>
        <tr>
            <td>Kbdhid.sys</td>
            <td>5.2.3790.2649</td>
            <td>24,576</td>
            <td>13-Apr-2006</td>
            <td>15:59</td>
            <td>x64</td>
        </tr>
    </tbody>
</table>
</div>
<h5 id=tocHeadRef>Windows Server 2003（基于 Itanium 的版本）</h5>
<script type=text/javascript>
loadTOCNode(4, 'moreinformation');
</script>
<div class=kb_nowrapper jQuery1289696904718="58">
<div class=kb_nowrapper jQuery1289696904718="59"><span class=kb_collapsetext_close><span class=kb_collapsetext_background></span>收起该表格</span><span class=kb_expandtext><span class=kb_expandtext_background></span>展开该表格</span></div>
<div class=kb_nowrapper jQuery1289696904718="60"></div>
<table class=table cellSpacing=1>
    <tbody>
        <tr>
            <th>文件名</th>
            <th>文件版本</th>
            <th>文件大小</th>
            <th>日期</th>
            <th>时间</th>
            <th>平台</th>
            <th>SP 要求</th>
            <th>服务分支</th>
        </tr>
        <tr>
            <td>Kbdhid.sys</td>
            <td>5.2.3790.493</td>
            <td>47,104</td>
            <td>13-Apr-2006</td>
            <td>15:54</td>
            <td>IA-64</td>
            <td>无</td>
            <td>RTMQFE</td>
        </tr>
        <tr>
            <td>Kbdhid.sys</td>
            <td>5.2.3790.2649</td>
            <td>49,664</td>
            <td>13-Apr-2006</td>
            <td>15:59</td>
            <td>IA-64</td>
            <td>SP1</td>
            <td>SP1QFE</td>
        </tr>
    </tbody>
</table>
</div>
有关更多信息，请单击下面的文章编号，以查看 Microsoft 知识库中相应的文章：
<div class=indent jQuery1289696904718="61"><a class=KBlink href="http://support.microsoft.com/kb/928839/" jQuery1289696904718="129">928839</a>&nbsp; <span class=pLink>(http://support.microsoft.com/kb/928839/ ) </span>如何使用键盘在 Virtual Server 2005 来宾计算机上生成内存转储文件 </div>
<div class=topOfPage jQuery1289696904718="62"><a href="http://support.microsoft.com/kb/244139#top" jQuery1289696904718="130"><img alt="" src="http://support.microsoft.com/library/images/support/en-us/uparrow.gif">回到顶端</a></div>
<h3 id=tocHeadRef>配置注册表项以生成内存转储文件</h3>
<script type=text/javascript>
loadTOCNode(2, 'moreinformation');
</script>可以在以下注册表子项下配置相应项以生成内存转储文件：
<div class=indent jQuery1289696904718="63">HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\i8042prt\crashdump</div>
REG_DWORD 项如下所示：
<div class=indent jQuery1289696904718="64">Dump1Keys<br>Dump2Key </div>
Dump1Keys 项是要使用的修改键的位图。其值包括：
<div class=indent jQuery1289696904718="65">#define CRASH_R_SHIFT 0x01 <br>#define CRASH_R_CTRL 0x02 <br>#define CRASH_R_ALT 0x04 <br>#define CRASH_L_SHIFT 0x10 <br>#define CRASH_L_CTRL 0x20 <br>#define CRASH_L_ALT 0x40</div>
Dump2Key 项是键盘布局扫描码表中的索引。下面是驱动程序中的实际表。<br><br><strong>注意</strong>：索引 124 (sysreq) 是一种特殊情形，因为一个 84 键的键盘具有不同的扫描码。
<div class=kb_codebody jQuery1289696904718="66">
<div class=kb_codecontent jQuery1289696904718="67"><code>
<pre class=code>const UCHAR keyToScanTbl[134] = {
0x00,0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,
0x0A,0x0B,0x0C,0x0D,0x7D,0x0E,0x0F,0x10,0x11,0x12,
0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,0x00,
0x3A,0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,
0x27,0x28,0x2B,0x1C,0x2A,0x00,0x2C,0x2D,0x2E,0x2F,
0x30,0x31,0x32,0x33,0x34,0x35,0x73,0x36,0x1D,0x00,
0x38,0x39,0xB8,0x00,0x9D,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0xD2,0xD3,0x00,0x00,0xCB,
0xC7,0xCF,0x00,0xC8,0xD0,0xC9,0xD1,0x00,0x00,0xCD,
0x45,0x47,0x4B,0x4F,0x00,0xB5,0x48,0x4C,0x50,0x52,
0x37,0x49,0x4D,0x51,0x53,0x4A,0x4E,0x00,0x9C,0x00,
0x01,0x00,0x3B,0x3C,0x3D,0x3E,0x3F,0x40,0x41,0x42,
0x43,0x44,0x57,0x58,0x00,0x46,0x00,0x00,0x00,0x00,
0x00,0x7B,0x79,0x70 };</pre>
</code></div>
</div>
本文中提到的第三方产品由 Microsoft 以外的其他公司提供。对于这些产品的性能或可靠性，Microsoft 不作任何暗示保证或其他形式的保证。</div>
<img src ="http://www.cppblog.com/Walker/aggbug/133574.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2010-11-14 09:14 <a href="http://www.cppblog.com/Walker/articles/133574.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）调试笔记：系统挂在DPC（上） </title><link>http://www.cppblog.com/Walker/articles/133573.html</link><dc:creator>漫步者×&amp;……%￥</dc:creator><author>漫步者×&amp;……%￥</author><pubDate>Sun, 14 Nov 2010 00:59:00 GMT</pubDate><guid>http://www.cppblog.com/Walker/articles/133573.html</guid><wfw:comment>http://www.cppblog.com/Walker/comments/133573.html</wfw:comment><comments>http://www.cppblog.com/Walker/articles/133573.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/Walker/comments/commentRss/133573.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/Walker/services/trackbacks/133573.html</trackback:ping><description><![CDATA[<h2>调试笔记：系统挂在DPC（上） </h2>
<p>这是发生在我的笔记本电脑上的一次系统挂死，发生在唤醒过程中，屏幕没有任何显示，因为我的电脑始终是启用通过热键（Ctrl+ScrollLock）来触发蓝屏的，所以可以通过热键触发蓝屏和产生转储。</p>
<p>以下是分析转储文件的简要过程，转储的类型是内核转储。系统中只有一个CPU。</p>
<p><font face="Courier New">kd&gt; !cpuinfo<br>CP&nbsp; F/M/S Manufacturer&nbsp; MHz PRCB Signature&nbsp;&nbsp;&nbsp; MSR 8B Signature Features<br>&nbsp;0&nbsp; 6,13,8 GenuineIntel 1862 0000002000000000&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a0033fff<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Cached Update Signature 0000002000000000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Initial Update Signature 0000002000000000</font></p>
<p>操作系统是XP SP2：</p>
<p><font face="Courier New">kd&gt; version<br>Windows XP Kernel Version 2600 (Service Pack 2) UP Free x86 compatible<br>Product: WinNt, suite: TerminalServer SingleUserTS<br>Built by: 2600.xpsp_sp2_qfe.070227-2300</font></p>
<p>执行!pcr命令观察CPU的控制区：</p>
<p><font face="Courier New">kd&gt; !pcr<br>KPCR for Processor 0 at ffdff000:<br>&nbsp;&nbsp;&nbsp; Major 1 Minor 1<br>&nbsp;NtTib.ExceptionList: 80548fec<br>&nbsp;&nbsp;&nbsp;&nbsp; NtTib.StackBase: 805492f0<br>&nbsp;&nbsp;&nbsp; NtTib.StackLimit: 80546500<br>&nbsp; NtTib.SubSystemTib: 00000000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NtTib.Version: 00000000<br>&nbsp;&nbsp; NtTib.UserPointer: 00000000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NtTib.SelfTib: 00000000</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SelfPcr: ffdff000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Prcb: ffdff120<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Irql: 00000000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IRR: 00000000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDR: ffffffff<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; InterruptMode: 00000000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IDT: 8003f400<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GDT: 8003f000<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TSS: 80042000</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CurrentThread: 80551d20<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NextThread: 89b512e8<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IdleThread: 80551d20</font></p>
<p><font face="Courier New">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DpcQueue:&nbsp; 0x80552380 0x804ff5b8 [Normal] nt!KiTimerExpiration<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x8abcfed4 0xba7843e8 [Normal] ACPI!ACPIInterruptServiceRoutineDPC<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x8a6560cc 0xba50fdf0 [Normal] NDIS!ndisMDpcX<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x8a899eec 0xbaa28650 [Normal] i8042prt!I8042KeyboardIsrDpc<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x8a899e90 0xbaa2b0ef [Normal] i8042prt!I8xKeyboardSysButtonEventDpc<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x8a899dd0 0xbaa2a163 [Normal] i8042prt!I8042ErrorLogDpc<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xb87ffa4c 0xb87e8020 [Normal] SynTP<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xb87ffa24 0xb87e7f60 [Normal] SynTP</font></p>
<p>显然，DPC（Deferred Procedure Call ）队列中有不少任务等待执行，这是系统挂死的一个典型症状。DPC是Windows系统中一种极其重要的机制，DPC任务在DISPATCH_LEVEL执行，高于用来执行普通线程的PASSIVE_LEVEL。这意味着，如果一个CPU的DPC队列中有任务，那么这个CPU就不会去执行普通的线程。如果长时间保持这种状态，那么系统便会表现出挂死的症状——界面没有刷新，窗口不听使唤。</p>
<p>仔细观察上面显示的DPC队列，第一列是DPC对象的地址，也就是KDPC结构的地址，第二列是这个DPC的对应函数，带方括号的第三列是DPC的状态，第四列是DPC函数的符号表示。</p>
<p>可以使用dt命令来显示DPC结构：</p>
<p>kd&gt; dt _KDPC 0x80552380 <br>nt!_KDPC<br>&nbsp;&nbsp; +0x000 Type&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 19<br>&nbsp;&nbsp; +0x002 Number&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0 ''<br>&nbsp;&nbsp; +0x003 Importance&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0x1 ''<br>&nbsp;&nbsp; +0x004 DpcListEntry&nbsp;&nbsp;&nbsp;&nbsp; : _LIST_ENTRY [ 0x8abcfed8 - 0xffdff980 ]<br>&nbsp;&nbsp; +0x00c DeferredRoutine&nbsp; : 0x804ff5b8&nbsp;&nbsp;&nbsp;&nbsp; void&nbsp; nt!KiTimerExpiration+0<br>&nbsp;&nbsp; +0x010 DeferredContext&nbsp; : (null) <br>&nbsp;&nbsp; +0x014 SystemArgument1&nbsp; : 0x007af640 <br>&nbsp;&nbsp; +0x018 SystemArgument2&nbsp; : (null) <br>&nbsp;&nbsp; +0x01c Lock&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0xffdff9c0&nbsp; -&gt; 0</p>
<p>有这么多DPC任务，那么当前CPU按理说要么应该在执行某个DPC，要么应该在执行更高优先级的中断任务。不妨观察一下栈回溯：</p>
<p><font size=1 face="Courier New">kd&gt; kvn<br>&nbsp;# ChildEBP RetAddr&nbsp; Args to Child&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>00 80548f98 baa2a7fa 000000e2 00000000 00000000 nt!KeBugCheckEx+0x1b (FPO: [5,0,0])<br>01 80548fb4 baa2a032 00899d40 01ffffc6 00000000 i8042prt!I8xProcessCrashDump+0x237 (FPO: [3,0,0])<br>02 80548ffc 80540add 89d6cd98 8a899c88 00010009 i8042prt!I8042KeyboardInterruptService+0x21c (FPO: [Non-Fpo])<br>03 80548ffc 806d0d50 89d6cd98 8a899c88 00010009 nt!KiInterruptDispatch+0x3d (FPO: [0,2] TrapFrame @ 80549020)<br>04 805490a4 bac3d183 00000046 8a84c028 882e97c8 <font color=#ff1493>hal!HalpPmTimerStallExecProc</font>+0x60 (FPO: [1,5,0])<br>05 805490c4 b8cb8b50 bafde064 882e981c b8cb8ad6 <font color=#ff1493>usbehci!EHCI_RH_PortResetComplete</font>+0x61 (FPO: [2,2,4])<br>06 805490e4 804ff550 882e97f8 4d547961 33b06716 <font color=#ff1493>USBPORT!USBPORT_AsyncTimerDpc</font>+0x7a (FPO: [4,1,4])<br>07 80549200 804ff667 80551f80 80551d20 ffdff000 nt!KiTimerListExpire+0x122 (FPO: [0,62,0])<br>08 8054922c 8054111d 80552380 00000000 007af63f nt!KiTimerExpiration+0xaf (FPO: [4,6,0])<br>09 80549250 80541096 00000000 0000000e 00000000 nt!KiRetireDpcList+0x46 (FPO: [0,0,0])<br>0a 80549254 00000000 0000000e 00000000 00000000 nt!KiIdleLoop+0x26 (FPO: [0,0,0])</font></p>
<p>从#06栈帧的<font color=#ff1493 face="Courier New">USBPORT!USBPORT_AsyncTimerDpc</font>函数来看，当前CPU果真在勤恳的清理DPC任务。</p>
<p>#04栈帧中的<font color=#ff1493 face="Courier New">HalpPmTimerStallExecProc</font>函数是所谓的忙等待（Busy Wait）函数，它会让CPU在此循环一定时间，这通常是不得以而为之的做法，因为调用这样的函数会让CPU白白空转。从#05栈帧的函数名来看，这个函数是负责管理USB 2.0设备的EHCI的Root Hub用来重置USB端口的，通常的做法是写某个硬件寄存器，然后读取同一个或者不同的寄存器，等待硬件的确认。这种等待通常时间很短。但是从本例的情况来看，可能出意外了，等了很久，很可能是反复读，然后等待，在反复等的过程中，系统收到了强制蓝屏的热键。</p>
<p>分析到这里，我们知道这次系统挂死是与USB驱动程序设置的DPC任务有关的。这个DPC任务在与硬件通信来重置端口时，遇到了意外，于是进入怪圈，不断循环，锲而不舍，孜孜不倦，没完没了......【呵呵，汉语的词汇就是丰富】</p>
<p>那么这个DPC任务到底遇到什么意外了呢? 要想继续追究就要看代码了，源代码不公开，就考验看汇编的能力了。好在伟大的x86汇编有着超强的表达力，很容易理解，下一篇文章中我们将继续分析。^_^</p>
<p>&nbsp;</p>
<p>BTW. 对于DPC，理解的再深刻也不过分，有空时可以反复读一下Mark Russinovich的介绍：</p>
<p><a href="http://technet.microsoft.com/en-us/sysinternals/bb963898.aspx"><u><font color=#0000ff>http://technet.microsoft.com/en-us/sysinternals/bb963898.aspx</font></u></a></p>
<img src ="http://www.cppblog.com/Walker/aggbug/133573.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/Walker/" target="_blank">漫步者×&……%￥</a> 2010-11-14 08:59 <a href="http://www.cppblog.com/Walker/articles/133573.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>