﻿<?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++博客-c++初学者</title><link>http://www.cppblog.com/tgh621/</link><description>专注技术开发</description><language>zh-cn</language><lastBuildDate>Mon, 06 Apr 2026 03:11:43 GMT</lastBuildDate><pubDate>Mon, 06 Apr 2026 03:11:43 GMT</pubDate><ttl>60</ttl><item><title>VC程序修改注册表开机启动</title><link>http://www.cppblog.com/tgh621/archive/2011/06/13/148593.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Mon, 13 Jun 2011 09:20:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2011/06/13/148593.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/148593.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2011/06/13/148593.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/148593.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/148593.html</trackback:ping><description><![CDATA[<div id="blog_text" class="cnt"><strong><font size="5">VC 开机自动启动程序代码</font></strong> 
<div class="date"></div>
<table style="table-layout: fixed" class="FCK__ShowTableBorders">
<tbody>
<tr>
<td>
<div class="cnt">
<p>很多监控软件要求软件能够在系统重新启动后不用用户去点击图标启动项目，而是直接能够启动运行，方法是写注册表Software\\Microsoft\\Windows\\CurrentVersion\\Run 。</p>
<p>参考程序可以见下：(查找程序目录的执行文件，存在则进行添加注册表操作)&nbsp;&nbsp;</p>
<p>//实用代码一</p>
<p>int C***Dlg::CreateRun(void)<br />{</p>
<p>//添加以下代码<br />HKEY&nbsp;&nbsp; RegKey;&nbsp;&nbsp; <br />CString&nbsp;&nbsp; sPath;&nbsp;&nbsp; <br />GetModuleFileName(NULL,sPath.GetBufferSetLength(MAX_PATH+1),MAX_PATH);&nbsp;&nbsp; <br />sPath.ReleaseBuffer();&nbsp;&nbsp; <br />int&nbsp;&nbsp; nPos;&nbsp;&nbsp; <br />nPos=sPath.ReverseFind('\\');&nbsp;&nbsp; <br />sPath=sPath.Left(nPos);&nbsp;&nbsp; <br />CString&nbsp;&nbsp; lpszFile=sPath+"<a href="http://www.cnblogs.com/adslg/admin/file://getip.exe%22;//"><font color="#5f92bf">\\</font><font color="#ff0000">getip</font><font color="#5f92bf">.exe";//</font></a>这里加上你要查找的执行文件名称&nbsp;&nbsp; <br />CFileFind&nbsp;&nbsp; fFind;&nbsp;&nbsp; <br />BOOL&nbsp;&nbsp; bSuccess;&nbsp;&nbsp; <br />bSuccess=fFind.FindFile(lpszFile);&nbsp;&nbsp; <br />fFind.Close();&nbsp;&nbsp; <br />if(bSuccess)&nbsp;&nbsp; <br />{&nbsp;&nbsp; <br />&nbsp;&nbsp; CString&nbsp;&nbsp; fullName;&nbsp;&nbsp; <br />&nbsp;&nbsp; fullName=lpszFile;&nbsp;&nbsp; <br />&nbsp;&nbsp; RegKey=NULL;&nbsp;&nbsp; <br />&nbsp;&nbsp; RegOpenKey(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion\\Run",&amp;RegKey);&nbsp;&nbsp; <br />&nbsp;&nbsp; RegSetValueEx(RegKey<font color="#ff0000">,"getip",</font>0,REG_SZ,(const&nbsp;&nbsp; unsigned&nbsp;&nbsp; char*)(LPCTSTR)fullName,fullName.GetLength());//这里加上你需要在注册表中注册的内容&nbsp;&nbsp; <br />&nbsp;&nbsp; this-&gt;UpdateData(FALSE);&nbsp;&nbsp; <br />}&nbsp;&nbsp; <br />else&nbsp;&nbsp; <br />{&nbsp;&nbsp; <br />&nbsp;&nbsp; //theApp.SetMainSkin();&nbsp;&nbsp; <br />&nbsp;&nbsp; ::AfxMessageBox("没找到执行程序，自动运行失败");&nbsp;&nbsp; <br />&nbsp;&nbsp; exit(0);&nbsp;&nbsp; <br />}&nbsp;&nbsp; <br />return 0;<br />}</p>
<p>//把上面的getip（共2处）替换成自己想启动程序的名字。</p>
<p>================================================</p>
<p><font color="#ff6600">实用代码二：</font></p>
<p><font color="#ff6600">//写入注册表,开机自启动 <br />HKEY hKey; <br />//找到系统的启动项 <br />LPCTSTR lpRun = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"; <br />//打开启动项Key <br />long lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, lpRun, 0, KEY_WRITE, &amp;hKey); <br />if(lRet == ERROR_SUCCESS) <br />{ <br />&nbsp;&nbsp;&nbsp; char pFileName[MAX_PATH] = {0}; <br />&nbsp;&nbsp;&nbsp; //得到程序自身的全路径 <br />&nbsp;&nbsp;&nbsp; DWORD dwRet = GetModuleFileName(NULL, pFileName, MAX_PATH); <br />&nbsp;&nbsp;&nbsp; //添加一个子Key,并设置值 // 下面的<font color="#00ff00">"getip"是应用程序名字（不加后缀.exe）</font><br />&nbsp;&nbsp;&nbsp; lRet = RegSetValueEx(hKey, <font color="#00ff00">"getip",</font> 0, REG_SZ, (BYTE *)pFileName, dwRet); </font></p>
<p><font color="#ff6600">&nbsp;&nbsp;&nbsp; //关闭注册表 <br />&nbsp;&nbsp;&nbsp; RegCloseKey(hKey); <br />&nbsp;&nbsp;&nbsp; if(lRet != ERROR_SUCCESS) <br />&nbsp;&nbsp;&nbsp; { <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox("系统参数错误,不能随系统启动"); <br />&nbsp;&nbsp;&nbsp; } <br />} </font></p>
<p><font color="#ff0000">一、当前用户专有的启动文件夹<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 这是许多应用软件自动启动的常用位置，Windows自动启动放入该文件夹的所有快捷方式。用户启动文件夹一般在：\Documents and Settings\&lt;用户名字&gt;\「开始」菜单\程序\启动，其中&#8220;&lt;用户名字&gt;&#8221;是当前登录的用户帐户名称。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 二、对所有用户有效的启动文件夹<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 这是寻找自动启动程序的第二个重要位置，不管用户用什么身份登录系统，放入该文件夹的快捷方式总是自动启动&#8212;&#8212;这是它与用户专有的启动文件夹的区别所在。该文件夹一般在：\Documents and Settings\All Users\「开始」菜单\程序\启动。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 三、Load注册键<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 介绍该注册键的资料不多，实际上它也能够自动启动程序。位置：HKEY_CURRENT_USER\Software\Microsoft\WindowsNT\CurrentVersion\Windows\load。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 四、Userinit注册键<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 位置：HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\WindowsNT\CurrentVersion\Winlogon\Userinit。这里也能够使系统启动时自动初始化程序。通常该注册键下面有一个userinit.exe，如图，但这个键允许指定用逗号分隔的多个程序，例如&#8220;userinit.exe,OSA.exe&#8221;（不含引号）。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 五、Explorer\Run注册键<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 和load、Userinit不同，Explorer\Run键在HKEY_CURRENT_USER和HKEY_LOCAL_MACHINE下都有，具体位置是：HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run，和HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 六、RunServicesOnce注册键<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; RunServicesOnce注册键用来启动服务程序，启动时间在用户登录之前，而且先于其他通过注册键启动的程序。RunServicesOnce注册键的位置是：HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServicesOnce，和HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServicesOnce。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 七、RunServices注册键<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; RunServices注册键指定的程序紧接RunServicesOnce指定的程序之后运行，但两者都在用户登录之前。RunServices的位置是：HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunServices，和HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunServices。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 八、RunOnce\Setup注册键<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; RunOnce\Setup指定了用户登录之后运行的程序，它的位置是：HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce\Setup，和HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce\Setup。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 九、RunOnce注册键<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 安装程序通常用RunOnce键自动运行程序，它的位置在HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce和HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce。HKEY_LOCAL_MACHINE下面的RunOnce键会在用户登录之后立即运行程序，运行时机在其他Run键指定的程序之前。HKEY_CURRENT_USER下面的RunOnce键在操作系统处理其他Run键以及&#8220;启动&#8221;文件夹的内容之后运行。如果是XP，你还需要检查一下HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnceEx。<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; 十、Run注册键<br /><br />&nbsp;&nbsp;&nbsp;&nbsp; Run是自动运行程序最常用的注册键，位置在：HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run，和HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run。HKEY_CURRENT_USER下面的Run键紧接HKEY_LOCAL_MACHINE下面的Run键运行，但两者都在处理&#8220;启动&#8221;文件夹之前。</font></p>
<p style="text-indent: 2em">有时候人们往往会为了一个程序的启动而头痛，因为一些用户往往不知道那些文件是如何启动的。所以经常会有些没用的东西挂在系统上占用资源。有时候也会有人因为不知道如何启动某个文件而头痛。更有些特洛依木马的作者因为不清楚系统的自启动方式而使自己的木马轻松被别人发现&#8230;&#8230; 　　</p>
<p style="text-indent: 2em">　　Windows的自启动方式其实有许多方式。除了一些常见的启动方式之外，还有一些非常隐蔽的可用来启动文件的方式。本文总结如下，虽然不是全部，但我想应该会对大家有所帮助。文章全部以系统默认的状态为准，以供研究。</p>
<p style="text-indent: 2em">　　其中（English）代表英文操作系统，（Chinese）代表中文操作系统。本文没加说明指的全为中文Windows98操作系统。 　　</p>
<p style="text-indent: 2em">　　警告： 文中提及的一些操作可能会涉及到系统的稳定性。例如如果不正确地使用注册表编辑器可以导致可能重新安装系统这样严重的问题。微软也不能保证因不正常使用注册表编辑器而造成的结果可以被解决。笔者不对使用后果负责，请根据自己的情况使用。 　　</p>
<p style="text-indent: 2em">　　Windows的自启动方式：</p>
<p style="text-indent: 2em">　　一.自启动目录： 　　</p>
<p style="text-indent: 2em">　　1.第一自启动目录：</p>
<p style="text-indent: 2em">　　默认路径位于：</p>
<p style="text-indent: 2em">　　C:windowsstart menuprogramsstartup（English）</p>
<p style="text-indent: 2em">　　C:windowsstart menuprograms启动（Chinese）</p>
<p style="text-indent: 2em">　　这是最基本、最常用的Windows启动方式，主要用于启动一些应用软件的自启动项目，如Office的快捷菜单。一般用户希望启动时所要启动的文件也可以通过这里启动，只需把所需文件或其快捷方式放入文件夹中即可。</p>
<p style="text-indent: 2em">对应的注册表位置：</p>
<p style="text-indent: 2em">　　[HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionExplorerShell Folders]</p>
<p style="text-indent: 2em">　　Startup=\"%Directory%\"</p>
<p style="text-indent: 2em">　　[HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionExplorerUser Shell Folders]</p>
<p style="text-indent: 2em">　　Startup=\"%Directory%\"</p>
<p style="text-indent: 2em">　　其中&#8220;%Directory%&#8221;为启动文件夹位置。</p>
<p style="text-indent: 2em">　　</p>
<p style="text-indent: 2em">　　英文默认为： 　　C:windowsstart menuprogramsstartup</p>
<p style="text-indent: 2em">　　中文默认为： 　　C:windowsstart menuprograms启动 　　</p>
<p style="text-indent: 2em">　　在开始菜单的&#8220;启动&#8221;文件夹是可更改的，如果用户更改了启动文件夹，则以上注册表的键值均会改变为相应的名称。 　</p>
<p style="text-indent: 2em">　　值得注意的是：开始菜单的&#8220;启动&#8221;文件夹中的内容虽然在默认的状态下可以被用户看得一清二楚。但通过改动还是可以达到相当隐蔽地启动的目的的： 　　</p>
<p style="text-indent: 2em">　　首先，&#8220;启动&#8221;文件夹中的快捷方式或其他文件的属性可以改变为&#8220;隐藏&#8221;。这样可以达到系统不启动被隐藏的文件，等到需要启动的时候又可以通过更改回文件属性而恢复启动的作用。</p>
<p style="text-indent: 2em">其次，其实&#8220;启动&#8221;文件夹只是一个普通的文件夹，但是由于系统监视了这个文件夹，所以变得有些特殊，但文件夹有的功能该文件夹也是有的。譬如&#8220;启动&#8221;文件夹的名称是可以更改的，并且&#8220;启动&#8221;文件夹也可以设置属性。如果把属性设置为&#8220;隐藏&#8221;，则在系统中的【开始】?【程序】菜单中是看不到&#8220;启动&#8221;文件夹的（即使在&#8220;文件夹选项&#8221;中已经设定了&#8220;显示所有文件&#8221;）。而系统还会启动这个被隐藏的文件夹中的非隐藏文件。</p>
<p style="text-indent: 2em">　　敏感的人们也许已经发现问题。举一个例子： 　　</p>
<p style="text-indent: 2em">　　如果我想启动A木马的server端服务器，我可以把原来的&#8220;启动&#8221;菜单的名称更改为&#8220;StartUp&#8221;（这里是随便改的，注册表相应的键值也会自动更改。）之后再创建一个名为&#8220;启动&#8221;的文件夹，把&#8220;StartUp&#8221;菜单中的文件全部复制（这里用复制，可以骗过用户的检查）到&#8220;启动&#8221;菜单中，然后把A木马的server程序放入&#8220;StartUp&#8221;文件夹中，最后把&#8220;StartUp&#8221;文件夹隐藏。大功告成！</p>
<p style="text-indent: 2em">　　</p>
<p style="text-indent: 2em">　　从外表看来，用户的【开始】?【启动】目录还在，而且要启动的文件也在。但系统此时启动的文件不是名为&#8220;启动&#8221;的文件夹中的文件，而是名为&#8220;StartUp&#8221;的文件夹中的文件。如果木马做的好的话，完全可以在每次启动的时候把&#8220;StartUp&#8221;中的文件复制到&#8220;启动&#8221;目录中来达到实时更新启动目录的目的。由于&#8220;StartUp&#8221;文件夹被隐藏，从【开始】?【程序】中是无法看到真正的启动菜单&#8220;StartUp&#8221;的，所以达到了隐蔽启动的目的！</p>
<p style="text-indent: 2em">　　这个启动方式虽然比较隐蔽，但通过msconfig依旧可以在&#8220;启动&#8221;页中看出来。　　</p>
<p style="text-indent: 2em">　　2.第二自启动目录：</p>
<p style="text-indent: 2em">　　是的，其实，Windows还有另外一个自启动目录，而且很明显但却经常被人们忽略的一个。</p>
<p style="text-indent: 2em">　　该路径位于：</p>
<p style="text-indent: 2em">　　C:WINDOWSAll UsersStart MenuProgramsStartUp（English）</p>
<p style="text-indent: 2em">　　C:WINDOWSAll UsersStart MenuPrograms启动（Chinese）</p>
<p style="text-indent: 2em">　　这个目录的使用方法和第一自启动目录是完全一样的。只要找到该目录，将所需要启动的文件拖放进去就可以达到启动的目的。</p>
<p style="text-indent: 2em">　　[HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsCurrentVersionexplorerUser Shell Folders]</p>
<p style="text-indent: 2em">　　\"Common Startup\"=\"%Directory%\"</p>
<p style="text-indent: 2em">　　[HKEY_LOCAL_MACHINESoftwareMicrosoftWindowsCurrentVersionexplorerShell Folders]</p>
<p style="text-indent: 2em">　　\"Common Startup\"=\"%Directory%\"</p>
<p style="text-indent: 2em">　　</p>
<p style="text-indent: 2em">　　值得注意的是：该目录在开始菜单的&#8220;启动&#8221;目录中是完全不能被看见的。而伴随着每次启动，该目录下的非隐藏文件也会随之启动！ 另外，在Msconfig中可以看到在这个目录下要启动的文件。</p>
<p style="text-indent: 2em">二.系统配置文件启动： 　　</p>
<p style="text-indent: 2em">　　由于系统的配置文件对于大多数的用户来说都是相当陌生的；这就造成了这些启动方法相对来说都是相当隐蔽的，所以这里提到的一些方法常常会被用于做一些破坏性的操作，请读者注意。 　　</p>
<p style="text-indent: 2em">　　1.WIN.INI启动：</p>
<p style="text-indent: 2em">　　启动位置（file.exe为要启动的文件名称）：</p>
<p style="text-indent: 2em">　　[windows]</p>
<p style="text-indent: 2em">　　load=file.exe</p>
<p style="text-indent: 2em">　　run=file.exe</p>
<p style="text-indent: 2em">　　注意：load=与run=的区别在于：通过load=运行文件，文件会在后台运行（最小化）；而通过run=来运行，则文件是在默认状态下被运行的。 　　</p>
<p style="text-indent: 2em">　　2.SYSTEM.INI启动：</p>
<p style="text-indent: 2em">　　启动位置（file.exe为要启动的文件名称）：</p>
<p style="text-indent: 2em">　　默认为：</p>
<p style="text-indent: 2em">　　[boot]</p>
<p style="text-indent: 2em">　　Shell=Explorer.exe</p>
<p style="text-indent: 2em">　　可启动文件后为：</p>
<p style="text-indent: 2em">　　[boot]</p>
<p style="text-indent: 2em">　　Shell=Explorer.exe file.exe</p>
<p style="text-indent: 2em">　　说明：</p>
<p style="text-indent: 2em">　　笔者记得在诺顿先生（就是开发出Norton系列软件的人）写的一本书里面曾经说过，1、2这两个文件的有无对系统没有什么影响，但由于时间的关系，笔者没有来得及试验，有兴趣者可以试一试。 　　</p>
<p style="text-indent: 2em">　　不过有一点是可以肯定的，这样的启动方式往往会被木马或一些恶作剧程序（如，妖之吻）利用而导致系统的不正常。由于一般用户很少会对这两个文件关心，甚至有的人不知道这些文件是做什么用的，所以隐蔽性很好。但由于其使用的越来越频繁，这种启动方式也被渐渐的察觉了。用户可以使用msconfig这个命令实现检查是否有什么程序被加载。具体的是在看是菜单中的&#8220;运行&#8221;中输入msconfig回车，之后按照文字说明即可。 　　</p>
<p style="text-indent: 2em">　　注意：</p>
<p style="text-indent: 2em">　　1.　　和WIN.INI文件不同的是，SYSTEM.INI的启动只能启动一个指定文件，不要把Shell=Explorer.exe file.exe换为Shell=file.exe，这样会使Windows瘫痪！</p>
<p style="text-indent: 2em">　　2.　　这种启动方式提前于注册表启动，所以，如果想限制注册表中的文件的启动，可是使用这种方法。</p>
<p style="text-indent: 2em">　　3.WININIT.INI启动：</p>
<p style="text-indent: 2em">　　Wininit.ini这个文件也许很多人不知道，一般的操作中用户也很少能直接和这个文件接触。但如果你编写过卸载程序的话，也许你会知道这个文件。</p>
<p style="text-indent: 2em">　　WinInit即为Windows Setup Initialization Utility。翻译成中文就是Windows安装初始化工具。这么说也许不明白，如果看到如下提示信息：</p>
<p style="text-indent: 2em">　　Please wait while Setup updates your configuration files.</p>
<p style="text-indent: 2em">　　This may take a few minutes...</p>
<p style="text-indent: 2em">　　大家也许就都知道了！这个就是Wininit.ini在起作用！ 　　</p>
<p style="text-indent: 2em">　　由于在Windows下，许多的可执行文件和驱动文件是被执行到内存中受到系统保护的。所以在Windows的正常状态下更改这些文件就成了问题，因此出现了Wininit.ini这个文件来帮助系统做这件事情。它会在系统装载Windows之前让系统执行一些命令，包括复制，删除，重命名等，以完成更新文件的目的。Wininit.ini文件存在于Windows目录下，但在一般时候我们在C:Windows目录下找不到这个文件，只能找到它的exe程序Wininit.exe。原因就是Wininit.ini在每次被系统执行完它其中的命令时就会被系统自动删除，直到再次出现新的Wininit.ini文件&#8230;&#8230;之后再被删除。</p>
<p style="text-indent: 2em">文件格式：</p>
<p style="text-indent: 2em">　　[rename]</p>
<p style="text-indent: 2em">　　file1=file2</p>
<p style="text-indent: 2em">　　file1=file2的意思是把file2文件复制为文件名为file1的文件，相当于覆盖file1文件。</p>
<p style="text-indent: 2em">　　这样启动时，Windows就实现了用file2更新file1的目的；如果file1不存在，实际结果是将file2复制并改名为file1；如果要删除文件，则可使用如下命令：</p>
<p style="text-indent: 2em">　　[rename]</p>
<p style="text-indent: 2em">　　nul=file2　</p>
<p style="text-indent: 2em">　　这也就是说把file2变为空，即删除的意思。</p>
<p style="text-indent: 2em">　　以上文件名都必须包含完整路径。</p>
<p style="text-indent: 2em">　　注意：</p>
<p style="text-indent: 2em">　　1.由于Wininit.ini文件处理的文件是在Windows启动以前处理的，所以不支持长文件名。</p>
<p style="text-indent: 2em">　　2.以上的文件复制、删除、重命名等均是不提示用户的情况下执行的。有些病毒也会利用这个文件对系统进行破坏，所以用户如果发现系统无故出现：</p>
<p style="text-indent: 2em">　　Please wait while Setup updates your configuration files.</p>
<p style="text-indent: 2em">　　This may take a few minutes...</p>
<p style="text-indent: 2em">　　那么也许系统就有问题了。</p>
<p style="text-indent: 2em">　　3. 在Windows 95 Resource Kit中提到过Wininit.ini文件有三个可能的段，但只叙述了[rename]段的用法。</p>
<p style="text-indent: 2em">　　4.WINSTART.BAT启动：</p>
<p style="text-indent: 2em">　　这是一个系统自启动的批处理文件，主要作用是处理一些需要复制、删除的任务。譬如有些软件会在安装或卸载完之后要求重新启动，就可以利用这个复制和删除一些文件来达到完成任务的目的。如：</p>
<p style="text-indent: 2em">　　&#8220;@if exist C:WINDOWSTEMPPROC.BAT call C:WINDOWSTEMPPROC.BAT&#8221;</p>
<p style="text-indent: 2em">　　这里是执行PROC.BAT文件的命令；</p>
<p style="text-indent: 2em">　　&#8220;call filename.exe &gt; nul&#8221;</p>
<p style="text-indent: 2em">　　这里是去除任何在屏幕上的输出。</p>
<p style="text-indent: 2em">　　值得注意的是WinStart.BAT文件在某种意义上有和AUTOEXEC.BAT一样的作用。如果巧妙安排完全可以达到修改系统的目的！</p>
<p style="text-indent: 2em">　　5.AUTOEXEC.BAT启动： 这个就没的说了，应该是用户再熟悉不过的系统文件之一了。每次重新启动系统时在DOS下启动。恶意的程序往往会利用这个文件做一些辅助的措施。　　</p>
<p style="text-indent: 2em">　　不过，在AUTOEXEC.BAT文件中会包含有恶意代码。如format c: /y等；由于BAT恶意程序的存在，这个机会大大地增加了。譬如最近很流行的SirCam蠕虫也利用了Autoexec.bat文件。</p>
<p style="text-indent: 2em">　　说明： 4、5这两个文件都是批处理文件，其作用往往不能完全写出来，因为批处理的用处在DOS时代的应用太广泛，它的功能相对来说也是比较强大。想利用这两个文件，需要对DOS有一定的了解。</p></div></td></tr></tbody></table></div><img src ="http://www.cppblog.com/tgh621/aggbug/148593.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2011-06-13 17:20 <a href="http://www.cppblog.com/tgh621/archive/2011/06/13/148593.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]使用Gflags来检测heap问题</title><link>http://www.cppblog.com/tgh621/archive/2011/01/17/138663.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Mon, 17 Jan 2011 04:57:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2011/01/17/138663.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/138663.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2011/01/17/138663.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/138663.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/138663.html</trackback:ping><description><![CDATA[<p>如果你是C++程序员，如果你写过一个很复杂的程序，如果你经常碰到莫名其妙的崩溃问题。那么你就有可能遭遇了野指针。如果你比较细心，注意了Debug Output输出窗口的话，那么你就有可能注意到这样一行提示：<br><strong>HEAP:&nbsp;&nbsp; Free&nbsp;&nbsp; Heap&nbsp;&nbsp; block&nbsp;&nbsp; xxxxxxxx modified&nbsp;&nbsp; at&nbsp;&nbsp; xxxxxxxx&nbsp; after&nbsp;&nbsp; it&nbsp;&nbsp; was&nbsp;&nbsp; freed </strong></p>
<p><strong></strong>&nbsp;</p>
<p>GFlags是Windows debug tools 工具包下的一个工具,在Windows 2000的Resource Kit中也可以找得到。用来设置一些调试属性，总体上分为3个级别System，Kernel和Image File。我们设置好Path环境变量,将其指向Debug tools工具的目录下。</p>
<p>下载安装 gflags:</p>
<p><a href="http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx"><u><font color=#0066cc>http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx</font></u></a></p>
<p><a href="http://www.ithov.com/Soft/system/systest/38210.shtml"><u><font color=#0066cc>http://www.ithov.com/Soft/system/systest/38210.shtml</font></u></a></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'">GFlags&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'"> </span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">-&nbsp;<span style="FONT-FAMILY: 宋体">老牌的PageHeap配置工具，有命令行和GUI两种操作方式，功能比较全，包含在Windbg调试器安装包内。同样在Windows&nbsp;2000&nbsp;Professional&nbsp;SP2&nbsp;以上可用。</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">一些使用GFlags命令行的例子：</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">配置正常页堆：</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">"C:\Program&nbsp;Files\Debugging&nbsp;Tools&nbsp;for&nbsp;Windows&nbsp;(x86)\gflags.exe"&nbsp;/p&nbsp;/enable&nbsp;qq.exe</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">配置完全页堆：</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">"C:\Program&nbsp;Files\Debugging&nbsp;Tools&nbsp;for&nbsp;Windows&nbsp;(x86)\gflags.exe"&nbsp;/p&nbsp;/enable&nbsp;qq.exe&nbsp;/full</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">列出当前启动了页堆的进程列表：</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">"C:\Program&nbsp;Files\Debugging&nbsp;Tools&nbsp;for&nbsp;Windows&nbsp;(x86)\gflags.exe"&nbsp;/p</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">取消页堆设置：</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">"C:\Program&nbsp;Files\Debugging&nbsp;Tools&nbsp;for&nbsp;Windows&nbsp;(x86)\gflags.exe"&nbsp;/p&nbsp;/disable&nbsp;qq.exe</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'">一些特殊选项解释：</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'"><strong>/unaligned</strong></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">这个选项只能用于完全页堆。当我们从普通堆管理器分配一块内存时，内存总是8字节对齐的，页堆默认情况下也会使用这个对齐规则，但是这会导致分配的内存块的结尾不能跟页边界精确对齐，可能存在0-7个字节的间隙，显然，对位于间隙范围内的访问是不会被立即发现。更准确的说，读操作将永远不能被发现，写操作则要等到内存块释放时校验间隙空间内的填充信息时才发现。/unaligned用于修正这个缺陷，它指定页堆管理器不必遵守8字节对齐规则，保证内存块尾部精确对齐页边界。</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">需要注意的是，一些程序启用这个选项可能出现异常，例如IE和QQ就不支持。</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'"><strong>/backwards</strong></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">这个选项只能用于完全页堆。这个选项使得分配的内存块头部与页边界对齐（而不是尾部与边界对齐），通过这个选项来检查头部的访问越界。</span></p>
<p><strong><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">/debug</span></strong></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'">指定一启动进程即Attach到调试器，对于那些不能自动生成dump的程序，是比较有用的选项。</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">完全页堆：</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 当分配一块内存时，通过调整内存块的分配位置，使其结尾恰好与系统分页边界对齐，然后在边界处再多分配一个不可访问的页作为保护区域。这样，一旦出现内存读/写越界时，进程就会Crash，从而帮助及时检查内存越界。</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">因为每次分配的内存都要以这种形式布局，尤其对于小片的内存分配，即使分配一个字节，也要分配一个内存页，和一个保留的虚拟内存页（注意在目前的实现中，这个用作边界保护区域的页从来不会被提交）。这就需要大量的内存，到底一个进程需要多少内存，很难估算，因此在使用Page&nbsp;Heap前，至少保证你的机器至少设置了1G虚拟内存以上。</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">正常页堆</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'"><span style="FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 正常页堆原理与CRT调试内存分配函数类似，通过分配少量的填充信息，在释放内存块时检查填充区域。来检测内存是否被损坏，此方法的优点是极大的减少了内存耗用量。缺点是只能在释放块时检测，不太好跟踪出错的代码位置。</span></span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'">页堆能处理的错误类型：</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'">错误类型&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'">正常页堆&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: bold; mso-spacerun: 'yes'">整页堆&nbsp;</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">堆句柄无效&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">立即发现&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">立即发现&nbsp;</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">堆内存块指针无效&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">立即发现&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">立即发现&nbsp;</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">多线程访问堆不同步&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">立即发现&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">立即发现&nbsp;</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">假设重新分配返回相同地址(realloc)&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'"> </span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">90%&nbsp;内存释放后发现&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">90%&nbsp;立即发现&nbsp;</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">内存块重复释放&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">90%&nbsp;立即发现&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">90%&nbsp;立即发现&nbsp;</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">访问已释放的内存块&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">90%&nbsp;在实际释放后发现&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">90%&nbsp;立即发现&nbsp;</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">访问块结尾之后的内容&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">在释放后发现&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">立即发现&nbsp;</span></p>
<p><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">访问块开始之前的内容&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">在释放后发现&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="FONT-FAMILY: '宋体'; COLOR: #000000; FONT-SIZE: 10.5pt; FONT-WEIGHT: normal; mso-spacerun: 'yes'">立即发现&nbsp;</span></p>
<p>以下是举例：</p>
<p>前期工作： 将gflags（默认安装在C:\Program Files\Debugging Tools for Windows (x86)）加入到path</p>
<p><strong>案例1：</strong><br>int _tmain(int argc, _TCHAR* argv[])<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; char *p = new char[8];<br>&nbsp;&nbsp;&nbsp;&nbsp; p[8] = 10;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; delete[] p;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br>}<br>程序本身是有问题的。数组已经越界，但是debug模式下并不报错，release模式下也很大可能是不crash的。</p>
<p>在命令提示符下运行：</p>
<p><strong>&gt;gflags /p /enable test.exe /full</strong></p>
<p>在release模式运行test.exe。exception将直接定位到 p[8] = 10; 这一行</p>
<p><strong>案例2：</strong><br>int _tmain(int argc, _TCHAR* argv[])<br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; char *p = new char[9];<br>&nbsp;&nbsp;&nbsp;&nbsp; p[9] = 10;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp; delete[] p;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return 0;<br>}<br>以上代码和案例1仅有一点不同，就是数组大小。但是如果运行<br>gflags /p /enable test.exe /full</p>
<p>在release模式下并不会出现exception并定位到 p[9] = 10;<br>原因是没有设置 <strong>/unaligned</strong> 参数，具体看说明。案例2中，数组有9字节大小，按内存8字节对齐的说法，这块内存应该是</p>
<p>16字节，后面还有7字节的空间，所以 p[9] = 10; 并不会产生exception。设置 /unaligned 参数，禁止8字节对齐，就</p>
<p>可以跟踪到 p[9] = 10; 这个exception</p>
<p><strong>&gt;gflags /p /enable test.exe /full /unaligned</strong></p>
<p>案例3：<br>class A<br>{<br>public:<br>&nbsp;int a;<br>&nbsp;void del(){<br>&nbsp;&nbsp;delete this;<br>&nbsp;&nbsp;a = 10;<br>&nbsp;}<br>};<br>int _tmain(int argc, _TCHAR* argv[])<br>{<br>&nbsp;A* a = new A();<br>&nbsp;a-&gt;del();<br>&nbsp;return 0;<br>}</p>
<p>在debug模式下可能产生exception:<br>HEAP:&nbsp;&nbsp; Free&nbsp;&nbsp; Heap&nbsp;&nbsp; block&nbsp;&nbsp; xxxxxxxx modified&nbsp;&nbsp; at&nbsp;&nbsp; xxxxxxxx&nbsp; after&nbsp;&nbsp; it&nbsp;&nbsp; was&nbsp;&nbsp; freed<br>在release模式下运行并不报错，但是程序本身是有问题的，delete this; 之后，又给成员变量 a=10; </p>
<p>这显然是不对的。</p>
<p><strong>&gt;gflags /p /enable test.exe /full</strong><br>此时在debug下运行程序，会产生exception，并定位到&nbsp;&nbsp;&nbsp;a = 10;</p>
<img src ="http://www.cppblog.com/tgh621/aggbug/138663.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2011-01-17 12:57 <a href="http://www.cppblog.com/tgh621/archive/2011/01/17/138663.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]如何手工抓取dump文件</title><link>http://www.cppblog.com/tgh621/archive/2010/10/27/131525.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Wed, 27 Oct 2010 09:38:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2010/10/27/131525.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/131525.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2010/10/27/131525.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/131525.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/131525.html</trackback:ping><description><![CDATA[<p>在生产环境下进行故障诊断时，为了不终止正在运行的服务或应用程序，有两种方式可以对正在运行的服务或应用程序的进程进行分析和调试。</p>
<p>首先一种比较直观简洁的方式就是用WinDbg等调试器直接attach到需要调试的进程，调试完毕之后再detach即可。但是这种方式有个缺点就是执行debugger命令时必须先break这个进程，执行完debug命令之后又得赶紧F5让他继续运行，因为被你break住的时候意味着整个进程也已经被你挂起。另外也经常会由于First Chance Excetpion而自动break，你得时刻留意避免长时间break整个进程。所以这样的调试方式对时间是个很大的考验，往往没有充裕的时间来做仔细分析。</p>
<p>另一种方式则是在出现问题的时候，比如CPU持续长时间100%，内存突然暴涨等非正常情况下，通过对服务进程snapshot抓取一个dump文件，完成dump之后先deatch，让进程继续运行。然后用windbg等工具来分析这个抓取到的dump文件。</p>
<p>那么如何在不终止进程的情况下抓取dump文件呢？Debugging Tools for Windows里提供了一个非常好的工具，adplus.vbs。从名字可以看出，实际上是一个vb脚本，只是对cdb调试器作的一个包装脚本。</p>
<p>其路径与Debugging Tools for Windows的安装路径相同，使用的方法也很简单，如下所示:</p>
<p>adplus.vbs -hang -p 1234 -o d:\dump</p>
<p>其中-hang指明使用hang模式，亦即在进程运行过程中附加上去snapshot抓取一个dump文件，完成之后detach。与之对应的是-crash崩溃模式，用户先启动adplus，然后由它启动要监控的程序，在出现异常崩溃时自动生成dump文件，或者通过Ctrl-C人为发出抓取指令。但是-crash模式在抓取完成之后，被监控的进程就必须终止。因此我们在这里只选用-hang模式。</p>
<p>-p是要调试的进程ID，比如ASP.NET应用线程池，在Win2003下就是w3wp.exe</p>
<p>-o 指定要output的dump文件路径。</p>
<p>另外，与adplus类似的，有个UserDump工具，但是抓取用户模式的进程，而adplus则是内核模式和用户模式两者皆可。</p>
<p>而总所周至的Dr. Waston，则会在进程崩溃之后的自动时候抓取dump文件，一样可以用于windbg等调试器来事后分析程序崩溃时的状态。</p>
<p>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝<br>0:000&gt; !dumpheap -stat <br>No export dumpheap found <br>======解决方法：<br>.load clr20\sos.dll，你要先执行的。sos.dll在默认的c:\windows\microsoft.net\framework\v2.....下面，你复制到c:\program files\debugging tools for windows下面的clr20目录下面（clr20是你手工创建的） <br>&nbsp;＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝<br>&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp; 在.NET下开发时，最基本的调试方法就是使用Visual Studio的单步调试。但是对于一些特殊情况，特别是涉及到CLR内部的时候使用这种方式就达不到目的了。 <br>&nbsp; 如果要查看运行时内存使用情况，IL代码，CLR信息等可以使用以下两种方式： <br>&nbsp; 1、使用VS2005 + sos.dll <br>&nbsp; 2、使用Windbg + sos.dll <br>&nbsp; 第二种方式功能更加强大，下面我就通过实际操作展示一下怎么使用这种方法得到运行时ArrayList内部的值。 <br>&nbsp; 有人可能会说：我直接用Visual Studio的单步调试岂不是更快？当然，这个只是一个演示，通过这个演示是为以后的高级调试打下基础 <br>&nbsp;&nbsp; <br>&nbsp; 在操作之前，先熟悉一下基本知识： <br>&nbsp; A、使用VS2005 + sos.dll调试 <br>&nbsp; 1、需要在项目-&gt;属性-&gt;调试-〉启用非托管代码调试 <br>&nbsp; 2、打开调试-〉窗口-〉即时 <br>&nbsp; 3、在即时窗口中输入 !load sos 加载调试模块 <br>&nbsp; 4、输入其它调试语句 <br>&nbsp;&nbsp; <br>&nbsp; B、使用Windbg + sos.dll <br>&nbsp; 1、去微软的网站下载最新的Windbg <br>&nbsp; 2、打开Windbg在File-〉Symbol File Path ...窗口中输入 srv*c:\symbols*http://msdl.microsoft.com/download/symbols <br>&nbsp; 3、运行需要调试的程序，然后在Windbg中File-〉Attach to Process中选择刚才运行的程序 <br>&nbsp; 4、在出现的Command窗口中就可以输入调试语句 <br>&nbsp; 5、常用调试语句： <br>&nbsp;&nbsp; lm //查看加载了哪些模块 <br>&nbsp;&nbsp; .load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll //加载调试模块 <br>&nbsp;&nbsp; ld TestClass //加载调试符号 <br>&nbsp;&nbsp; !name2ee TestClass.exe TestClass.Program.test //显示test方法相关的地址 <br>&nbsp;&nbsp; !dumpmt -md 00976d48 //得到类的成员函数详细信息 <br>&nbsp;&nbsp; !dumpil 00973028 // 显示这个方法被编译器编译之后的IL代码 <br>&nbsp;&nbsp; !dumpheap -stat //该命令显示程序中所有对象的统计信息，显示的大小是对象本身的大小，不包括对象里面值的大小 <br>&nbsp;&nbsp; !dumpheap -mt 790fcb30 //该命令显示MethodTable 790fcb30的详细信息 <br>&nbsp;&nbsp; !gcroot 012919b8 //来显示一个实例的所属关系 <br>&nbsp;&nbsp; !dumpobj(do) 012a3904 //显示一个对象的具体内容，看对象里面有什么，值是什么 <br>&nbsp;&nbsp; !ObjSize 012a1ba4 //对象实际在内存中的大小 <br>&nbsp;&nbsp; !eeheap -gc //查看托管堆的情况(包括大小) <br>&nbsp;&nbsp; !DumpArray //查看数组信息 <br>&nbsp;&nbsp; 下面就来看看具体的调试步骤： <br>&nbsp; 1、我们的测试代码 <br>&nbsp;&nbsp; <br>&nbsp;&nbsp; <br>&nbsp; namespace TestClass <br>&nbsp; { <br>&nbsp;&nbsp; class Program <br>&nbsp;&nbsp; { <br>&nbsp;&nbsp; [STAThread] <br>&nbsp;&nbsp; static void Main(string[] args) <br>&nbsp;&nbsp; { <br>&nbsp;&nbsp; ArrayList list = new ArrayList(); <br>&nbsp;&nbsp; list.Add("aaaa"); <br>&nbsp;&nbsp; list.Add("bbbb"); <br>&nbsp;&nbsp; Console.ReadLine(); <br>&nbsp;&nbsp; } <br>&nbsp;&nbsp; } <br>&nbsp; }很简单，就是一个ArrayList <br>&nbsp;&nbsp; <br>&nbsp; 运行这个程序(开始执行，不调试)，然后进入Windbg，Attach到这个进程 <br>&nbsp;&nbsp; <br>&nbsp; 2、查看所有堆栈信息 <br>&nbsp; 0:004&gt; !dumpheap -stat <br>&nbsp;&nbsp; MT Count TotalSize Class Name <br>&nbsp; 7910062c 1 12 System.Security.Permissions.SecurityPermission <br>&nbsp; 7918e284 1 16 System.IO.TextReader+SyncTextReader <br>&nbsp; 79102d10 1 20 Microsoft.Win32.SafeHandles.SafeFileMappingHandle <br>&nbsp; 79102cb4 1 20 Microsoft.Win32.SafeHandles.SafeViewOfFileHandle <br>&nbsp; 79101d30 1 20 System.Text.InternalEncoderBestFitFallback <br>&nbsp; 79100a7c 1 20 Microsoft.Win32.SafeHandles.SafeFileHandle <br>&nbsp; 79105cd4 1 24 System.Collections.ArrayList <br>&nbsp; ...... <br>&nbsp; 7912ad90 11 9036 System.Object[] <br>&nbsp; 790fcb30 2083 131492 System.String <br>&nbsp; Total 2202 objects <br>&nbsp; 除了我们的ArrayList外，还有很多其它的系统信息，先不用管它 <br>&nbsp;&nbsp; <br>&nbsp; 3、查看我们的ArrayList的信息 <br>&nbsp; 0:004&gt; !dumpheap -mt 79105cd4 <br>&nbsp;&nbsp; Address MT Size <br>&nbsp; 012a1b88 79105cd4 24 <br>&nbsp; total 1 objects <br>&nbsp; Statistics: <br>&nbsp;&nbsp; MT Count TotalSize Class Name <br>&nbsp; 79105cd4 1 24 System.Collections.ArrayList <br>&nbsp; Total 1 objects <br>&nbsp;&nbsp; <br>&nbsp; 4、查看对应地址内部实际的值 <br>&nbsp; 0:004&gt; !do 012a1b88 <br>&nbsp; Name: System.Collections.ArrayList <br>&nbsp; MethodTable: 79105cd4 <br>&nbsp; EEClass: 79105c28 <br>&nbsp; Size: 24(0x18) bytes <br>&nbsp;&nbsp; (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) <br>&nbsp; Fields: <br>&nbsp;&nbsp; MT Field Offset Type VT Attr Value Name <br>&nbsp; 7912ad90 40008df 4 System.Object[] 0 instance 012a1bb0 _items <br>&nbsp; 791018e0 40008e0 c System.Int32 1 instance 2 _size <br>&nbsp; 791018e0 40008e1 10 System.Int32 1 instance 2 _version <br>&nbsp; 790fc35c 40008e2 8 System.Object 0 instance 00000000 _syncRoot <br>&nbsp; 7912ad90 40008e3 1c0 System.Object[] 0 shared static emptyArray <br>&nbsp;&nbsp; &gt;&gt; Domain:Value 00149c58:012a1ba0 &lt;&lt; <br>&nbsp; 可以看到ArrayList的大小为2，具体的值保存在地址012a1bb0中，是一个System.Object[]类型的数组 <br>&nbsp;&nbsp; <br>&nbsp; 5、查看数组信息 <br>&nbsp; 0:004&gt; !DumpArray 012a1bb0 <br>&nbsp; Name: System.Object[] <br>&nbsp; MethodTable: 7912ad90 <br>&nbsp; EEClass: 7912b304 <br>&nbsp; Size: 32(0x20) bytes <br>&nbsp; Array: Rank 1, Number of elements 4, Type CLASS <br>&nbsp; Element Methodtable: 790fc35c <br>&nbsp; [0] 012a1b50 <br>&nbsp; [1] 012a1b6c <br>&nbsp; [2] null <br>&nbsp; [3] null <br>&nbsp;&nbsp; <br>&nbsp; 6、查看数组内对象的值 <br>&nbsp; 0:004&gt; !do 012a1b50 <br>&nbsp; Name: System.String <br>&nbsp; MethodTable: 790fcb30 <br>&nbsp; EEClass: 790fca90 <br>&nbsp; Size: 26(0x1a) bytes <br>&nbsp;&nbsp; (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) <br>&nbsp; String: aaaa <br>&nbsp; Fields: <br>&nbsp;&nbsp; MT Field Offset Type VT Attr Value Name <br>&nbsp; 791018e0 4000096 4 System.Int32 1 instance 5 m_arrayLength <br>&nbsp; 791018e0 4000097 8 System.Int32 1 instance 4 m_stringLength <br>&nbsp; 790fe534 4000098 c System.Char 1 instance 61 m_firstChar <br>&nbsp; 790fcb30 4000099 10 System.String 0 shared static Empty <br>&nbsp;&nbsp; &gt;&gt; Domain:Value 00149c58:790d81bc &lt;&lt; <br>&nbsp; 7912b1d8 400009a 14 System.Char[] 0 shared static WhitespaceChars <br>&nbsp;&nbsp; &gt;&gt; Domain:Value 00149c58:012a16f0 &lt;&lt;&nbsp; <br>＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝＝</p>
<p>windbg使用小总结<br>【抓dump】<br>1、一般抓法<br>adplus -hang -p 3230 -quiet 抓3230 pid进程，hang模式，相当于把那个进程暂停住，取内存快照<br>adplus -crash -pn w3wp -quiet 抓w3wp进程，crash模式，当那个进程崩溃结束的时候自动抓取当时的内存<br>adplus -hang -iis -quiet 抓IIS相关进程，包括其上host的web应用，以及iis自身<br>2、抓window服务<br><a href="http://support.microsoft.com/kb/824344/zh-cn">http://support.microsoft.com/kb/824344/zh-cn</a><br>3、远程抓<br><a href="http://blog.joycode.com/tingwang/archive/2006/08/11/79763.aspx">http://blog.joycode.com/tingwang/archive/2006/08/11/79763.aspx</a><br>4、抓蓝屏和死机的dump<br>电脑无故重启或者蓝屏会在C:\WINDOWS\Minidump\下保存一个minidump，但是这个minidump可用的命令很少，一般只打!analyze &#8211;v看到是哪个进程引起的，还有相关的驱动模块就基本定位问题了。<br>5、IIS回收的时候抓<br><a href="http://blog.yesky.com/blog/omakey/archive/2006/12/17/1618015.html">http://blog.yesky.com/blog/omakey/archive/2006/12/17/1618015.html</a><br>6、计划任务抓<br>比如一个进程起来后不知道它什么时候会意外崩溃，可以在计划任务里用crash里抓，当那个进程意外终止的时候，cdb可以直接附加上去，抓取当时的dump，如果要抓一些会自动重启的进程，而且要抓每次重启前的dump，可以参考附录里一节。</p>
<p>【常用命令】<br>1、先path C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727，把.net路径设置为path环境变量，一遍在windbg里可以直接.load sos，而不必.load C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sos.dll<br>2、ld demo，加载你程序的pdb文件，调试.net程序一般要把kernel32和mscorwks的符号加载上，关于这两个东西大家可以查资料，尤其是后者有哪些函数可以多了解一些。<br>3、在windbg的file/symbol file path对话框里输入以下文字，以便自动加载和下载符号<br>C:\WINDOWS\Symbols;d:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\symbols;.sympath SRV*d:\localsymbols*http://msdl.microsoft.com/download/symbols<br>其中有windows、.net2.0和自动从网上下载的调试符号，注意根据自己的情况适当修改目录</p>
<p>【调试死锁】<br>1、!syncblk，查看哪些线程拿到了锁<br>2、~67e!clrstack 跳到某个拿到锁的线程看它正在干什么操作，迟迟不肯释放锁<br>3、!runaway 查看这个占有锁的线程运行了多长时间。<br>4、~*e!clrstack查看所有线程的托管堆栈，看看哪些是正在等待锁的，比如hang在System.Threading.Monitor.Enter(System.Object) <br>5、~136s选择该线程，显示如下<br>0:000&gt; ~136s eax=00005763 ebx=08deeb5c ecx=03eff0d4 edx=5570ab69 esi=08deeb5c edi=7ffd6000 eip=7c95ed54 esp=08deeb10 ebp=08deebb8 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!KiFastSystemCallRet: 7c95ed54 c3 ret<br>找到ecx寄存器的值，复制后ctrl+f，向上查找，会找到!syncblk的地方，如下<br>0:000&gt; !syncblk Index SyncBlock MonitorHeld Recursion Owning Thread Info SyncBlock Owner 1906 03ee4be4 5 1 03ee8f88 22c8 67 185e2ef0 System.Object 5390 052ca39c 3 1 05292b30 1dd4 49 1060d3ac System.Object 9372 0530702c 15 1 0012d3a8 1aa8 80 185e7704 System.Object 11428 03eff0d4 35 1 053b8fa8 169c 120 166acd98 System.Object 15278 0531c6b4 61 1 06bc1430 26d8 86 1a5bea88 System.Object<br>可以看到136线程等待的锁被120号线程占着不放（格式有点乱，凑合看），<br>6、有时候通过ecx寄存器找锁不是很确定，可以用~* kb来把所有线程堆栈打出来，然后根据!syncblk出来的同步快的值去搜索大概有多少个线程在等那个锁。因为同样是等待锁，可等的状态不一样，有的在Q里，有的锁已经升级，有的去尝试去拿锁了，所以不一定当时ecx寄存器指向那块内存，具体如何找到某个正在等待锁的线程等待的锁的内存地址，以及它正等待的这个锁被哪个线程拿着，我还没琢磨出规律来，但一般情况下，如果有其它同步对象的话，更难查。.net里用我上面说的几步就能查出锁的问题了。</p>
<p><br>【内存泄漏】<br>1、!dumpheap -stat看看哪些对象个数最多，占内存最大，<br>2、找到某个格式比较多的对象，可以看它的方法表，然后用!dumpheap -mt 66398fa4去随机找几个对象的地址<br>3、用!do 1e5a22bc命令去查看几个对象的状态，属性的值等，看看正常不正常<br>4、用!gcroot -nostacks 1e5a22bc去查看几个对象的根正常不正常，如果有些对象的根不是自己预先设计的那样，很可能被自己没想到的对象强引用了，所以GC无法回收它，就泄漏了。<br>【CPU百分百】<br>主要用几个计数器和!runaway命令，具体见以下链接<br><a href="http://www.cnblogs.com/onlytianc">http://www.cnblogs.com/onlytianc</a> ... 7/06/03/769307.html<br>【线程池耗尽】<br>!threadpool 能看到完成端口，线程池工作线程和timer回调各占线程池的情况。<br>【其它】<br>1、!eestack -short -ee查看所有重要(获取锁的，托管的，停止并允许回收的)线程的dumpstack，差不多相当于~*e!dumpstack<br>2、.time 可以看到进程跑了多少时间<br>3、!dso 查看当前线程里有哪些对象，分析内存泄漏问题也许会用到<br>【小结】<br>要想很好的用windbg排查.net问题，首先要了解一些clr宿主的基础知识，以及IL的一些基础，还有简单的寄存器和汇编尝试，再就是有个好的思路，最后就是经验和对代码逻辑的理解。</p>
<p><br>【附录：写了一个自动抓dump的工具，可在程序异常退出的时候抓dump】<br>【使用方法】<br>1、先在cmd下运行以下命令确保计划任务开着<br>net start "task scheduler"<br>2、执行以下命令安排自动抓包<br>at 13:27 d:\myapp\autodump\processmon.exe<br>其中计划启动的时间和自动抓包的程序路径要根据情况设置，计划启动之前当前用户一定要注销。<br>【相关配置】<br>&lt;appSettings&gt;<br>&nbsp; &lt;add key="adplusPath" value="D:\MyApp\Debugging\adplus.vbs"/&gt;&lt;!--adplus的路径--&gt;<br>&nbsp; &lt;add key="ProcessName" value="w3wp"/&gt;&lt;!--要抓dump的进程的名字，可用部分名字，不用完整的--&gt;<br>&lt;/appSettings&gt;</p>
<p><br>【源码】</p>
<p><br>using System;<br>using System.Collections.Generic;<br>using System.Diagnostics;<br>using System.Threading;</p>
<p>namespace ProcessMon<br>{<br>&nbsp;&nbsp;&nbsp; class Program<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static readonly List&lt;int&gt; _dumpPIDs = new List&lt;int&gt;();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static readonly string _processName = System.Configuration.ConfigurationManager.AppSettings["ProcessName"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static readonly string _adplusPath = System.Configuration.ConfigurationManager.AppSettings["adplusPath"];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Main(string[] args)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(true)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("..");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThreadProc();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.Sleep(10000);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void ThreadProc()<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach(Process vProcess in Process.GetProcesses())<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&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;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string&nbsp; processName&nbsp; =&nbsp; vProcess.ProcessName.ToLower();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (processName.IndexOf(_processName) &gt;= 0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("{0}-{1}", vProcess.ProcessName, vProcess.Id);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (_dumpPIDs.Contains(vProcess.Id))<br>&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; continue;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; _dumpPIDs.Add(vProcess.Id);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DumpProcessDeg d = DumpProcess;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; d.BeginInvoke(vProcess.Id, null, null);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DumpProcess(vProcess.Id);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(Exception ex)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine(ex);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private delegate void DumpProcessDeg(int pid);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static&nbsp; void&nbsp; DumpProcess(int pid)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ProcessStartInfo&nbsp; Info&nbsp; =&nbsp; new&nbsp; System.Diagnostics.ProcessStartInfo();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Info.FileName = _adplusPath;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Info.Arguments = string.Format("-crash -p {0} -quiet",pid);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Info.WorkingDirectory&nbsp; =&nbsp; "C:\\";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Process&nbsp; Proc&nbsp; ;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Proc&nbsp; =&nbsp; Process.Start(Info);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch(System.ComponentModel.Win32Exception&nbsp; e)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("系统找不到指定的程序文件。\r{0}",&nbsp; e);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Proc.EnableRaisingEvents = true;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("外部程序的开始执行时间：{0}",&nbsp; Proc.StartTime);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Proc.WaitForExit(60000);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(Proc.HasExited&nbsp; ==&nbsp; false){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("由主程序强行终止外部程序的运行！");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Proc.Kill();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("由外部程序正常退出！");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("外部程序的结束运行时间：{0}",&nbsp; Proc.ExitTime);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("外部程序在结束运行时的返回值：{0}",&nbsp; Proc.ExitCode);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>} </p>
<p>===============<br>补充几个命令:</p>
<p>1、!analyze -v ：用于分析挂掉线程的详细情形，错误原因。</p>
<p>2、!locks ：列出全部资源使用情况。</p>
<p>3、!locks -v 0x???????? ：特定地址的死锁分析。</p>
<p>4、!thread 0x????????：特定线程详情。</p>
<p>5、.thread 0x????????：转到某个线程堆栈。<br>&nbsp;</p>
<img src ="http://www.cppblog.com/tgh621/aggbug/131525.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2010-10-27 17:38 <a href="http://www.cppblog.com/tgh621/archive/2010/10/27/131525.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>sse2指令集</title><link>http://www.cppblog.com/tgh621/archive/2010/08/20/124113.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Fri, 20 Aug 2010 09:53:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2010/08/20/124113.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/124113.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2010/08/20/124113.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/124113.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/124113.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;1移动指令:1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Movapsmovaps XMM,XMM/m128 movaps XMM/128,XMM把源存储器内容值送入目的寄存器,当有m128时,必须对齐内存16字节,也就是内存地址低4位为0.2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Movupsmovups...&nbsp;&nbsp;<a href='http://www.cppblog.com/tgh621/archive/2010/08/20/124113.html'>阅读全文</a><img src ="http://www.cppblog.com/tgh621/aggbug/124113.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2010-08-20 17:53 <a href="http://www.cppblog.com/tgh621/archive/2010/08/20/124113.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Shell_NotifyIcon在任务栏添加一个图标</title><link>http://www.cppblog.com/tgh621/archive/2009/02/26/75001.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Thu, 26 Feb 2009 10:44:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2009/02/26/75001.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/75001.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2009/02/26/75001.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/75001.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/75001.html</trackback:ping><description><![CDATA[memset(&amp;ntfData,0,sizeof(ntfData));<br>&nbsp;ntfData.cbSize = sizeof(NOTIFYICONDATA);<br>&nbsp;ntfData.hWnd = this-&gt;GetSafeHwnd();<br>&nbsp;if(m_hCurrentIcon)<br>&nbsp;&nbsp;ntfData.hIcon = m_hCurrentIcon;<br>&nbsp;ntfData.uFlags = NIF_MESSAGE|NIF_ICON|NIF_TIP;<br>&nbsp;ntfData.uID = IDR_MAINFRAME;<br>&nbsp;memcpy(ntfData.szTip, m_strTipNormal, sizeof(ntfData.szTip)-1);<br>&nbsp;ntfData.uCallbackMessage = WM_COMMANDNOTIFY;<br>&nbsp;Shell_NotifyIcon(NIM_ADD, &amp;ntfData);
<img src ="http://www.cppblog.com/tgh621/aggbug/75001.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2009-02-26 18:44 <a href="http://www.cppblog.com/tgh621/archive/2009/02/26/75001.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MFC窗口的清除过程[转]</title><link>http://www.cppblog.com/tgh621/archive/2009/02/11/73433.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Wed, 11 Feb 2009 02:50:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2009/02/11/73433.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/73433.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2009/02/11/73433.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/73433.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/73433.html</trackback:ping><description><![CDATA[<p>对于vc++初学者来说,总觉得窗口对象的清除过程有些莫名其妙.在程序中看不到对delete的显式调用,这似乎违反了c++中有关初始化和清除的规则.那么,程序是怎样取消一个窗口对象?</p>
<p>要消除窗口对象,必须清楚窗口对象的构成.在一个通常的程序中，先创建c++窗口对象,然后由Windows创建实际的窗口结构,并返回句柄与c++对象连接.也就是说,窗口对象包含c++窗口对象和Windows窗口对象,两者通过句柄HWND联系.</p>
<p>现在,让我们看看"正规"的窗口对象清除流程.所谓对象的清除是指释放对象所占的资源,窗口对象中Windows窗口对象占有的是系统资源,c++对象占有的是内存资源.释放系统资源相对要简单一些:调用虚函数DestroyWindow删除Windows窗口对象.如果DestroyWindow删除的是父窗口,Windows会自动为子窗口调用DestroyWindow.一般来说,程序不必调用DestroyWindow.因为当用户关闭窗口时,Windows便发送WM_CLOSE消息,WM_CLOSE的缺省消息处理函数CWnd::OnClose调用DestroyWindow.</p>
<p>到这时,清除工作已经完成了一半,屏幕上的窗口已经不见了!但是别忘了,在内存中还有一个c++窗口对象.让我们再看看c++对象清除的过程:当窗口被取消时,窗口最后发送的一个消息是WM_NCDESTROY.它缺省的消息处理函数CWnd::OnNcDestroy把c++窗口对象与句柄HWND分离,并调用一个很重要的虚函数PostNcDestroy.这个函数是搞清窗口对象清除的关键.Cwnd中的PostNcDestroy什么都不做.有些MFC窗口类会重载它,并加入delete this代码删除c++对象.这些窗口类常常是以new操作符建立在堆中的.由于重载了PostNcDestroy,使窗口有自动清除功能.因此,我们不用关心清除问题了.另外的一些MFC窗口类一般是以变量形式创建的,MFC没有为也没必要为它们重载PostNcDestroy函数.</p>
<p>不具备自动清除功能的窗口类,一般在堆栈中创建或嵌入于其它c++对象中:</p>
<p>所有标准的Windows控件类(如CStatic, CEdit, CListBox等等)</p>
<p>由CWnd类直接派生出来的子窗口对象(如用户定制的控件)</p>
<p>拆分窗口类(CSplitterWnd)</p>
<p>缺省的控制条类(CControlBar的派生类)</p>
<p>对话框类(CDialog)在堆栈上创建的模态对话框类</p>
<p>所有的Windows通用对话框(除CFindReplaceDialog)</p>
<p>由ClassWizard创建的对话框</p>
<p>具有自动清除功能的窗口类,一般在堆中创建:</p>
<p>主框架窗口类(直接或间接从CFrameWnd类派生)</p>
<p>视图类(直接或间接从CView类派生)</p>
<p>从某种程度上来说,MFC的"服务到家"使初学者有些找不着北.不过,不得不承认:MFC干的很漂亮!</p>
<p>谈到这里,我们应该明白c++里一条重要的准则:用DestroyWindow清除窗口对象,不要用"delete".</p>
<p>对于不具备自动清除功能的窗口类使用"delete"时,"delete"先调用析构函数里的DestroyWindow,由于在析构函数中,虚机制不起作用,这里只能调用本地版本(Cwnd类)DestroyWindow函数,显然这不是我们想要的.对于有自动清除功能的窗口类,好象问题更严重一点,前面提到了重载的PostNcDestroy已经含有了"delete this",这样c++对象就被释放了两次.</p>
<p>很多人认为,vc++同vb一样,是一个完全可视化的产品,不用在看c++的书了.通过上面对窗口对象的清除的介绍,可以发现,Windows程序是与Windows紧密结合的,而且牵涉到很多c++的知识(如虚函数、析构函数、new操作符等).要对vc++有进一步理解,必须理解Windows机制,深入学习c++.</p>
<img src ="http://www.cppblog.com/tgh621/aggbug/73433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2009-02-11 10:50 <a href="http://www.cppblog.com/tgh621/archive/2009/02/11/73433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]可在运行时编辑的加速键表</title><link>http://www.cppblog.com/tgh621/archive/2009/01/12/71821.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Mon, 12 Jan 2009 09:22:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2009/01/12/71821.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/71821.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2009/01/12/71821.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/71821.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/71821.html</trackback:ping><description><![CDATA[＊＊＊简　介＊＊＊
<p>　　本文首先简要介绍了一下<a href="http://dev.21tx.com/os/windows/" target=_blank><u><font color=#0000ff>Windows</font></u></a>中的几个与<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表有关的API函数及结构。然后对在WIN32位程序中实现<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表进行了探讨，分别就API下的程序设计及MFC下的程序设计进行了叙述。<br>　　对于运行时可编辑的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表仅在MFC下进行了详细描述。包括其实现原理，并引导大家建立了一个用于编辑<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>的对话框，含详细的代码。关于在API下实现运行时的可编辑<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表不再叙述，可参考MFC下的代码。<br>　　我们通常希望将编辑过的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表保存起来，以便下次运行程序时保持我们编辑后的风格。在本文的最后，介绍了如何将<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表保存至文件中，并从文件中读取我们保存的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表。你若是有意将<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表保存至<a href="http://school.21tx.com/os/regtable/" target=_blank><u><font color=#0000ff>注册表</font></u></a>或其它什么地方，可参考其它的有关资料。本人建议保存至文件比较恰当。<br>　　本文介绍的所有方法及代码都是在 Windows98SE + Microsoft Visual <a href="http://dev.21tx.com/language/c/" target=_blank><u><font color=#0000ff>C++</font></u></a> 6.0 中进行编制和调试的。　</p>
<p><br>　　一、与<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表有关的几个API函数和结构。</p>
<p>　　操作<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表使用的几个API函数（关于这几个函数的详细说明请参考有关书籍）：<br>　　HACCEL LoadAccelerators(HINSTANCE hInstance, LPCTSTR lpTableNAme);<br>　　LoadAccelerators函数从程序的资源中加载一个<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表，加载成功后返回一个加速健表的句柄。其中：<br>　　hInstance　　应用程序的实例句柄。<br>　　lpTableName　指向<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表名称字符串的指针。</p>
<p>　　HACCEL CreateAcceleratorTable(LPACCEL lpaccl, int cEntries);<br>　　CreateAcceleratorTable函数根据一个ACCEL结构数组创建一个<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表。该函数与LoadAccelerators不同的是：LoadAccelerators函数加载的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表在程序结束后系统会自动将该<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表从内存中清除，但CreateAcceleratorTable函数创建的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表需要使用函数DestoryAceleratorTable函数进行清除。<br>　　lpaccl　　　一个指向ACCEL结构数组的指针。<br>　　cEntries　　数组中元素的个数。</p>
<p>　　BOOL　DestoryAcceleratorTable(HACCEL hAccel);<br>　　DestoryAcceleratorTable函数清除由CreateAcceleratorTable函数创建的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表，成功则返回TRUE。其中：<br>　　hAccel　　　需要清除的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表句柄。</p>
<p>　　int TranslateAccelerator(HWND hWd, HACCEL hAccTable, LPMSG lpMsg);<br>　　TranslateAccelerator函数负责翻译<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>。其中：<br>　　hWnd　　　　 窗口句柄，翻译后的消息将被发往该窗口<br>　　hAccTable　　<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表句柄。<br>　　lpMsg　　　　指向MSG结构的指针。</p>
<p>　　ACCEL结构的定义：<br>typedef struct tagACCEL{<br>&nbsp;&nbsp;&nbsp; BYTE&nbsp;&nbsp;&nbsp; fVirt;<br>&nbsp;&nbsp;&nbsp; WORD&nbsp;&nbsp;&nbsp; key;<br>&nbsp;&nbsp;&nbsp; WORD&nbsp;&nbsp;&nbsp; cmd;<br>}ACCEL,*LPACCEL;</p>
<p>其中：<br>　　fVirt　　　<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>的标记。<br>　　key　　　　键的代码。如fVirt成员包含FVIRTKEY标志，则key指一个虚键码，否则是一个ASCII码。<br>　　cmd　　　　命令ID号，该参数将被放入WM＿COMMAND或WM＿SYSCOMMAND消息的wParam参数的低位字发至窗口。</p>
<p><br>　　二、在windows下如何使用<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表。</p>
<p>　　在window下使用<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表一般有两种方法：1，创建一个<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>资源，在程序中使用API函数LoadAccelerators来将<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表加载入内存。并在消息循环中使用API函数TranslateAccelerator来翻译该<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表。2、在程序中填充一个ACCEL数组。然后调用API函数CreateAcceleratorTable来创建加速表，翻译<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>同上，但不要忘记在退出程序前使用API函数DestoryAcceleratorTable来清除它。下面分别给出一个例子：</p>
<p>/*例1:使用LoadAccelerators。<br>　假设你已经建立了一个<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>资源，ID为IDR＿ACCEL。<br>　假设你已经定义了初始化函数InitApplication(HINSTANCE hInstance,int nCmdShow),<br>　该函数执行注册窗口类和创建窗口操作。<br>*/<br>#include &lt;windows.h&gt;<br>#include "rc/resource.h"<br>BOOL InitApplication(HINSTANCE hInstance,int nCmdShow);</p>
<p>int APIENTRY WinMain(HINSTANCE hInstance,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HINSTANCE hPrevInstance,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LPSTR&nbsp;&nbsp;&nbsp;&nbsp; lpCmdLine,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nCmdShow) <br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; MSG msg;<br>&nbsp;&nbsp;&nbsp;&nbsp; HANDLE hAccelTable;<br>&nbsp;&nbsp;&nbsp;&nbsp; // 初始化应用程序，并生成主窗口.<br>&nbsp;&nbsp;&nbsp;&nbsp; if (!InitApplication(hInstance, nCmdShow))<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 初始化失败<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //使用函数LoadAccelerators从程序资源中加载<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_ACCEL));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 取得并分发消息直到接收到 WM_QUIT 消息.<br>&nbsp;&nbsp;&nbsp;&nbsp; while (GetMessage(&amp;msg, NULL, 0, 0))<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在分发消息前首先试着用<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表进行翻译，如果是一个<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>则由<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //TranslateAccelerator函数进行翻译，不再继续处理该消息。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!TranslateAccelerator(msg.hwnd, hAccelTable, &amp;msg))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TranslateMessage(&amp;msg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DispatchMessage(&amp;msg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; return msg.wParam;&nbsp; // Returns the value from PostQuitMessage <br>} <br>&nbsp;</p>
<p><br>/*例2:使用CreateAcceleratorTable。<br>　假设你已经定义了初始化函数InitApplication(HINSTANCE hInstance,int nCmdShow),<br>　该函数执行注册窗口类和创建窗口操作。<br>*/</p>
<p>#include &lt;windows.h&gt;<br>#include "rc/resource.h"</p>
<p>＃define ID_CMD_A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000230<br>＃define ID_CMD_B&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000231<br>＃define ID_CMD_C&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000232<br>＃define ID_CMD_D&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000233<br>＃define ID_CMD_E&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000234<br>＃define ID_CMD_F&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000235<br>＃define ID_CMD_G&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0x00000236</p>
<p>//定义了七个<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>,请在消息回调函数中处理这七个命令ID。<br>static ACCEL accel[]={<br>&nbsp;&nbsp;&nbsp; {FVIRTKEY|FCONTROL,VK_F5,ID_CMD_A},<br>&nbsp;&nbsp;&nbsp; {FVIRTKEY|FCONTROL,VK_F6,ID_CMD_B},<br>&nbsp;&nbsp;&nbsp; {FVIRTKEY|FCONTROL,VK_HOME,ID_CMD_C},<br>&nbsp;&nbsp;&nbsp; {FVIRTKEY|FCONTROL,VK_END,ID_CMD_D},<br>&nbsp;&nbsp;&nbsp; {FVIRTKEY|FCONTROL,"G",ID_CMD_E},<br>&nbsp;&nbsp;&nbsp; {FVIRTKEY|FCONTROL,VK_SPACE,ID_CMD_F},<br>&nbsp;&nbsp;&nbsp; {FVIRTKEY|FCONTROL,"K",ID_CMD_G},<br>};&nbsp;&nbsp;&nbsp; </p>
<p>BOOL InitApplication(HINSTANCE hInstance,int nCmdShow);</p>
<p>int APIENTRY WinMain(HINSTANCE hInstance,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HINSTANCE hPrevInstance,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LPSTR&nbsp;&nbsp;&nbsp;&nbsp; lpCmdLine,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nCmdShow) <br>{<br>&nbsp;&nbsp;&nbsp;&nbsp; MSG msg;<br>&nbsp;&nbsp;&nbsp;&nbsp; HANDLE hAccelTable;<br>&nbsp;&nbsp;&nbsp;&nbsp; // 初始化应用程序，并生成主窗口.<br>&nbsp;&nbsp;&nbsp;&nbsp; if (!InitApplication(hInstance, nCmdShow))<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 初始化失败<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //使用函数CreateAcceleratorTable从数组accel中加载<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hAccelTable = CreateAcceleratorTable(accel, sizeof(accel)/sizeof(ACCEL));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 取得并分发消息直到接收到 WM_QUIT 消息.<br>&nbsp;&nbsp;&nbsp;&nbsp; while (GetMessage(&amp;msg, NULL, 0, 0))<br>&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //在分发消息前首先试着用<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表进行翻译，如果是一个<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>则由<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //TranslateAccelerator函数进行翻译，不再继续处理该消息。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!TranslateAccelerator(msg.hwnd, hAccelTable, &amp;msg))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TranslateMessage(&amp;msg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DispatchMessage(&amp;msg);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp; //删除<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表<br>&nbsp;&nbsp;&nbsp;&nbsp; DestoryAcceleratorTable(hAccelTable);<br>&nbsp;&nbsp;&nbsp;&nbsp; return msg.wParam;&nbsp; // Returns the value from PostQuitMessage <br>} <br>&nbsp;</p>
<p><br>　　在MFC程序设计中，有关于<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表的操作已经被CFrameWnd类进行了封装。通常，我们的程序的主框架类CMainFrame从CFrameWnd类派生（SDI界面程序），或者从CMDIFrameWnd类派生（MDI界面程序），而CMDIFrameWnd类也是从CFrameWnd类派生的。所以，我们并不用去关心那个资源号为IDR_MAINFRAME的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表是如何加载的，如果你只是需要一个这样的静态的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表的话。<br>　　那么我们能不能使用自己的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表呢？答案是：可以的。<br>　　创建<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表的方法同前。由于在MFC中，WinMain函数被隐藏，我们不能直接修改WinMain函数，所以，<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表的创建将在CMainFrame的OnCreate函数中创建。<br>　　1、在CMainFrame类中添加一个HACCEL类型保护成员变量：m_hMyAccel。在构造函数中初始化为NULL。<br>　　2、在CMainFrame::OnCreate函数的 &#8220;return 0;&#8221;句前增加创建<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表的代码。返回的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表句柄保存在m_hMyAccel中：</p>
<p>　　　方法1：(IDR＿MYACCEL为你定义的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表资源号)<br>　　　m_hMyAccel=LoadAccelerators(AfxGetApp()-&gt;m_hInstance,MAKEINTRESOURCE(IDR_MYACCEL));</p>
<p>　　　方法2：(accel同前面的例子一样在本文件的开头部分进行定义)<br>　　　m_hMyAccel=CreateAcceleratorTable(accel,sizeof(accel)/sizeof(ACCEL));</p>
<p>　　3、如果是使用CreateAccelTable函数创建的，则重载虚拟函数DestoryWindow()。在该函数的&#8220;return CMDIFrameWnd::DestroyWindow();&#8221;前增加如下代码:</p>
<p>&nbsp;&nbsp;&nbsp; if(m_hMyAccel!=NULL){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DestroyAcceleratorTable(m_hMyAccel);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_hMyAccel=NULL;<br>&nbsp;&nbsp;&nbsp; }</p>
<p>　　通过前面的三步，<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表已经能正确地被创建和删除了，但是它还没有工作。下面就是要让我们刚才所创建的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表工作起来。<br>　　在API程序设计中大家已经知道了<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>是如何工作的。也就是在消息还没有分发出去之前先检查是不是一个<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>。我们通过API函数TranslateAccelerator来实现。在MFC中，消息机制已经被封装了，我们不能去修改消息循环。但是，框架在分发消息前会调用虚拟函数PerTranslateMessage，并且如果该函数返回TRUE，则不再处理该消息。这正是我们所需要的。<br>　　让我们再回到CMainFrame类，生成PerTranslateMessage函数的覆盖版本。修改函数体如下：</p>
<p>BOOL CMainFrame::PreTranslateMessage(MSG* pMsg) <br>{<br>&nbsp;&nbsp;&nbsp; // TODO: Add your specialized code here and/or call the base class<br>&nbsp;&nbsp;&nbsp; if(m_hMyAccel&amp;&amp;TranslateAccelerator(m_hWnd, m_hMyAccel, pMsg))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return TRUE;<br>&nbsp;&nbsp;&nbsp; return CMDIFrameWnd::PreTranslateMessage(pMsg);<br>}</p>
<p>　　好了，现在我们的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>已经能正常地工作了。以上方法用在其它窗口同样有效（如对话框中，你不妨在对话框中试试）。</p>
<p><br>　　三、可在运行时编辑的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表。</p>
<p>　　通过上面的叙述，你可能已经对可编辑的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表有了一定的轮廓。其实其实现思想很简单：只要对一个ACCEL数组进行修改，然后重新生成<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表就行了。<br>　　为了区分命令，我们为每个命令取一个名字。在CMainFrame类定义的前面加上下面的一个结构定义：</p>
<p>typedef struct{<br>&nbsp;&nbsp;&nbsp; char cCmd[32];<br>&nbsp;&nbsp;&nbsp; ACCEL accel;<br>}ACCELITEM,*LPACCELITEM;</p>
<p>　　我们将使用该结构来保存<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表的数据。在CMainFrame类中添加两个保护成员变量：</p>
<p>&nbsp;&nbsp;&nbsp; LPACCELITEM m_lpAccel;<br>&nbsp;&nbsp;&nbsp; DWORD m_dwAccelCount;</p>
<p>　　在构造函数中将m_lpAccel初始化为NULL，m_dwAccelCount初始化为0。在数组accel的定义下面增加一个字符串数组的静态变量的定义（用来指定命令的名称,请仿照下面自己定义，个数多少不限，字符串长度不要超过31个字符）：</p>
<p>static char strCmd[][32]={<br>&nbsp;&nbsp;&nbsp; "Command One",<br>&nbsp;&nbsp;&nbsp; "Command Two",<br>&nbsp;&nbsp;&nbsp; "Command Three",<br>&nbsp;&nbsp;&nbsp; "Command Four",<br>&nbsp;&nbsp;&nbsp; "Command Five"<br>};</p>
<p><br>　　在CMainFrame类中添加一个保护成员函数LoadAccel(),该函数用来将<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表装入，定义如下：</p>
<p>BOOL CMainFrame::LoadAccel()<br>{<br>&nbsp;&nbsp;&nbsp; ASSERT(m_hActAccel==NULL);<br>&nbsp;&nbsp;&nbsp; ASSERT(m_lpAccel==NULL);<br>&nbsp;&nbsp;&nbsp; m_dwAccelCount=sizeof(accel)/sizeof(ACCEL);<br>&nbsp;&nbsp;&nbsp; m_lpAccel=new ACCELITEM[m_dwAccelCount];<br>&nbsp;&nbsp;&nbsp; memset(m_lpAccel,0,sizeof(ACCELITEM)*m_dwAccelCount);<br>&nbsp;&nbsp;&nbsp; DWORD dwCmdStr=sizeof(strCmd)/sizeof(char[32]);<br>&nbsp;&nbsp;&nbsp; for(DWORD dw=0;dw&lt;m_dwAccelCount;dw++){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_lpAccel[dw].accel=accel[dw];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcpy(m_lpAccel[dw].cCmd,dw&lt;dwCmdStr?strCmd[dw]:"Command Unknow");<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; m_hActAccel=CreateAcceleratorTable(accel,m_dwAccelCount);<br>&nbsp;&nbsp;&nbsp; return TRUE;</p>
<p>}</p>
<p>　　删除OnCreate函数中原来创建<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表的代码，改成对LoadAccel()的调用：</p>
<p>&nbsp;&nbsp;&nbsp; LoadAccel();</p>
<p>　　在CMainFrame的析构函数中增加以下内容：</p>
<p>&nbsp;&nbsp;&nbsp; if(m_lpAccel)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delete[] m_lpAccel;</p>
<p>　　为了能够对<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>数据进行编辑，我们在工程中添加一个对话框资源，ID为&#8220;IDD_ACCELEDIT&#8221;，在对话框上放置三个成组框（Group Box），左边一个标题为&#8220;命令&#8221;；中间一个标题为&#8220;组合键&#8221;；右边一个标题为&#8220;虚键值&#8221;。在左边成组框中放置一个列表框（List Box），ID为&#8220;IDC_LST_CMD&#8221;。在中间成组框中放置三个核选框（Check Box），上下排列。上面的核选框的ID为&#8220;IDC_CHK_ALT&#8221;，标题为&#8220;Alt&#8221;，并勾选&#8220;Group&#8221;属性；中间的核选框的ID为&#8220;IDC_CHK_SHIFT&#8221;，标题为&#8220;Shift&#8221;，取消&#8220;Group&#8221;属性；下面的核选框的ID为&#8220;IDC_CHK_CTRL&#8221;，标题为&#8220;Ctrl&#8221;，取消&#8220;Group&#8221;属性。在右边的成组框中放置一个列表框，ID为&#8220;IDC_LST_KEY&#8221;。调整各个控件的尺寸及位置至合适。设置控件的TAB跳转顺序，从左至右、从上到下。依次为：左成组框、IDC_LST_CMD列表框、中成组框、IDC_CHK_ALT核选框、IDC_CHK_SHIFT核选框、IDC_CHK_CTRL核选框、右成组框、IDC_LST_KEY列表框、OK按钮、CANCEL按钮。<br>　　打开类向导，为对话框新建一个类，类名为&#8220;CDlgEditAccel&#8221;。为两个列表框和三个核选框映射变量，如下：<br>　　控件ID:ID_LST_CMD；类型:Control/CListBox；名称:m_LstCmd；<br>　　控件ID:ID_LST_KEY；类型:Control/CListBox；名称:m_LstKey；<br>　　控件ID:ID_CHK_ALT；类型:Control/CButton；名称:m_ChkAlt；<br>　　控件ID:ID_CHK_SHIFT；类型:Control/CButton；名称:m_ChkShift；<br>　　控件ID:ID_CHK_CTRL；类型:Control/CButton；名称:m_ChkCtrl；</p>
<p>　　在类CDlgEditAccel的定义前加入下面的定义：<br>typedef struct tag_KeyName{<br>&nbsp;&nbsp;&nbsp; CString m_strName;<br>&nbsp;&nbsp;&nbsp; WORD m_wID;<br>&nbsp;&nbsp;&nbsp; tag_KeyName(CString str,WORD id){m_strName=str,m_wID=id;}<br>}KEYNAME,*LPKEYNAME;</p>
<p>typedef struct tag_ActAccel{<br>&nbsp;&nbsp;&nbsp; CString m_strCmd;<br>&nbsp;&nbsp;&nbsp; ACCEL m_Accel;<br>}ACTACCEL,*LPACTACCEL;</p>
<p>class CActAccelList<br>{<br>public:<br>&nbsp;&nbsp;&nbsp; CActAccelList();<br>&nbsp;&nbsp;&nbsp; ~CActAccelList();</p>
<p>protected:<br>&nbsp;&nbsp;&nbsp; LPACTACCEL m_lpActAccel;<br>&nbsp;&nbsp;&nbsp; int m_iCount;</p>
<p>public:<br>&nbsp;&nbsp;&nbsp; ACTACCEL&amp; operator[](int index);<br>&nbsp;&nbsp;&nbsp; BOOL SetSize(int iSize);<br>&nbsp;&nbsp;&nbsp; int GetSize();<br>};</p>
<p><br>　　在文件DlgEditAccel.cpp文件中定义类CActAccelList的成员函数，代码如下：<br>CActAccelList::CActAccelList()<br>{<br>&nbsp;&nbsp;&nbsp; m_lpActAccel=NULL;<br>&nbsp;&nbsp; m_iCount=0;<br>}</p>
<p>CActAccelList::~CActAccelList()<br>{<br>&nbsp;&nbsp;&nbsp; if(m_iCount&gt;0&amp;&amp;m_lpActAccel)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delete[] m_lpActAccel;<br>}</p>
<p>int CActAccelList::GetSize()<br>{<br>&nbsp;&nbsp;&nbsp; return m_iCount;<br>}</p>
<p>ACTACCEL&amp; CActAccelList::operator []( int index)<br>{<br>&nbsp;&nbsp;&nbsp; if(!(index&gt;=0&amp;&amp;index&lt;m_iCount))<br>&nbsp;&nbsp;&nbsp; AfxThrowMemoryException();<br>&nbsp;&nbsp;&nbsp; return m_lpActAccel[index];<br>}</p>
<p>BOOL CActAccelList::SetSize(int iSize)<br>{<br>&nbsp;&nbsp;&nbsp; if(iSize&lt;0)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br>&nbsp;&nbsp;&nbsp; if(m_iCount&gt;0&amp;&amp;m_lpActAccel)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; delete[] m_lpActAccel;<br>&nbsp;&nbsp;&nbsp; m_iCount=0;<br>&nbsp;&nbsp;&nbsp; m_lpActAccel=new ACTACCEL[iSize];<br>&nbsp;&nbsp;&nbsp; if(m_lpActAccel==NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br>&nbsp;&nbsp;&nbsp; m_iCount=iSize;<br>&nbsp;&nbsp;&nbsp; return TRUE;<br>}</p>
<p><br>　　在DlgAccelEdit.cpp文件头部定义全局变量－－数组key：<br>static KEYNAME key[]={<br>&nbsp;&nbsp;&nbsp; KEYNAME("Left mouse button",VK_LBUTTON),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Right mouse button",VK_RBUTTON),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Control-break processing",VK_CANCEL),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Middle mouse button",VK_MBUTTON),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Back Space",VK_BACK),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Tab",VK_TAB),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Clear",VK_CLEAR),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Enter",VK_RETURN),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Shift",VK_SHIFT),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Ctrl",VK_CONTROL),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Alt",VK_MENU),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Pause",VK_PAUSE),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Caps Lock",VK_CAPITAL),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Esc",VK_ESCAPE),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Space",VK_SPACE),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Page Up",VK_PRIOR),&nbsp; <br>&nbsp;&nbsp;&nbsp; KEYNAME("Page Down",VK_NEXT),<br>&nbsp;&nbsp;&nbsp; KEYNAME("End",VK_END),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Home",VK_HOME),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Left",VK_LEFT),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Up",VK_UP),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Right",VK_RIGHT),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Down",VK_DOWN),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Select",VK_SELECT),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Excute",VK_EXECUTE),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Print Screen",VK_SNAPSHOT),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Insert",VK_INSERT),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Delete",VK_DELETE),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Help",VK_HELP),<br>&nbsp;&nbsp;&nbsp; KEYNAME("0",'0'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("1",'1'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("2",'2'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("3",'3'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("4",'4'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("5",'5'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("6",'6'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("7",'7'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("8",'8'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("9",'9'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("A",'A'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("B",'B'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("C",'C'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("D",'D'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("E",'E'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F",'F'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("G",'G'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("H",'H'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("I",'I'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("J",'J'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("K",'K'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("L",'L'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("M",'M'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("N",'N'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("O",'O'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("P",'P'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Q",'Q'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("R",'R'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("S",'S'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("T",'T'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("U",'U'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("V",'V'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("W",'W'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("X",'X'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Y",'Y'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Z",'Z'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Left windows",VK_LWIN),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Right windows",VK_RWIN),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Applications",VK_APPS),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 0",&nbsp;VK_NUMPAD0),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 1",&nbsp;VK_NUMPAD1),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 2",&nbsp;VK_NUMPAD2),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 3",&nbsp;VK_NUMPAD3),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 4",&nbsp;VK_NUMPAD4),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 5",&nbsp;VK_NUMPAD5),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 6",&nbsp;VK_NUMPAD6),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 7",&nbsp;VK_NUMPAD7),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 8",&nbsp;VK_NUMPAD8),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Numeric keypad 9",&nbsp;VK_NUMPAD9), <br>&nbsp;&nbsp;&nbsp; KEYNAME("Multiply",VK_MULTIPLY),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Add",VK_ADD),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Separator",VK_SEPARATOR),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Subtract",VK_SUBTRACT),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Decimal Point",VK_DECIMAL),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Divide",VK_DIVIDE),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F1",VK_F1),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F2",VK_F2),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F3",VK_F3),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F4",VK_F4),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F5",VK_F5),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F6",VK_F6),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F7",VK_F7),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F8",VK_F8),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F9",VK_F9),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F10",VK_F10),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F11",VK_F11),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F12",VK_F12),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F13",VK_F13),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F14",VK_F14),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F15",VK_F15),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F16",VK_F16),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F17",VK_F17),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F18",VK_F18),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F19",VK_F19),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F20",VK_F20),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F21",VK_F21),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F22",VK_F22),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F23",VK_F23),<br>&nbsp;&nbsp;&nbsp; KEYNAME("F24",VK_F24),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Attn",VK_ATTN),<br>&nbsp;&nbsp;&nbsp; KEYNAME("CrSel",VK_CRSEL),<br>&nbsp;&nbsp;&nbsp; KEYNAME("ExSel",VK_EXSEL),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Erase",VK_EREOF),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Play",VK_PLAY),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Zoom",VK_ZOOM),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Reserved for future use",VK_NONAME ),<br>&nbsp;&nbsp;&nbsp; KEYNAME("PA1",VK_PA1),<br>&nbsp;&nbsp;&nbsp; KEYNAME("Clear(OEM)",VK_OEM_CLEAR ),</p>
<p>&nbsp;&nbsp;&nbsp; KEYNAME("<a href="file://%22,'//'"><u><font color=#0000ff>file://",'//'</font></u></a>),<br>&nbsp;&nbsp;&nbsp; KEYNAME("-",'-'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("=",'='),<br>&nbsp;&nbsp;&nbsp; KEYNAME("[",'['),<br>&nbsp;&nbsp;&nbsp; KEYNAME("]",']'),<br>&nbsp;&nbsp;&nbsp; KEYNAME(";",';'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("\'",'\''),<br>&nbsp;&nbsp;&nbsp; KEYNAME(",",','),<br>&nbsp;&nbsp;&nbsp; KEYNAME(".",'.'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("/",'/'),<br>&nbsp;&nbsp;&nbsp; KEYNAME("`",'`')<br>};</p>
<p><br>　　在类CDlgAccelEdit中响应Windows消息：WM_INITDIALOD，代码如下：<br>BOOL CDlgEditAccel::OnInitDialog() <br>{<br>&nbsp;&nbsp;&nbsp; CDialog::OnInitDialog();</p>
<p>&nbsp;&nbsp;&nbsp; // TODO: Add extra initialization here<br>&nbsp;&nbsp;&nbsp; SetWindowText("Edit Accelerator Table");</p>
<p>&nbsp;&nbsp;&nbsp; int iCount=m_AccelList.GetSize();<br>&nbsp;&nbsp;&nbsp; int i;<br>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;iCount;i++){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_LstCmd.AddString(m_AccelList[i].m_strCmd);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;sizeof(key)/sizeof(KEYNAME);i++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_LstKey.AddString(key[i].m_strName);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; m_LstCmd.SetCurSel(0);<br>&nbsp;&nbsp;&nbsp; OnSelchangeLstCmd();<br>&nbsp;&nbsp;&nbsp; return TRUE;&nbsp; // return TRUE unless you set the focus to a control<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // EXCEPTION: OCX Property Pages should return FALSE<br>}</p>
<p><br>　　为类CDglAccelEdit增加一个保护成员函数：<br>&nbsp;&nbsp;&nbsp; void SaveChange(int index=-1);</p>
<p>　　在文件DlgAccelEdit中定义该函数，代码如下：</p>
<p>void CDlgEditAccel::SaveChange(int index)<br>{<br>&nbsp;&nbsp;&nbsp; if(index&gt;=0||(index=m_LstCmd.GetCurSel())&gt;=0)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(m_LstKey.GetCurSel()&lt;0){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox("你必需选择一个键码!");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BYTE btCmp=((m_ChkAlt.GetCheck()==1)?FALT:NULL)|<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((m_ChkCtrl.GetCheck()==1)?FCONTROL:NULL)|<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ((m_ChkShift.GetCheck()==1)?FSHIFT:NULL)|FVIRTKEY;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; WORD wKey=key[m_LstKey.GetCurSel()].m_wID;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_AccelList[index].m_Accel.fVirt=btCmp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_AccelList[index].m_Accel.key=wKey;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>　　响应列表框ID_LST_CMD的通知消息&#8220;LBN_SELCHANGE&#8221;，函数代码如下：<br>void CDlgEditAccel::OnSelchangeLstCmd() <br>{<br>&nbsp;&nbsp;&nbsp; // TODO: Add your control notification handler code here<br>&nbsp;&nbsp;&nbsp; int iCmd=m_LstCmd.GetCurSel();<br>&nbsp;&nbsp;&nbsp; WORD wKey=m_AccelList[iCmd].m_Accel.key;<br>&nbsp;&nbsp;&nbsp; BYTE btCmp=m_AccelList[iCmd].m_Accel.fVirt;<br>&nbsp;&nbsp;&nbsp; m_ChkAlt.SetCheck(btCmp&amp;FALT);<br>&nbsp;&nbsp;&nbsp; m_ChkCtrl.SetCheck(btCmp&amp;FCONTROL);<br>&nbsp;&nbsp;&nbsp; m_ChkShift.SetCheck(btCmp&amp;FSHIFT);<br>&nbsp;&nbsp;&nbsp; int iCount=sizeof(key)/sizeof(KEYNAME);<br>&nbsp;&nbsp;&nbsp; int id=-1;<br>&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;iCount;i++)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(key[i].m_wID==wKey){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id=i;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; m_LstKey.SetCurSel(id);</p>
<p>}</p>
<p>　　响应列表框ID_LST_KEY的通知消息&#8220;LBN_SELCHANGE&#8221;，函数代码如下：<br>void CDlgEditAccel::OnSelchangeLstKey() <br>{<br>&nbsp;&nbsp;&nbsp; SaveChange();&nbsp;<br>}</p>
<p>　　响应核选框ID_CHK_ALT的通知消息&#8220;BN_CLICKED&#8221;，函数代码如下：<br>void CDlgEditAccel::OnChkAlt() <br>{<br>&nbsp;&nbsp;&nbsp; SaveChange();<br>}</p>
<p>　　响应核选框ID_CHK_SHIFT的通知消息&#8220;BN_CLICKED&#8221;，函数代码如下：<br>void CDlgEditAccel::OnChkShift() <br>{<br>&nbsp;&nbsp;&nbsp; SaveChange();<br>}</p>
<p>　　响应核选框ID_CHK_CTRL的通知消息&#8220;BN_CLICKED&#8221;，函数代码如下：<br>void CDlgEditAccel::OnChkCtrl() <br>{<br>&nbsp;&nbsp;&nbsp; SaveChange();<br>}</p>
<p>　　至此，用于编辑<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>的对话框已经完成。我们现在要做的就是在程序中打开对话框来编辑了。让我们回到CMainFrame类中。我们将在该类中响应一个命令来打开编辑对话框，对<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表进行编辑。完成后点按OK回到主程序中，然后更新<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表。这样后，我们刚刚编辑好的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表就开始起作用了。<br>　　首先，打开菜单。在&#8220;查看&#8221;项后增加一个&#8220;工具(&amp;T)&#8221;下拉菜单，在下拉菜单中增加一个了菜单：&#8220;编辑<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表(&amp;A)...&#8221;，ID为&#8220;ID_TOOL_ACCELEDIT&#8221;。打开类向导，选择CMainFrame类，响应&#8220;ID_TOOL_ACCELEDIT&#8221;命令。编辑响应函数如下：</p>
<p>void CMainFrame::OnToolAcceledit() <br>{<br>&nbsp;&nbsp;&nbsp; // TODO: Add your command handler code here<br>&nbsp;&nbsp;&nbsp; CDlgEditAccel dlg;</p>
<p>&nbsp;&nbsp;&nbsp; DWORD i;<br>&nbsp;&nbsp;&nbsp; dlg.m_AccelList.SetSize(m_dwAccelCount);<br>&nbsp;&nbsp;&nbsp; for(i=0;i&lt;m_dwAccelCount;i++){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dlg.m_AccelList[i].m_strCmd=m_lpAccel[i].cCmd;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dlg.m_AccelList[i].m_Accel=m_lpAccel[i].accel;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; if(IDOK==dlg.DoModal()){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACCEL* pAccel=new ACCEL[m_dwAccelCount];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(DWORD dw=0;dw&lt;m_dwAccelCount;dw++){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_lpAccel[dw].accel=pAccel[dw]=dlg.m_AccelList[dw].m_Accel;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(m_hActAccel!=NULL)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DestroyAcceleratorTable(m_hActAccel);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_hActAccel=CreateAcceleratorTable(pAccel,m_dwAccelCount);<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>　　在该文件的头部包含类CDlgEditAccel的头文件：</p>
<p>#include "DlgEditAccel.h"</p>
<p>　　至此，可编辑的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表已经完成了。不妨试试看。</p>
<p>　　三、将<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表保存至文件，并在程序运行时自动从文件中加载。</p>
<p>　　上面我们实现了可编辑的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表。但是，当程序退出后，我们编辑过的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>数据就消失了，下次运行程序时还是和以前一样了。将<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表保存起来以备下次使用，这是我们所希望的。下面讨论的方法是使用文件来保存我们的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表。<br>　　首先，在MainFrm.cpp文件的头部定义一个全局字符串，也就是用于保存<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>的文件名：</p>
<p>static char strAccelFileName[]="Accel.cus";</p>
<p>　　定义一个DWORD值作为文件头，我们通过该DWORD值来判断是否是一个有效格式的文件：</p>
<p>#define ACCEL_FILE_HEAD&nbsp;0x00434341</p>
<p><br>　　其次，为CMainFrame类添加一个保护的成员函数：&#8220;BOOL SaveAccel()&#8221;，编辑其代码如下：<br>BOOL CMainFrame::SaveAccel()<br>{<br>&nbsp;&nbsp;&nbsp; char lpAppName[256];<br>&nbsp;&nbsp;&nbsp; char lpAppPath[256];<br>&nbsp;&nbsp;&nbsp; char* lpFilePart=NULL;<br>&nbsp;&nbsp;&nbsp; GetModuleFileName(AfxGetInstanceHandle(),lpAppName,255);<br>&nbsp;&nbsp;&nbsp; GetFullPathName(lpAppName,255,lpAppPath,&amp;lpFilePart);<br>&nbsp;&nbsp;&nbsp; TRACE1("Application File Name:%s.\n",lpAppName);<br>&nbsp;&nbsp;&nbsp; strcpy(lpFilePart,strAccelFileName);<br>&nbsp;&nbsp;&nbsp; TRACE1("Accelerator File Name:%s.\n",lpAppPath);<br>&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwHead=ACCEL_FILE_HEAD;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwCount=m_dwAccelCount;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFile fAccel(lpAppPath,CFile::modeCreate|CFile::modeWrite);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fAccel.SeekToBegin();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fAccel.Write(&amp;dwHead,sizeof(DWORD));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fAccel.Write(&amp;dwCount,sizeof(DWORD));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(DWORD dw=0;dw&lt;dwCount;dw++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fAccel.Write(m_lpAccel+dw,sizeof(ACCELITEM));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return TRUE;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; catch(CFileException* e)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char buf[256];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char buf2[512];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int iError;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iError=e-&gt;m_cause;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e-&gt;GetErrorMessage(buf,256);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(buf2,"将<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表保存到文件 \"%s\" 中时发生错误!\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "错 误 号:%d\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "错误描述:%s\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "\0",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strAccelFileName,iError,buf);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox(buf2,MB_OK|MB_ICONSTOP|MB_DEFBUTTON1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>　　再次，修改LoadAccel()函数如下：</p>
<p>BOOL CMainFrame::LoadAccel()<br>{<br>&nbsp;&nbsp;&nbsp; char lpAppName[256];<br>&nbsp;&nbsp;&nbsp; char lpAppPath[256];<br>&nbsp;&nbsp;&nbsp; char* lpFilePart=NULL;<br>&nbsp;&nbsp;&nbsp; GetModuleFileName(AfxGetInstanceHandle(),lpAppName,255);<br>&nbsp;&nbsp;&nbsp; GetFullPathName(lpAppName,255,lpAppPath,&amp;lpFilePart);<br>&nbsp;&nbsp;&nbsp; TRACE1("Application File Name:%s.\n",lpAppName);<br>&nbsp;&nbsp;&nbsp; strcpy(lpFilePart,strAccelFileName);<br>&nbsp;&nbsp;&nbsp; TRACE1("Accelerator File Name:%s.\n",lpAppPath);<br>&nbsp;&nbsp;&nbsp; try<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwHead;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwCount;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CFile fAccel(lpAppPath,CFile::modeRead);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fAccel.SeekToBegin();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(fAccel.Read(&amp;dwHead,sizeof(DWORD))!=sizeof(DWORD))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxThrowFileException(CFileException::endOfFile,12,lpAppPath);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(dwHead!=ACCEL_FILE_HEAD)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxThrowFileException(CFileException::invalidFile,13,lpAppPath);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(fAccel.Read(&amp;dwCount,sizeof(DWORD))!=sizeof(DWORD))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxThrowFileException(CFileException::endOfFile,12,lpAppPath);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(m_lpAccel==NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_lpAccel=new ACCELITEM[dwCount];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset(m_lpAccel,0,sizeof(ACCELITEM)*dwCount);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_dwAccelCount=dwCount;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(DWORD dw=0;dw&lt;dwCount;dw++)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(fAccel.Read(m_lpAccel+dw,sizeof(ACCELITEM))!=sizeof(ACCELITEM))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxThrowFileException(CFileException::endOfFile,12,lpAppPath);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ACCEL* pAccel=new ACCEL[dwCount];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(dw=0;dw&lt;dwCount;dw++){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pAccel[dw]=m_lpAccel[dw].accel;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_hActAccel=CreateAcceleratorTable(pAccel,dwCount);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return TRUE;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; catch(CFileException* e)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char buf[256];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char buf2[512];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int iError;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iError=e-&gt;m_cause;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e-&gt;GetErrorMessage(buf,256);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sprintf(buf2,"从文件 \"%s\" 中加载<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表时发生错误!\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "错 误 号:%d\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "错误描述:%s\n"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "是否生成默认的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表?\n\0",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strAccelFileName,iError,buf);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(IDYES==AfxMessageBox(buf2,MB_YESNO|MB_SYSTEMMODAL|MB_ICONSTOP|MB_DEFBUTTON1))<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(m_hActAccel==NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(m_lpAccel==NULL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_dwAccelCount=sizeof(accel)/sizeof(ACCEL);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_lpAccel=new ACCELITEM[m_dwAccelCount];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; memset(m_lpAccel,0,sizeof(ACCELITEM)*m_dwAccelCount);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWORD dwCmdStr=sizeof(strCmd)/sizeof(char[30]);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(DWORD dw=0;dw&lt;m_dwAccelCount;dw++){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_lpAccel[dw].accel=accel[dw];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strcpy(m_lpAccel[dw].cCmd,dw&lt;dwCmdStr?strCmd[dw]:"Command Unknow");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_hActAccel=CreateAcceleratorTable(accel,m_dwAccelCount);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SaveAccel();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return TRUE;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;<br>&nbsp;&nbsp;&nbsp; }<br>}</p>
<p>　　最后，在DestroyWindow()函数头部增加对SaveAccel()函数的调用：</p>
<p>&nbsp;&nbsp;&nbsp; SaveAccel();<br>&nbsp;&nbsp;&nbsp; ...</p>
<p>　　好了，你自己的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>数据已经能保存在文件中了，并能从中正确加载。如果文件不存在或程序读取时发现错误则提醒你是否建立缺省的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表，如你确认的话则生成缺省的<strong style="COLOR: black; BACKGROUND-COLOR: #a0ffff">加速键</strong>表并立刻保存至文件中。<br>　　由于水平有限，其中难免有误，欢迎批评指正、发表你的意见。本人不胜感激。<br></p>
<img src ="http://www.cppblog.com/tgh621/aggbug/71821.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2009-01-12 17:22 <a href="http://www.cppblog.com/tgh621/archive/2009/01/12/71821.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用WaitForSingleObject等待事件处理</title><link>http://www.cppblog.com/tgh621/archive/2009/01/12/71782.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Mon, 12 Jan 2009 02:17:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2009/01/12/71782.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/71782.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2009/01/12/71782.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/71782.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/71782.html</trackback:ping><description><![CDATA[<div style="BORDER-RIGHT: #cccccc 1px solid; PADDING-RIGHT: 5px; BORDER-TOP: #cccccc 1px solid; PADDING-LEFT: 4px; FONT-SIZE: 13px; PADDING-BOTTOM: 4px; BORDER-LEFT: #cccccc 1px solid; WIDTH: 98%; WORD-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: #cccccc 1px solid; BACKGROUND-COLOR: #eeeeee"><span style="COLOR: #008080">&nbsp;1</span><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;ClearMessage(HWND&nbsp;hWnd,BOOL&nbsp;bExtMsg)<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img id=Codehighlighter1_42_535_Open_Image onclick="this.style.display='none'; Codehighlighter1_42_535_Open_Text.style.display='none'; Codehighlighter1_42_535_Closed_Image.style.display='inline'; Codehighlighter1_42_535_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_42_535_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_42_535_Closed_Text.style.display='none'; Codehighlighter1_42_535_Open_Image.style.display='inline'; Codehighlighter1_42_535_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_42_535_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_42_535_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;MSG&nbsp;GetMsg;<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(PeekMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg,hWnd,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,PM_REMOVE))<br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img id=Codehighlighter1_106_211_Open_Image onclick="this.style.display='none'; Codehighlighter1_106_211_Open_Text.style.display='none'; Codehighlighter1_106_211_Closed_Image.style.display='inline'; Codehighlighter1_106_211_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_106_211_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_106_211_Closed_Text.style.display='none'; Codehighlighter1_106_211_Open_Image.style.display='inline'; Codehighlighter1_106_211_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_106_211_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_106_211_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">&nbsp;6</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(GetMsg.message&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;WM_TIMER)&nbsp;</span><span style="COLOR: #0000ff">continue</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">&nbsp;7</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TranslateMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg);<br></span><span style="COLOR: #008080">&nbsp;8</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatchMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg);<br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">10</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(hWnd&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;NULL&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;bExtMsg)<br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img id=Codehighlighter1_246_533_Open_Image onclick="this.style.display='none'; Codehighlighter1_246_533_Open_Text.style.display='none'; Codehighlighter1_246_533_Closed_Image.style.display='inline'; Codehighlighter1_246_533_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_246_533_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_246_533_Closed_Text.style.display='none'; Codehighlighter1_246_533_Open_Image.style.display='inline'; Codehighlighter1_246_533_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_246_533_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_246_533_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">12</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(PeekMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg,NULL,WM_WINDOWPOSCHANGING,WM_WINDOWPOSCHANGED,PM_REMOVE))<br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img id=Codehighlighter1_337_402_Open_Image onclick="this.style.display='none'; Codehighlighter1_337_402_Open_Text.style.display='none'; Codehighlighter1_337_402_Closed_Image.style.display='inline'; Codehighlighter1_337_402_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_337_402_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_337_402_Closed_Text.style.display='none'; Codehighlighter1_337_402_Open_Image.style.display='inline'; Codehighlighter1_337_402_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_337_402_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_337_402_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">14</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TranslateMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg);<br></span><span style="COLOR: #008080">15</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatchMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg);<br></span><span style="COLOR: #008080">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000">&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">(PeekMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg,NULL,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,</span><span style="COLOR: #000000">0</span><span style="COLOR: #000000">,PM_QS_PAINT))<br></span><span style="COLOR: #008080">18</span><span style="COLOR: #000000"><img id=Codehighlighter1_465_530_Open_Image onclick="this.style.display='none'; Codehighlighter1_465_530_Open_Text.style.display='none'; Codehighlighter1_465_530_Closed_Image.style.display='inline'; Codehighlighter1_465_530_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_465_530_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_465_530_Closed_Text.style.display='none'; Codehighlighter1_465_530_Open_Image.style.display='inline'; Codehighlighter1_465_530_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_465_530_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_465_530_Open_Text><span style="COLOR: #000000">{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br></span><span style="COLOR: #008080">19</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TranslateMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg);<br></span><span style="COLOR: #008080">20</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DispatchMessage(</span><span style="COLOR: #000000">&amp;</span><span style="COLOR: #000000">GetMsg);<br></span><span style="COLOR: #008080">21</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">22</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">23</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">24</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">25</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span><span style="COLOR: #0000ff">void</span><span style="COLOR: #000000">&nbsp;CExportRegeditToXml::OnBnClickedCancel()<br></span><span style="COLOR: #008080">26</span><span style="COLOR: #000000"><img id=Codehighlighter1_584_943_Open_Image onclick="this.style.display='none'; Codehighlighter1_584_943_Open_Text.style.display='none'; Codehighlighter1_584_943_Closed_Image.style.display='inline'; Codehighlighter1_584_943_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_584_943_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_584_943_Closed_Text.style.display='none'; Codehighlighter1_584_943_Open_Image.style.display='inline'; Codehighlighter1_584_943_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top></span><span id=Codehighlighter1_584_943_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_584_943_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">27</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">28</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">29</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(&nbsp;(&nbsp;NULL&nbsp;</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">&nbsp;m_TransRegedit&nbsp;)&nbsp;&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;(&nbsp;</span><span style="COLOR: #000000">!</span><span style="COLOR: #000000">pThreadData</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">m_brepalce)&nbsp;)&nbsp;<br></span><span style="COLOR: #008080">30</span><span style="COLOR: #000000"><img id=Codehighlighter1_657_927_Open_Image onclick="this.style.display='none'; Codehighlighter1_657_927_Open_Text.style.display='none'; Codehighlighter1_657_927_Closed_Image.style.display='inline'; Codehighlighter1_657_927_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_657_927_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_657_927_Closed_Text.style.display='none'; Codehighlighter1_657_927_Open_Image.style.display='inline'; Codehighlighter1_657_927_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_657_927_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_657_927_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">31</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">(MessageBox(_T(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">目前正在导出注册表，是否取消!</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">),_T(</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">警告</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">),MB_YESNO)</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">IDYES)<br></span><span style="COLOR: #008080">32</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">;<br></span><span style="COLOR: #008080">33</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pThreadData</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">m_brepalce&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;TRUE;<br></span><span style="COLOR: #008080">34</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">35</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span style="COLOR: #0000ff">while</span><span style="COLOR: #000000">&nbsp;(m_TransRegedit&nbsp;</span><span style="COLOR: #000000">&amp;&amp;</span><span style="COLOR: #000000">&nbsp;WaitForSingleObject(m_TransRegedit</span><span style="COLOR: #000000">-&gt;</span><span style="COLOR: #000000">m_hThread,</span><span style="COLOR: #000000">40</span><span style="COLOR: #000000">)</span><span style="COLOR: #000000">!=</span><span style="COLOR: #000000">WAIT_OBJECT_0)&nbsp;<br></span><span style="COLOR: #008080">36</span><span style="COLOR: #000000"><img id=Codehighlighter1_866_899_Open_Image onclick="this.style.display='none'; Codehighlighter1_866_899_Open_Text.style.display='none'; Codehighlighter1_866_899_Closed_Image.style.display='inline'; Codehighlighter1_866_899_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif" align=top><img id=Codehighlighter1_866_899_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_866_899_Closed_Text.style.display='none'; Codehighlighter1_866_899_Open_Image.style.display='inline'; Codehighlighter1_866_899_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedSubBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span id=Codehighlighter1_866_899_Closed_Text style="BORDER-RIGHT: #808080 1px solid; BORDER-TOP: #808080 1px solid; DISPLAY: none; BORDER-LEFT: #808080 1px solid; BORDER-BOTTOM: #808080 1px solid; BACKGROUND-COLOR: #ffffff"><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_866_899_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">37</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClearMessage(NULL,&nbsp;TRUE);<br></span><span style="COLOR: #008080">38</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">39</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;m_TransRegedit&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;NULL;<br></span><span style="COLOR: #008080">40</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedSubBlockEnd.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;}</span></span><span style="COLOR: #000000"><br></span><span style="COLOR: #008080">41</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top><br></span><span style="COLOR: #008080">42</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;OnCancel();<br></span><span style="COLOR: #008080">43</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockEnd.gif" align=top>}</span></span></div>
<img src ="http://www.cppblog.com/tgh621/aggbug/71782.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2009-01-12 10:17 <a href="http://www.cppblog.com/tgh621/archive/2009/01/12/71782.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]PSD格式文件的读取</title><link>http://www.cppblog.com/tgh621/archive/2008/12/26/70462.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Fri, 26 Dec 2008 10:15:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2008/12/26/70462.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/70462.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2008/12/26/70462.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/70462.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/70462.html</trackback:ping><description><![CDATA[<font size=2>　PhotoShop，我想没有人会不知道吧。如今最新的版本是6.0，其图象文件*.PSD和5.5相比变化并不太大。以下我就介绍*.PSD文件的读取方法，并提供完整读取函数。其中：m_Rect为目标区域，m_lpDDS7为目标DirectDraw表面，m_pbAlphaMask为目标Aplha通告指针。Read16函数为从指定文件当前位置读取一个WORD，Read32函数为从指定文件当前位置读取一个DWORD。MAX_PSD_CHANNELS为24。以下就是*.PSD文件的读取方法，有兴趣的朋友可以继续深入研究，到时可别忘了发我一份。<br></font></font><font class=f14 id=zoom><font face=宋体><br></font></font><font face=宋体></font></font><font face=Arial><font class=f14 id=zoom><font face=宋体><font color=#99ccff><font size=2><span class=ColorCode>　　HRESULT LoadPSD( LPSTR strFilename )</span> </font><font color=#c0c0c0><span class=ColorCatchword><font size=2>// 读取PSD文件</font></span></font><font size=2><br><span class=ColorCode>　　{<br>　　　　DWORD dwWidth, dwHeight;</span> <font color=#c0c0c0><span class=ColorCatchword>// 宽高</span></font><br><span class=ColorCode>　　　　long lSurfWidth = m_Rect.right - m_Rect.left;<br>　　　　long lSurfHeight = m_Rect.bottom - m_Rect.top;<br>　　　　WORD CompressionType;</span> <font color=#c0c0c0><span class=ColorCatchword>// 压缩类型</span></font><br><span class=ColorCode>　　　　HDC hDC;<br>　　　　FILE *fpPSD;<br>　　　　WORD ChannelCount;</span> <font color=#c0c0c0><span class=ColorCatchword>// 通道数</span></font><br><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 打开PSD文件</font></span><br><span class=ColorCode>　　　　if ( ( fpPSD = fopen ( strFilename, "rb" ) ) == NULL ) {<br>　　　　　　return E_FAIL;<br>　　　　}<br></span><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 头四个字节为"8BPS"</font><br></span><span class=ColorCode>　　　　char signature[5];<br>　　　　signature[0] = fgetc( fpPSD );<br>　　　　signature[1] = fgetc( fpPSD );<br>　　　　signature[2] = fgetc( fpPSD );<br>　　　　signature[3] = fgetc( fpPSD );<br>　　　　signature[4] = '\0';<br>　　　　if ( strcmp( signature,"8BPS" ) != 0 ) {<br>　　　　　　return E_FAIL;<br>　　　　}<br></span><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 版本必须为1</font></span><br><span class=ColorCode>　　　　if ( Read16( fpPSD ) != 1 ) {<br>　　　　　　return E_FAIL;<br>　　　　}<br></span><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 跳过一些数据 (总是0)</font></span><br><span class=ColorCode>　　　　Read32( fpPSD );<br>　　　　Read16( fpPSD );<br></span><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 读取通道数</font><br></span><span class=ColorCode>　　　　ChannelCount = Read16( fpPSD );</span><br><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 确定至少有一个通道</font><br></span><span class=ColorCode>　　　　if ( ( ChannelCount &lt; 0 ) || ( ChannelCount &gt; MAX_PSD_CHANNELS ) ) {<br>　　　　　　return E_FAIL;<br>　　　　}<br></span><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 读入宽和高</font><br></span><span class=ColorCode>　　　　dwHeight = Read32( fpPSD );<br>　　　　dwWidth = Read32( fpPSD );<br>　　　　if ( dwWidth != ( DWORD )lSurfWidth ||</span> <span class=ColorCode>dwHeight != ( DWORD )lSurfHeight ) {<br>　　　　　　return E_FAIL;<br>　　　　}<br></span><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 只读入8位通道</font></span><br><span class=ColorCode>　　　　if ( Read16( fpPSD ) != 8 ) {<br>　　　　　　return E_FAIL;<br>　　　　}<br></span><br><span class=ColorCatchword><font color=#c0c0c0>　　　　// 确定模式为RGB.<br>　　　　// 可能值：<br>　　　　// 0: 位图<br>　　　　// 1: 灰阶<br>　　　　// 2: 索引<br>　　　　// 3: RGB<br>　　　　// 4: CMYK<br>　　　　// 7: Multichannel<br>　　　　// 8: Duotone<br>　　　　// 9: Lab</font><br></span><span class=ColorCode>　　　　if ( Read16( fpPSD ) != 3 ) {<br>　　　　　　return E_FAIL;<br>　　　　}<br></span><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 跳过数据（如调色板）</font><br></span><span class=ColorCode>　　　　int ModeDataCount = Read32( fpPSD );<br>　　　　if ( ModeDataCount )<br>　　　　　　fseek( fpPSD, ModeDataCount, SEEK_CUR );<br></span><br><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 跳过数据（如：pen tool paths, etc）</font><br></span><span class=ColorCode>　　　　int ResourceDataCount = Read32( fpPSD );<br>　　　　if ( ResourceDataCount )<br>　　　　　　fseek( fpPSD, ResourceDataCount, SEEK_CUR );<br><br></span><span class=ColorCatchword>　　　　<font color=#c0c0c0>// 条过保留数据</font><br></span><span class=ColorCode>　　　　int ReservedDataCount = Read32( fpPSD );<br>　　　　if ( ReservedDataCount )<br>　　　　　　fseek( fpPSD, ReservedDataCount, SEEK_CUR );<br></span><br><span class=ColorCatchword><font color=#c0c0c0>　　　　// 0: 非压缩<br>　　　　// 1: RLE压缩</font><br></span><span class=ColorCode>　　　　CompressionType = Read16( fpPSD );<br>　　　　if ( CompressionType &gt; 1 ) {<br>　　　　　　return E_FAIL;<br>　　　　}<br><br>　　　　BYTE* PSDPixels = new BYTE[ ( lSurfWidth * lSurfHeight ) * 4 ];<br></span><br><span class=ColorCatchword><font color=#c0c0c0>　　　　// 解包数据</font><br></span><span class=ColorCode>　　　　UnPackPSD( fpPSD, lSurfWidth, lSurfHeight, PSDPixels, ChannelCount, CompressionType );<br><br>　　　　fclose( fpPSD );<br></span><br><span class=ColorCatchword><font color=#c0c0c0>　　　　// 复制信息</font><br></span><span class=ColorCode>　　　　BITMAPINFO BitmapInfo;<br>　　　　ZeroMemory( &amp;BitmapInfo, sizeof( BitmapInfo ) );<br>　　　　BitmapInfo.bmiHeader.biSize = sizeof( BitmapInfo.bmiHeader );<br>　　　　BitmapInfo.bmiHeader.biWidth = lSurfWidth;<br>　　　　BitmapInfo.bmiHeader.biHeight = -lSurfHeight;<br>　　　　BitmapInfo.bmiHeader.biPlanes = 1;<br>　　　　BitmapInfo.bmiHeader.biBitCount = 32;<br><br>　　　　m_lpDDS7-&gt;GetDC( &amp;hDC );<br><br>　　　　int rc = StretchDIBits( hDC,<br>　　　　　　　　　　　　　　　　0,<br>　　　　　　　　　　　　　　　　0,<br>　　　　　　　　　　　　　　　　lSurfWidth,<br>　　　　　　　　　　　　　　　　lSurfHeight,<br>　　　　　　　　　　　　　　　　0,<br>　　　　　　　　　　　　　　　　0,<br>　　　　　　　　　　　　　　　　lSurfWidth,<br>　　　　　　　　　　　　　　　　lSurfHeight,<br>　　　　　　　　　　　　　　　　PSDPixels,<br>　　　　　　　　　　　　　　　　&amp;BitmapInfo,<br>　　　　　　　　　　　　　　　　DIB_RGB_COLORS,<br>　　　　　　　　　　　　　　　　SRCCOPY );<br><br>　　　　m_lpDDS7-&gt;ReleaseDC( hDC );<br><br>　　　　if ( rc == GDI_ERROR ) {<br>　　　　　　H_ARRAY_DELETE( PSDPixels );<br><br>　　#ifdef _DEBUG<br>　　　　g_pHERR-&gt;OutDebugMsg( 3, H2DSERR_INVALID_PSD );<br>　　#endif<br>　　　　return E_FAIL;<br><br>　　　　}<br><br></span></font><span class=ColorCatchword><font size=2>　　　<font color=#c0c0c0>　// 是否读取Alpha混合通道</font><br></font></span><span class=ColorCode><font size=2>　　　　if( ChannelCount &gt; 3 ) {<br>　　　　　　m_pbAlphaMask = new BYTE[ lSurfWidth * lSurfHeight ];<br><br>　　　　for ( int x = 0; x &lt; lSurfWidth; x++ )<br>　　　　　　for ( int y = 0; y &lt; lSurfHeight; y++ ) {<br>　　　　　　　　m_pbAlphaMask[ ( y * lSurfWidth ) + x ] =<br>　　　　　　　　　　　　　　 　PSDPixels[ ( ( ( y * lSurfHeight ) + x ) * 4 ) + 3 ];<br>　　　　　　}<br>　　　　}<br>　　　　else {<br>　　　　　　m_pbAlphaMask = NULL;<br>　　　　}<br><br>　　　　H_ARRAY_DELETE( PSDPixels );<br><br>　　　　return DD_OK;<br>　　}<br><br></font><span class=ColorCatchword><font size=2>　　</font><font color=#c0c0c0 size=2>// PSD文件解包</font><font size=2><br>　</font></span></span></font>
<div align=left><font color=#99ccff><span class=ColorCode><font size=2>　　void CHades2DSurface::UnPackPSD( FILE *fp,</font></span><font size=2><span class=ColorCatchword>　　　　　// fp为PSD文件指针，</span><span class=ColorCode><br>　　　　　　　　　　　　　　　　　　 DWORD dwWidth,　　 </span><span class=ColorCatchword>// dwWidth、dwHeight为宽高，</span><span class=ColorCode><br>　　　　　　　　　　　　　　　　　　 DWORD dwHeight,<br>　　　　　　　　　　　　　　　　　　 BYTE* pixels,</span><span class=ColorCatchword>　　　// pixels为解包目标指针，</span><span class=ColorCode><br>　　　　　　　　　　　　　　　　　　 WORD ChannelCnt,</span><span class=ColorCatchword>　 // ChannelCnt为通道数，</span><span class=ColorCode><br>　　　　　　　　　　　　　　　　　　 WORD Compression )</span></font><span class=ColorCatchword><font size=2> // Compression位压缩类型。 </font></span></font></div>
<div align=left><font color=#99ccff><span class=ColorCatchword><font size=2>　　　　　　　　　　　　　　　　<br>　　　　　　　　　　　　　　　　 <br></font></span><font size=2><span class=ColorCode>　　{<br>　　　　int Default[4] = { 0, 0, 0, 255 };<br>　　　　int chn[4] = { 2, 1, 0, 3};<br>　　　　int PixelCount = dwWidth * dwHeight;<br><br>　　　　if ( Compression ) {<br>　　　　　　fseek( fp, dwHeight * ChannelCnt * 2, SEEK_CUR );<br><br>　　　　　　for ( int c = 0; c &lt; 4; c++ ) {<br>　　　　　　　　int pn = 0;<br>　　　　　　　　int channel = chn[c];<br><br>　　　　　　　　if ( channel &gt;= ChannelCnt ) {<br>　　　　　　　　　　for ( pn=0; pn &lt; PixelCount ;pn++ ) {<br>　　　　　　　　　　　　pixels[ ( pn * 4 ) + channel ] = Default[ channel ];<br>　　　　　　　　　　}<br>　　　　　　　　}<br>　　　　　　　　else</span> <span class=ColorCatchword>// 非压缩<br></span></font><span class=ColorCode><font size=2>　　　　　　　　{<br>　　　　　　　　　　int count = 0;<br>　　　　　　　　　　while( count &lt; PixelCount ) {<br>　　　　　　　　　　　　int len = fgetc( fp );<br>　　　　　　　　　　　　if( len == 128 ) { }</font></span></font></div>
<div align=left><font color=#99ccff><span class=ColorCode><font size=2>　　　　　　　　　　　　else if ( len &lt; 128 )</font></span><font size=2> </font><span class=ColorCatchword><font size=2>// 非RLE</font></span></font></div>
<div align=left><font color=#99ccff><span class=ColorCode><font size=2>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　len++;<br>　　　　　　　　　　　　　　count += len;<br>　　　　　　　　　　　　　　while(len) {<br>　　　　　　　　　　　　　　　　pixels[ ( pn * 4 ) + channel ] = fgetc( fp );<br>　　　　　　　　　　　　　　　　pn++;<br>　　　　　　　　　　　　　　　　len--;<br>　　　　　　　　　　　　　　}<br>　　　　　　　　　　　　}<br>　　　 　　　　　　　　 else if ( len &gt; 128 )</font></span><font size=2> <span class=ColorCatchword>// RLE打包</span><br></font><span class=ColorCode><font size=2>　　　　　　　　　　　　{<br>　　　　　　　　　　　　　　len ^= 0x0FF;<br>　　　　　　　　　　　　　　len += 2;<br>　　　　　　　　　　　　　　unsigned char val = fgetc( fp );<br>　　　　　　　　　　　　　　count += len;<br>　　　　　　　　　　　　　　while( len ) {<br>　　　　　　　　　　　　　　　　pixels[ ( pn * 4 ) + channel ] = val;<br>　　　　　　　　　　　　　　　　pn++;<br>　　　　　　　　　　　　　　　　len--;<br>　　　　　　　　　　　　　　}<br>　　　　　　　　　　　　}<br>　　　　　　　　　　}<br>　　　　　　　　}<br>　　　　　　}<br>　　　　}<br>　　　　else<br>　　　　{<br>　　　　　　for ( int c=0; c &lt; 4; c++ ) {<br>　　　　　　　　int channel = chn[c];<br>　　　　　　　　if ( channel &gt; ChannelCnt ) {<br>　　　　　　　　　　for( int pn = 0; pn &lt; PixelCount; pn++ ) {<br>　　　　　　　　　　　　pixels[ ( pn * 4 ) + channel ] = Default[ channel ];<br>　　　　　　　　　　}<br>　　　　　　　　}<br>　　　　　　　　else {<br>　　　　　　　　　　for( int n = 0; n &lt; PixelCount; n++ ) {<br>　　　　　　　　　　　　pixels[ ( n * 4 ) + channel ] = fgetc( fp );<br>　　　　　　　　　　}<br>　　　　　　　　}<br>　　　　　　}<br>　　　　}<br>　　}</font></span></font></div>
</font></font><font face=宋体></font></font>
<img src ="http://www.cppblog.com/tgh621/aggbug/70462.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2008-12-26 18:15 <a href="http://www.cppblog.com/tgh621/archive/2008/12/26/70462.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]GIF文档</title><link>http://www.cppblog.com/tgh621/archive/2008/12/26/70461.html</link><dc:creator>大海</dc:creator><author>大海</author><pubDate>Fri, 26 Dec 2008 10:14:00 GMT</pubDate><guid>http://www.cppblog.com/tgh621/archive/2008/12/26/70461.html</guid><wfw:comment>http://www.cppblog.com/tgh621/comments/70461.html</wfw:comment><comments>http://www.cppblog.com/tgh621/archive/2008/12/26/70461.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/tgh621/comments/commentRss/70461.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/tgh621/services/trackbacks/70461.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 1.概述~~~~~~~~　　GIF(Graphics Interchange Format，图形交换格式)文件是由 CompuServe公司开发的图形文件格式，版权所有，任何商业目的使用均须 CompuServe公司授权。　　GIF图象是基于颜色列表的（存储的数据是该点的颜色对应于颜色列表的索引值），最多只支持8位（256色）。GIF文件内部分成许多存储块，用来存储多幅图象或者是决定图象表现行...&nbsp;&nbsp;<a href='http://www.cppblog.com/tgh621/archive/2008/12/26/70461.html'>阅读全文</a><img src ="http://www.cppblog.com/tgh621/aggbug/70461.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/tgh621/" target="_blank">大海</a> 2008-12-26 18:14 <a href="http://www.cppblog.com/tgh621/archive/2008/12/26/70461.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>