﻿<?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++博客-&lt;font style="position:absolute; top:10px; left:20px; color: #c0c0c0;  "&gt;幽幽&lt;/font&gt;-随笔分类-Windows</title><link>http://www.cppblog.com/justin-shi/category/6153.html</link><description>&lt;font&gt;&amp;nbsp;&lt;/font&gt;   </description><language>zh-cn</language><lastBuildDate>Sun, 20 Dec 2009 01:20:00 GMT</lastBuildDate><pubDate>Sun, 20 Dec 2009 01:20:00 GMT</pubDate><ttl>60</ttl><item><title>技术备忘录</title><link>http://www.cppblog.com/justin-shi/archive/2009/12/18/103441.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Fri, 18 Dec 2009 01:20:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2009/12/18/103441.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/103441.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2009/12/18/103441.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/103441.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/103441.html</trackback:ping><description><![CDATA[<font face=#ce_temp_font#>转一些从shell32.dll导出的函数<br><br>外壳对话框<br><br><strong>外壳对话框的秘密</strong><br><br>常见的Windows的通用对话框被封装在Comdlg32.dll，这给我们的编程提供了很大的便利。但它还不够完整，我们在系统里经常能看到大量的可重复使用的对话框,但在Windows的文档里你却找不到它们的调用方法。而如果我们自己去做这样的界面是非常费时费力的而且也是没有必要的，因为这些对话框实际上很容易得到。这里我要介绍一些已经众所周知或不为认知的对话框，它们可以应用在我们的程序中使程序显得非常友好和专业。<br><br><strong>浏览文件夹对话框</strong><br><br><img src="http://www.asm32.net/pic/article/i1854_001.jpg"><br><br>图2.23<br><br>大多数Delphi程序员都知道如何使用VCL的TOpenDialog控件来让用户浏览将要打开的文件。然而有时你可能只想让用户选择文件夹而不是特定的文件，windows已经提供了一个这样的对话框如图2.23所示。我们可以通过公开的函数SHBrowseForFolder来调用 (这个函数定义在ShlObj单元)，函数定义如下：<br><br>function SHBrowseForFolder(var BrowseInfo: TBrowseInfo): PItemIDList; stdcall;<br><br>这个函数只有一个参数，但这个参数是一个比较复杂的记录类型<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;TBrowseInfo&nbsp;=&nbsp;packed&nbsp;record<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hwndOwner:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pidlRoot:&nbsp;PItemIDList;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pszDisplayName:&nbsp;PChar;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpszTitle:&nbsp;PChar;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ulFlags:&nbsp;UINT;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpfn:&nbsp;TFNBFFCallBack;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lParam:&nbsp;LPARAM;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iImage:&nbsp;Integer;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;end; </td>
        </tr>
    </tbody>
</table>
<br><br>hwndOwner数据成员包含对话框的父窗体的窗口句柄，可以把它设成0。PIdlRoot数据成员指向一个PIDL的指针对应于对话框初始化时的根目录。指定了PIdlRoot后，就只有根目录及它的子目录会出现在对话框中。可以设定它为nil，这时缺省的根目录是桌面，pszDisplayName 数据成员指向一个缓冲区可以用来储存被用户选中的文件名，缓冲区的大小至少为MAX_PATH 这个常数那么大，否则遇到特别长的文件名会溢出。lpszTitle 数据对象指向一个以null结尾的字符串，字符串作为对话框的标题来显示。注意标题不要太长，否则显示时会被截断。ulFlags 标志数据对象用来限制在对话框中显示的文件夹类型。可以设定它为0或下列值的组合：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;//在对话框中会包含一个状态区，回调函数可以通过向对话框发送消息来设定状态<br><br>BIF_STATUSTEXT<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//只允许选择标准文件系统，若选了非标准的文件夹如打印机，确认按钮会变灰<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BIF_RETURNONLYFSDIRS&nbsp;&nbsp;&nbsp;=&nbsp;$0001;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//不选择网络文件夹<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BIF_DONTGOBELOWDOMAIN&nbsp;&nbsp;=&nbsp;$0002;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;给状态条留出空白<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BIF_STATUSTEXT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$0004;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;只选择文件系统的上级目录<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BIF_RETURNFSANCESTORS&nbsp;&nbsp;=&nbsp;$0008;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//只选择计算机<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BIF_BROWSEFORCOMPUTER&nbsp;&nbsp;=&nbsp;$1000;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//只选择打印机<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BIF_BROWSEFORPRINTER&nbsp;&nbsp;&nbsp;=&nbsp;$2000;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//包括文件也可以选<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BIF_BROWSEINCLUDEFILES&nbsp;=&nbsp;$4000; </td>
        </tr>
    </tbody>
</table>
<br><br>注意：如果你想对话框显示lpszTitle里的用户定制的状态条信息，必须包括 BIF_STATUSTEXT标识。Lpfn数据对象是一个回调函数类型的指针，函数类型如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;TFNBFFCallBack&nbsp;=&nbsp;function(DialogHandle:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageID:&nbsp;UINT;&nbsp;PIDL:&nbsp;PItemIDList;&nbsp;Data:&nbsp;LPARAM):Integer;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>这是一个回调函数，可以用来在同用户交互时控制和更新对话框的显示。如果你不想控制对话框，可以把它设成nil，lParam 数据对象允许你在回调函数中以参数lpfn形式返回一个指针（通常我们用它来返回对象），当然也可以把它设成为0。IImage数据成员不需要设置，因为它是用来接收系统中同文件夹相关的图标列表索引的，我们这里设它为0。<br><br>SHBrowseForFolder函数返回一个唯一的指向被选择的文件夹的PIDL。如果文件夹是一个传统的文件对象的话，可以用函数SHGetPathFromIDList把PIDL转换为真实的目录。同时，作为调用者，必须负责释放被返回的item identifier list，使用IMalloc COM 接口来释放。<br><br>注意：不要用FreeMem或其他方法来释放 PIDL ，这是因为外壳的内存管理是独立的，只能用IMalloc来释放。<br><br>现在我们已经可以显示对话框了，那让我们更深入一步看看如何能够控制对用户动作的反应，这就要用到了回调函数 TFNBFFCallBack。 注意回调函数的意思就是，你只是实现了它，系统就知道什么时候去调用它，就好比一个守株待兔的例子。<br><br>DialogHandle参数代表对话框窗口句柄。通常可以用这个句柄给对话框发消息，MessageID 参数并不是一个TMessage 结构的记录，它是对话框通过回调函数发给用户消息的，它可以是下面两个值：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;BFFM_INITIALIZED&nbsp;=&nbsp;1;&nbsp;&nbsp;&nbsp;//&nbsp;对话框将要显示<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BFFM_SELCHANGED&nbsp;&nbsp;=&nbsp;2;&nbsp;//&nbsp;用户选中了某项 </td>
        </tr>
    </tbody>
</table>
<br><br>PIDL参数包含其他的额外信息。如果MessageID 是 BFFM_INITIALIZED，PIDL将等于nil。如果MessageID是BFFM_SELCHANGED，PIDL的值将是一个PIDL 对应于用户选择的文件夹。Data 参数包含用户付给TbrowseInfo记录中的Lparam数据成员的值，通常可以传递一个对象指针。下面是一个简单的回调函数的例子：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;BrowseForFolderCallback(DialogHandle:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MessageID:&nbsp;UINT;&nbsp;PIDL:&nbsp;PItemIDList;&nbsp;Data:&nbsp;LPARAM):<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Integer;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;begin<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//响应对话框的通知消息<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;case&nbsp;(MessageID)&nbsp;of<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BFFM_INITIALIZED:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DialogInitialized(DialogHandle,&nbsp;Data);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BFFM_SELCHANGED:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HandleNewSelection(DialogHandle,&nbsp;PIDL,&nbsp;Data);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Result&nbsp;:=&nbsp;0;&nbsp;//&nbsp;总返回0.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;end; </td>
        </tr>
    </tbody>
</table>
<br><br>在回调函数里，可以根据用户的输入发送三个用户的消息给对话框，下面是消息ID:<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;改变对话框的状态信息<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BFFM_SETSTATUSTEXT&nbsp;=&nbsp;WM_USER&nbsp;+&nbsp;100;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//控制确定按钮失效与否<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BFFM_ENABLEOK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;WM_USER&nbsp;+&nbsp;101;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//改变选择的文件夹<br><br>&nbsp;&nbsp;&nbsp;&nbsp;BFFM_SETSELECTION&nbsp;&nbsp;=&nbsp;WM_USER&nbsp;+&nbsp;102; </td>
        </tr>
    </tbody>
</table>
<br><br>通常，这些消息发送给对话框使之根据用户的选择更新显示，当然你也可以发送其他的消息给对话框，比如可以发送WM_SETTEXT消息来改变对话框的标题。<br><br>下面是一个发送消息的例子（见表2.11）：<br><br>PostMessage(DialogHandle, BFFM_SETSELECTION, True, LPARAM(PChar (NewPath)));<br><br>表2.11<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#eeeeee cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>
            <table border=1 cellSpacing=0 cellPadding=0>
                <tbody>
                    <tr>
                        <td width=154>Message ID </td>
                        <td width=175>WParam </td>
                        <td width=206>LParam </td>
                    </tr>
                    <tr>
                        <td width=154>BFFM_SETSTATUSTEXT </td>
                        <td width=175>没有使用 </td>
                        <td width=206>一个指向新的状态信息的Pchar </td>
                    </tr>
                    <tr>
                        <td width=154>BFFM_ENABLEOK </td>
                        <td width=175>没有使用 </td>
                        <td width=206>True使得确认按钮有效，False无效 </td>
                    </tr>
                    <tr>
                        <td width=154>BFFM_SETSELECTION </td>
                        <td width=175>如果Lparam是路径则为True，若Lparam是PIDL则为False </td>
                        <td width=206>指向被选择的文件路径或PIDL的Pchar </td>
                    </tr>
                </tbody>
            </table>
            <br></td>
        </tr>
    </tbody>
</table>
<br><br>另外要提到的是，Delphi也提供了对这个函数的封装，那就是SelectDirectory函数。<br><br><strong>关于对话框</strong><br><br>通常我们都要在自己的程序里加上一个关于对话框来显示一些版本信息等等，Windows为我们提供了一个标准的对话框如图2.24所示，可以在一定范围内对它定制，不过它只适合显示简单的标识和文本（我觉得用处极小）。我们可以通过函数ShellAbout来调用它（声明在ShellAPI单元里），函数定义如下:<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;ShellAbout(Owner:&nbsp;HWND;&nbsp;ApplicationName:&nbsp;PChar;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;OtherText:&nbsp;PChar;&nbsp;IconHandle:&nbsp;HICON):&nbsp;Integer;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>Owner参数标识了拥有对话框的父窗体句柄，通常设为0，表明没有父窗体。ApplicationName 参数包含对话框的标题，字符串中可以包含&#8220;#&#8221;字符，它能起到分割符的作用。这种情况下，函数会把分割符前的字符串作为标题栏，分割符后的部分作为 "Microsoft"字符串后的第一行。OtherText参数包含了打算显示在Microsoft 版本和版权信息后的字符串。IconHandle 参数标识了打算显示在对话框上的图标标识，如果设为0，函数会显示Windows缺省的图标。<br><br><img src="http://www.asm32.net/pic/article/i1854_002.jpg"><br><br>图2.24<br><br><br><img src="http://www.asm32.net/pic/article/i1854_003.jpg"><br><br>图2.25<br><br>格式化对话框<br><br>SHFormatDrive函数会显示一个格式化对话框，如图2.25所示，它是一个半公开的函数。但现在它不在微软的SDK里。然而微软承认它的存在并把它从Shell32.dll里用名字公开声明，Delphi中的函数定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;SHFormatDrive(Owner:&nbsp;HWND;&nbsp;Drive:&nbsp;UINT;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FormatID:&nbsp;UINT;&nbsp;OptionFlags:&nbsp;UINT):&nbsp;DWORD;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>Owner参数标识拥有对话框的窗体句柄，文档中推荐不要设为0，但实际上好像没什么影响。Drive参数是用来标识打算格式化的驱动器的数值，它是以0为底的，从A开始 A:=0,B:=1依此类推。FormatID 参数允许我们指定一个格式化的模板，通常情况下，只要赋值为SHFMT_ID_DEFAULT就可以了。OptionFlags 参数是一个选项掩码，来确定格式化的选项。当前有两个选项：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;SHFMT_OPT_FULL&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$0001;&nbsp;//&nbsp;快速格式化<br><br>&nbsp;&nbsp;&nbsp;&nbsp;SHFMT_OPT_SYSONLY&nbsp;=&nbsp;$0002;&nbsp;//&nbsp;复制系统文件 </td>
        </tr>
    </tbody>
</table>
<br><br>如果函数调用失败，会返回下列错误中的一种来表明错误原因，错误常数如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;SHFMT_NOFORMAT&nbsp;=&nbsp;$FFFFFFFD;&nbsp;&nbsp;&nbsp;//&nbsp;驱动器无法格式化<br><br>&nbsp;&nbsp;&nbsp;&nbsp;SHFMT_CANCEL&nbsp;&nbsp;&nbsp;=&nbsp;$FFFFFFFE;&nbsp;&nbsp;&nbsp;//格式化被取消了<br><br>&nbsp;&nbsp;&nbsp;&nbsp;SHFMT_ERROR&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$FFFFFFFF;&nbsp;//其他错误 </td>
        </tr>
    </tbody>
</table>
<br><br>Windows NT 和 WideChar<br><br>在进一步研究未公开的函数前, 我们必须清楚一点，对于未公开的函数来说以null结尾的字符串类型参数大多数被声明为类型指针而不是PChar。这有点像陷阱，但必须承认这是事实。在Win 9X上所有的字符串类型参数声明为PAnsiChar，而在Windows NT上被声明为PWideChar。如果你想你的应用程序适应所有平台，你必须考虑两种情况，在运行时要判断平台类型，这是很讨厌的，但这也是使用未公开的API的代价。<br><br><strong>选择图标对话框</strong><br><br><img src="http://www.asm32.net/pic/article/i1854_004.jpg"><br><br>图2.26<br><br>我们要讨论的第一个完全未公开的函数是PickIconDlg。如图2.26所示这个函数会显示一个对话框，用户可以用来从文件中选择一个图标资源。它通常是用文件类型编辑器来关联图标和某一文件类型的，也会在快捷方式对话框中被调用来修改快捷方式的图标。这个函数从Shell32.dll 用值62来公开出来，函数定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;PickIconDlg(Owner:&nbsp;HWND;&nbsp;FileName:&nbsp;Pointer;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MaxFileNameChars:&nbsp;DWORD;&nbsp;var&nbsp;IconIndex:&nbsp;DWORD):LongBool;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>Owner参数和上面的意义类似。FileName 参数指向一个缓冲区，包含了被浏览图标的文件名，缓冲区要不小于MAX_PATH＋1。MaxFileNameChars 指定字符数量大小。IconIndex 常数是以0为底的图标索引，当对话框打开时会把焦点定在IconIndex对应的图标上，函数返回后，IconIndex指向最后被选的图标索引。如果用户点了取消按钮，函数返回False。<br><br><strong>运行程序对话框</strong><br><br><br><img src="http://www.asm32.net/pic/article/i1854_005.jpg"><br><br>图2.27<br><br>RunFileDlg函数是相当灵活的，如图2.27所示就是调用开始菜单的运行子菜单后会显示的对话框，我们通过值61把它从Shell32.dll暴露出来。下面是函数声明：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;procedure&nbsp;RunFileDlg(Owner:&nbsp;HWND;&nbsp;IconHandle:&nbsp;HICON;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WorkPath:&nbsp;Pointer;&nbsp;Caption:&nbsp;Pointer;&nbsp;Description:&nbsp;Pointer;&nbsp;Flags:&nbsp;UINT);&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>Owner参数就不用再说了。IconHandle参数是显示在对话框上的图标句柄，如果为nil，缺省的icon将会使用。WorkPath 参数指向一个字符串来指定应用程序运行的工作路径。 Title 参数指向作为对话框标题的字符串，如果为nil，就使用缺省的标题。Description 参数指向一个描述字符串，主要是告诉用户如何去做，可以设为nil，这时使用缺省的描述。 Flags参数用一组位掩码来设定对话框的属性。下面是定义：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;RFF_NOBROWSE&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$01;&nbsp;&nbsp;&nbsp;//&nbsp;移去浏览按钮<br><br>&nbsp;&nbsp;&nbsp;&nbsp;RFF_NODEFAULT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$02;&nbsp;&nbsp;&nbsp;//&nbsp;无缺省的选项<br><br>&nbsp;&nbsp;&nbsp;&nbsp;RFF_CALCDIRECTORY&nbsp;=&nbsp;$04;&nbsp;&nbsp;&nbsp;//&nbsp;由文件名确定工作路径<br><br>&nbsp;&nbsp;&nbsp;&nbsp;RFF_NOLABEL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$08;&nbsp;&nbsp;&nbsp;//&nbsp;去掉编辑框标签<br><br>&nbsp;&nbsp;&nbsp;&nbsp;RFF_NOSEPARATEMEM&nbsp;=&nbsp;$20;&nbsp;//&nbsp;去掉在单独的内存空间运行的复选框&nbsp;(只对NT有效) </td>
        </tr>
    </tbody>
</table>
<br><br>这个对话框一个很好的特性是允许你控制用户可以运行的应用程序。当用户选择了确认按钮，对话框的父窗体会发送一个通知消息来传递将要运行的程序信息。通知消息是一个 WM_NOTIFY消息，它的通知代码设定为RFN_VALIDATE (-510)，然后lParam指向一个 TNM_RunFileDlg记录。定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;TNM_RunFileDlg&nbsp;=&nbsp;packed&nbsp;record<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hdr:&nbsp;TNMHdr;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpFile:&nbsp;Pointer;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lpDirectory:&nbsp;Pointer;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nShow:&nbsp;LongBool;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;end; </td>
        </tr>
    </tbody>
</table>
<br><br>hdr数据对象是TNMHdr类型，它是一种标准的Windows数据类型，每个WM_NOTIFY消息的lParam参数都会指向这个数据成分。同时根据不同的消息类型，可能一些额外的数据跟在记录后面，标准的TNMHdr记录定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;TNMHdr&nbsp;=&nbsp;packed&nbsp;record<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hwndFrom:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;idFrom:&nbsp;UINT;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;code:&nbsp;UINT;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;end; </td>
        </tr>
    </tbody>
</table>
<br><br>记录中的hwndFrom包含发送消息的窗口句柄，idFrom则包含发送消息的控件标示符，code 中包含标识被发送的消息的通知代码。<br><br>在TNMHdr记录后被打包的额外数据包含三个数据成分：LpFile指向一个包含将要运行的文件的路径字符串；LpDirectory指向正在运行程序的工作目录字符串；最后，nShow 用来指定将要运行的应用程序是否可见。<br><br>对于本文中特定的消息，只对TNMHdr记录中的Code感兴趣，通过检验Code可以确保我们收到一个运行文件校验消息，同时使我们可以存取额外的TNM_RunFileDlg数据成员。当TNMHdr记录中的code等于RFN_VALIDATE(-510)时，可以获得一个TNM_RunFileDlg记录。下面是校验消息的代码：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;var<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileToRun:&nbsp;String;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;...<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;TheMessage.Msg&nbsp;=&nbsp;WM_NOTIFY&nbsp;then<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;PNMHdr(TheMessage.LParam).code&nbsp;=&nbsp;RFN_VALIDATE&nbsp;then<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;WideCharToStrVar(PNM_RUNFILEDLG(<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TheMessage.LParam).lpFile,&nbsp;FileToRun);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;... </td>
        </tr>
    </tbody>
</table>
<br><br>注意只有当我们已经检验TNMHdr的Code为RFN_VALIDATE后，才映射LParam 参数为PNM_RunFileDlg类型。<br><br>通知消息的返回值决定了应用程序是否能够运行，下面是可能的值：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;RF_OK&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$00;&nbsp;//允许程序运行<br><br>&nbsp;&nbsp;&nbsp;&nbsp;RF_CANCEL&nbsp;=&nbsp;$01;&nbsp;&nbsp;&nbsp;//取消操作，关闭对话框<br><br>&nbsp;&nbsp;&nbsp;&nbsp;RF_RETRY&nbsp;&nbsp;=&nbsp;$02;&nbsp;//取消操作，对话框仍然打开 </td>
        </tr>
    </tbody>
</table>
<br><br><strong>查找文件对话框</strong><br><br><br><img src="http://www.asm32.net/pic/article/i1854_006.jpg"><br><br>图2.28<br><br>调用查找文件对话框的函数是SHFindFiles，对话框如图2.28所示。它是从Shell32.dll按索引值90公开出来的:<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;SHFindFiles(SearchRoot:&nbsp;PItemIDList;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;SavedSearchFile:&nbsp;PItemIDList):&nbsp;LongBool;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>SearchRoot 参数允许从一个特定的文件夹开始查找，同在资源管理器中在文件夹上用右键点击查找菜单的效果是一样的。如果设为nil，那么查找是从桌面开始的。 SavedSearchFile 参数让你指定一个以前查询保存的查找策略文件（*.fnd文件），根据以前的设定来查找，若不需要的话可以设定为nil。如果你指定了一个非空值的SearchRoot PIDL，那么在调用完SHFindFiles后必须负责释放掉。但是有点奇怪的是，如果你指定了一个非空的SavedSearchFile PIDL参数，函数成功调用的话，你不能去释放这个 PIDL，否则会出错，但如果调用失败了的话，你必须释放它。<br><br>同大多数对话框函数不一样，这个函数是非模态的，也就是系统在另外一个独立的线程中启动对话框，然后立即返回，对话框会在你的程序结束后自动关闭。也就是说你没有任何直接的方法来告诉用户如何使用查找到的结果，所以要想知道用户找到的文件的话，最好是让你的程序支持文件拖放，以便让用户把找到的文件拖放给你。<br><br><strong>查找电脑对话框</strong><br><br>同SHFindFiles比较接近的一个函数是SHFindComputer，这个函数调用的结果同开始菜单上查找电脑菜单调用的结果是一样的。它的参数同SHFindFiles完全一样，不同之处在于它完全忽略传递给它的参数，很显然是保留起来为了将来扩展的需要。这里我们只要把参数都设成nil就可以了，另外注意这个对话框也是非模态的。 SHFindComputer 是从Shell32.dll 以索引号91公开出来的：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;SHFindComputer(Reserved1:&nbsp;PItemIDList;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Reserved2:&nbsp;PItemIDList):&nbsp;LongBool;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br><br><strong>查找文件对话框</strong><br><br>通过调用GetFileNameFromBrowse函数可以调出这个对话框，不过说实在的，它实际上只是GetOpenFileName 函数的简单封装。而我们常用的TOpenDialog控件也是对GetOpenFileName 函数封装，这个函数我们很少会去直接用它。不过还是写出来吧，它是从Shell32.dll里按索引值63公开出来的：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;GetFileNameFromBrowse(Owner:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FileName:&nbsp;Pointer;&nbsp;MaxFileNameChars:&nbsp;DWORD;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;InitialDirectory:&nbsp;Pointer;&nbsp;DefaultExtension:&nbsp;Pointer;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Filter:&nbsp;Pointer;&nbsp;Caption:&nbsp;Pointer):&nbsp;LongBool;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br><img src="http://www.asm32.net/pic/article/i1854_007.jpg"><br><br>图2.29<br><br>大多数参数对应于OPENFILENAME 结构的成员。Owner参数我想就不用再重复了， FileName 参数指向一个初始化对话框编辑控制文件名的缓冲区，函数返回后FileName包含被选择的文件路径，它的大小一般设成MAX_PATH＋1那么大。MaxFileNameChars 参数用来指定FileName缓冲区的大小。 InitialDirectory参数指向对话框初始化的目录名，但如果FileName参数被指定了，InitialDirectory就会被忽略而使用FileName参数中的路径。DefaultExtension参数指向一个包含要搜索的缺省扩展名的字符串。Filter参数指向一个以null结尾的可以用来在下拉列表中限定文件类型的过滤字符串。Caption参数指向对话框标题字符串。<br><br>如果用户选择了一个要打开的文件，函数返回True，当有错误发生，用户选择取消按钮或关闭对话框的话会返回False。<br><br><strong>外壳对象属性对话框</strong><br><br>另一个未公开的对话框函数是SHObjectProperties，它可以用来显示外壳对象的属性，比如驱动器、文件夹或文件等，运行效果如图2.29所示。函数可以从Shell32.dll中按索引值178公开出来，定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;SHObjectProperties(Owner:&nbsp;HWND;&nbsp;Flags:&nbsp;UINT;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ObjectName:&nbsp;Pointer;&nbsp;InitialTabName:&nbsp;Pointer):LongBool;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>Flags参数用来指定ObjectName参数对应对象的类型，它可以是下列标识：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;//打印机<br><br>&nbsp;&nbsp;&nbsp;&nbsp;OPF_PRINTERNAME&nbsp;=&nbsp;$01;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;//路径<br><br>&nbsp;&nbsp;&nbsp;&nbsp;OPF_PATHNAME&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$02; </td>
        </tr>
    </tbody>
</table>
<br><br>ObjectName参数指向一个包含路径名的字符串或是要显示属性的打印机名。如果打印机是本地的，可以使用实际的打印机名，如果是网络打印机，就需要使用完整的UNC样式名称，比如\\COMPUTERNAME\PRINTERNAME。InitialTabName参数指向一个属性对话框中页面名称字符串，用来指定要显示的缺省页面。如果InitialTabName参数为nil，或不匹配任何页面的名称，第一个属性页面将会被显示。<br><br>如果函数调用成功会返回True，如果失败会返回False。要想获得扩展的错误信息，可以调用API函数GetLastError。要注意的是这个对话框是非模态的，类似于查找文件对话框，所以函数一被调用，就肯定会显示一个对话框，同时我们没有办法知道用户什么时候关闭了对话框。<br><br><strong>映射网络驱动对话框</strong><br><br><br><br><img src="http://www.asm32.net/pic/article/i1854_008.jpg"><br><br>图2.30<br><br>图2.30显示了映射网络驱动器的对话框，我们通过SHNetConnectionDialog函数调用它（win 9x和Win NT上都支持），它可以按索引值160从Shell32.dll暴露出来，函数定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;SHNetConnectionDialog(Owner:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResourceName:&nbsp;Pointer;&nbsp;ResourceType:&nbsp;DWORD):&nbsp;DWORD;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>SHStartNetConnectionDialog函数也会显示同样的对话框，但它显示的对话框是非模态的，同时只在NT上才支持。它可以按索引值215从Shell32.dll中公开出来，函数定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;SHStartNetConnectionDialog(Owner:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ResourceName:&nbsp;PWideChar;&nbsp;ResourceType:&nbsp;DWORD):DWORD;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>上面两个函数的参数完全相同。其中ResourceName参数指向一个要连接的网络资源UNC路径名。指定了这个参数的话，显示的对话框中被预设的连接资源就不可改变了。如果这个参数为nil，则在对话框中用户可以指定要连接的资源。ResourceType参数可以是下面的值之一：RESOURCETYPE_DISK或RESOURCETYPE_PRINT。它的不同将会生成不同的对话框。参数为RESOURCETYPE_DISK允许我们为网络驱动资源指定一个盘符，另一个参数允许我们映射一个并行口名比如LPT2为一个网络打印机。然而，不知道为什么RESOURCETYPE_PRINT参数在NT上无效。<br><br><br><img src="http://www.asm32.net/pic/article/i1854_009.jpg"><br><br>图2.31<br><br>如果函数调用成功的话，返回值是NO_ERROR，如果用户取消的对话框，则返回 -1($FFFFFFFF)，如果调用失败则返回其他的错误代码，具体错误信息可以用GetLastError API调用获得。<br><br><strong>关闭系统对话框</strong><br><br>ExitWindowsDialog和RestartDialog函数可以用来显示关闭和重启系统对话框（如图2.31），它们同公开的ExitWindowsEx API函数没有什么太大的不同，但在其过程中都会产生一个对话框。ExitWindowsDialog函数可以按索引值60从Shell32.dll中公开出来，RestartDialog函数的在Shell32.dll中的索引值则是59，两个函数的定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;procedure&nbsp;ExitWindowsDialog(Owner:&nbsp;HWND);&nbsp;stdcall;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;RestartDialog(Owner:&nbsp;HWND;&nbsp;Reason:&nbsp;Pointer;&nbsp;ExitType:&nbsp;UINT):&nbsp;DWORD;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>对ExitWindowsDialog函数来说，对话框好像并不使用Owner参数作为父窗口，在Windows 95上，当操作成功的话owner窗口会收到一个WM_QUIT消息。在Windows NT上，owner 窗口根本不被使用。同时这个函数没有返回值，所以没有办法知道用户选择了什么操作以及操作是否被取消了。<br><br>RestartDialog函数更有用一些，当我们修改了系统的设置，并希望重新启动系统使修改生效的时候可以使用这个函数。Reason参数指向一个要显示在对话框中的字符串，用来解释关闭系统的原因。ExitType参数指定关闭类型，可以使用ExitWindowsEX函数使用值的一个子集及额外的几个新值，下面是它们的完全列表：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;EWX_LOGOFF&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$00;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;EWX_SHUTDOWN&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$01;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;EWX_REBOOT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;=&nbsp;$02;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;EW_RESTARTWINDOWS&nbsp;=&nbsp;$42;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;EW_REBOOTSYSTEM&nbsp;&nbsp;&nbsp;=&nbsp;$43;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;EW_EXITANDEXECAPP&nbsp;=&nbsp;$44; </td>
        </tr>
    </tbody>
</table>
<br><br>如果用户选择执行关闭操作，函数返回IDYES，否则返回IDNO。<br><br>要注意的是显示在对话框中的原因字符串后总会跟着一个系统缺省提供的字符串用来显示确认信息，所以应该在我们的Reason字符串后附上空格或回车换行字符。另外返回值不能用于确定操作的成功性，它只表明用户的选择，如果重启操作由于某些原因失败了，返回值仍然是IDYES。同时要注意的是要想调用成功，用户还必须有SE_SHUTDOWN_NAME权限（在NT上）。<br><br><strong>缺少内存对话框</strong><br><br>SHOutOfMemoryMessageBox是一个未公开的函数，当系统内存不足时可以用来显示标准的外壳信息对话框，它在Shell32.dll中的索引值是126，函数定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;SHOutOfMemoryMessageBox(Owner:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Caption:&nbsp;Pointer;&nbsp;Style:&nbsp;UINT):&nbsp;Integer;&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>它会调用MessageBox API，同时传递3个标准的参数和ERROR_OUTOFMEMORY错误消息。Caption参数指向对话框标题字符串。如果Caption为nil，父窗口的标题就会被使用。Style参数可以被设置为任意MessageBox函数使用的MB_XXX常数的组合，通常设置它为MB_OK或MB_ICONHAND。函数调用返回值参见SDK中MessageBox函数说明。<br><br>当MessageBox函数被调用时，MB_SETFOREGROUND标识会被添加到Style参数中，但如果第一次调用失败了的话，MessageBox函数会被再次调用，这次MB_SYSTEMMODAL 标识会被添加到Style参数中。MB_SYSTEMMODAL同MB_ICONHAND标识结合后会忽略内存状况来显示消息对话框。当内存确实不足时，函数不会显示任何东西，然而它仍然会返回MessageBox函数调用结果。所以我们可以根据返回值判断函数是否调用成功了。<br><br><strong>空间不足对话框</strong><br><br><br><img src="http://www.asm32.net/pic/article/i1854_010.gif"><br><br>图2.32<br><br>另一个资源相关的函数是SHHandleDiskFull，它会显示磁盘不足的信息对话框（如图2.32）。我们可以在由于没有足够磁盘空间时导致程序无法运行的条件下调用这个函数，调用后，如果回收站中有什么东西没有删除的话，对话框允许用户清空回收站来释放磁盘空间。它在Shell32.dll中的索引值为185，函数的定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;procedure&nbsp;SHHandleDiskFull(Owner:&nbsp;HWND;&nbsp;Drive:&nbsp;UINT);&nbsp;stdcall; </td>
        </tr>
    </tbody>
</table>
<br><br>Drive参数用于指定以0为底的驱动器盘符。0代表A:\，1代表B:\，依此类推。这个函数的应用比较困难，因为当回收站中没有任何东西时对话框不会显示，同时也没有任何返回值表示对话框是否显示，还无法知道用户的操作，比如它是否真的清空了。看起来比较可行的应用只能是程序自行监视磁盘剩余空间，只是使用这个对话框作为一个快速修复的工具。<br><br><strong>一般外壳消息对话框</strong><br><br>ShellMessageBox函数仅仅是一个对MessageBox函数的简单封装函数，它允许使用字符串资源标识符或标准的以null结尾的字符串，同时还允许加入支持格式化ForamtMessage函数的控制符。ShellMessageBox函数在Shell32.dll中的索引值为183：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;ShellMessageBoxA(Module:&nbsp;THandle;&nbsp;Owner:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Text:&nbsp;PChar;&nbsp;Caption:&nbsp;PChar;&nbsp;Style:&nbsp;UINT;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Parameters:&nbsp;array&nbsp;of&nbsp;Pointer):&nbsp;Integer;&nbsp;cdecl; </td>
        </tr>
    </tbody>
</table>
<br><br>更确切地说这个函数应该叫ShellMessageBoxA因为它只支持ANSI字符串，还有一个UNICODE的版本的函数ShellMessageBoxW，它的索引值为182，但它只在Windows NT上才有，函数定义如下：<br><br>
<table style="BORDER-COLLAPSE: collapse; font-face: fixsys" class=ArticleText border=1 cellSpacing=0 borderColor=#000000 cellPadding=5 width="100%">
    <tbody>
        <tr>
            <td bgColor=#eeeeee>&nbsp;&nbsp;&nbsp;&nbsp;function&nbsp;ShellMessageBoxW(Module:&nbsp;THandle;&nbsp;Owner:&nbsp;HWND;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Text:&nbsp;PWideChar;&nbsp;Caption:&nbsp;PWideChar;&nbsp;Style:&nbsp;UINT;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Parameters:&nbsp;array&nbsp;of&nbsp;Pointer):&nbsp;Integer;&nbsp;cdecl; </td>
        </tr>
    </tbody>
</table>
<br><br>Module参数是提供字符串资源的模块句柄，句柄可以用GetModuleHandle函数获得。顾名思义Text 参数指向一个要显示在对话框中的文本，它也可以是资源字符串ID，文本中可以包括格式控制序列，它将会被在Parameters中提供的额外字符串替代。控制符格式为&#8220;%#&#8221;，其中&#8220;#&#8221;是额外字符串在参数中的位置，比如&#8220;%1&#8221;将被第一个Parameters数组中的字符串元素替代，&#8220;%3&#8221;将会被第三个元素替代，依此类推。Caption参数指向对话框标题文本，同样它也可以是资源ID，如果参数为nil，Owner指定的窗口标题将被用于对话框标题。Style参数是由位掩码标识组成的，可以设置成MessageBox函数支持的MB_XXX常数的组合。返回值同MessageBox完全一样。<br><br>对于这个函数很重要的一点是微软公司使用cdecl来输出这个函数而不是通常的 stdcall。此外，Parameters参数使用了C语言中的可变参数列表，这意味着这个函数不是语言无关的，这使得调用起来非常麻烦，因为Delphi并不直接支持cdecl和可变参数列表。为了解决这个问题，Parameters参数被映射为一个动态指针列表。同时我们还需要使用嵌入式汇编来建立cdecl样式的堆栈。由于动态指针列表的性质，我们必须至少指定一个指针值。如果不想指定要替代的字符串，简单设置为nil就可以了。</font>
<img src ="http://www.cppblog.com/justin-shi/aggbug/103441.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2009-12-18 09:20 <a href="http://www.cppblog.com/justin-shi/archive/2009/12/18/103441.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struct DCB</title><link>http://www.cppblog.com/justin-shi/archive/2009/05/05/81909.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Mon, 04 May 2009 18:35:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2009/05/05/81909.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/81909.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2009/05/05/81909.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/81909.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/81909.html</trackback:ping><description><![CDATA[<div class=articleContent id=articleBody>
<p><font color=#0000ff>串口通讯中的DCB结构</font></p>
<p><font color=#0000ff>typedef struct _DCB {// dcb</font></p>
<p><font color=#0000ff>DWORD DCBlength; // sizeof(DCB)</font></p>
<p><font color=#0000ff>DORD BaudRate; // current baud rate　指定当前的波特率</font></p>
<p><font color=#0000ff>DWORD fBinary: 1; // binary mode, no EOF check　指定是否允许二进制模式WIN95中须为TRUE<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr></font></p>
<p><font color=#0000ff>DWORD fParity: 1; // enable parity checking　指定奇偶校验是否允许</font></p>
<p><font color=#0000ff>DWORD fOutxCtsFlow:1; // CTS output flow control 指定CTS是否用于检测发送控制。当为TRUE是CTS为OFF，发送将被挂起。</font></p>
<p><br><font color=#0000ff>DWORD fOutxDsrFlow:1; // DSR output flow control&nbsp;<wbr>&nbsp;<wbr> 指定CTS是否用于检测发送控制。当为TRUE是CTS为OFF，发送将被挂起。</font></p>
<p><br><font color=#0000ff>DWORD fDtrControl:2; // DTR flow control type</font></p>
<p><font color=#0000ff>DTR_CONTROL_DISABLE 值将DTR置为OFF,DTR_CONTROL_ENABLE值将DTR置为ON,&nbsp;<wbr></font></p>
<p><font color=#0000ff>DTR_CONTROL_HANDSHAKE 允许DTR"握手",</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>DWORD fDsrSensitivity:1;// DSR sensitivity 当该值为TRUE时DSR为OFF时接收的字节被忽略</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>DWORD fTXContinueOnXoff:1; // XOFF continues Tx<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 指定当接收缓冲区已满,并且驱动程序已经发送出XoffChar字符时发送是否停止。</font></p>
<p><font color=#0000ff>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TRUE时，在接收缓冲区接收到缓冲区已满的字节XoffLim且驱动程序已经发送出XoffChar字&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 符中止接收字节之后，发送继续进行。<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> FALSE时，在接收缓冲区接收到代表缓冲区已空的字节XonChar且驱动程序已经发送出恢复发送的XonChar之后，发送继续进行。</font></p>
<p><br><font color=#0000ff>DWORD fOutX: 1; // XON/XOFF out flow control&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> TRUE时，接收到XoffChar之后便停止发送接收到XonChar之后将重新开始</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>DWORD fInX: 1; // XON/XOFF in flow control<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>TRUE时，接收缓冲区接收到代表缓冲区满的XoffLim之后，XoffChar发送出去接收缓冲区接收到代表缓冲区空的XonLim之后，XonChar发送出去</font></p>
<p><br><font color=#0000ff>DWORD fErrorChar: 1; // enable error replacement<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 　该值为TRUE且fParity为TRUE时，用ErrorChar 成员指定的字符代替奇偶校验错误的接收字符</font></p>
<p><br><font color=#0000ff>DWORD fNull: 1; // enable null stripping　　TRUE时，接收时去掉空（0值）字节</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>DWORD fRtsControl:2; // RTS flow control</font></p>
<p><br><font color=#0000ff>DWORD fAbortOnError:1; // abort reads/writes on error　　TRUE时,有错误发生时中止读和写操作RTS_CONTROL_DISABLE时,RTS置为OFFRTS_CONTROL_ENABLE时, RTS置为ON</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>RTS_CONTROL_HANDSHAKE时,当接收缓冲区小于半满时RTS为ON当接收缓冲区超过四分之三满时RTS为OFF</font></p>
<p><br><font color=#0000ff>RTS_CONTROL_TOGGLE时,当接收缓冲区仍有剩余字节时RTS为ON ,否则缺省为OFF</font></p>
<p><br><font color=#0000ff>DWORD fDummy2:17; // reserved　　未使用</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>WORD wReserved; // not currently used　　未使用,必须为0</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>WORD XonLim; // transmit XON threshold&nbsp;<wbr>&nbsp;<wbr> 指定在XON字符发送这前接收缓冲区中可允许的最小字节数</font></p>
<p><br><font color=#0000ff>WORD XoffLim; // transmit XOFF threshold&nbsp;<wbr> 指定在XOFF字符发送这前接收缓冲区中可允许的最小字节数</font></p>
<p><br><font color=#0000ff>BYTE ByteSize; // number of bits/byte, 4-8　　指定端口当前使用的数据位</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>BYTE Parity; // 0-4=no,odd,even,mark,space　指定端口当前使用的奇偶校验方法,可能为:<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> EVENPARITY,MARKPARITY,NOPARITY,ODDPARITY　BYTE</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>StopBits;&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> 0,1,2 = 1, 1.5, 2 　　指定端口当前使用的停止位数,可能为:<br>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr>&nbsp;<wbr> ONESTOPBIT,ONE5STOPBITS,TWOSTOPBITS</font></p>
<p><br><font color=#0000ff>char XonChar; // Tx and Rx XON character　　指定用于发送和接收字符XON的值</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>char XoffChar; // Tx and Rx XOFF character　　指定用于发送和接收字符XOFF值</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>char ErrorChar; // error replacement character本字符用来代替接收到的奇偶校验发生错误时的值</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>char EofChar; // end of input character　当没有使用二进制模式时,本字符可用来指示数据的结束</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>char EvtChar; // received event character　当接收到此字符时,会产生一个事件</font></p>
<p>&nbsp;<wbr></p>
<p><font color=#0000ff>WORD wReserved1; // reserved; do not use 未使用</font></p>
<p><font color=#0000ff>} DCB;<br></font></p>
</div>
<!--   -->
<img src ="http://www.cppblog.com/justin-shi/aggbug/81909.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2009-05-05 02:35 <a href="http://www.cppblog.com/justin-shi/archive/2009/05/05/81909.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>技术备忘</title><link>http://www.cppblog.com/justin-shi/archive/2009/03/13/76505.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Fri, 13 Mar 2009 11:56:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2009/03/13/76505.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/76505.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2009/03/13/76505.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/76505.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/76505.html</trackback:ping><description><![CDATA[DLL注入程序的一般步骤：<br><br>（1）取得宿主进程（即要注入木马的进程）的进程ID dwRemoteProcessId；<br><br>（2）取得DLL的完全路径，并将其转换为宽字符模式pszLibFileName；<br><br>（3）利用Windows API OpenProcess打开宿主进程，应该开启下列选项：<br><br><span>a.PROCESS_CREATE_THREAD：允许在宿主进程中创建线程；</span><br><br>b.PROCESS_VM_OPERATION：允许对宿主进程中进行VM操作；<br><br>c.PROCESS_VM_WRITE：允许对宿主进程进行VM写。<br><br><span>（4）利用Windows API VirtualAllocEx函数在远程线程的VM中分配DLL完整路径宽字符所需的存储空间，并利用Windows API WriteProcessMemory函数将完整路径写入该存储空间；</span><br><br>（5）利用Windows API GetProcAddress取得Kernel32模块中LoadLibraryW函数的地址，这个函数将作为随后将启动的远程线程的入口函数；<br><br><span>（6）利用Windows API CreateRemoteThread启动远程线程，将LoadLibraryW的地址作为远程线程的入口函数地址，将宿主进程里被分配空间中存储的完整DLL路径作为线程入口函数的参数以另其启动指定的DLL；</span><br><br><span>（7）清理现场。</span> 
<img src ="http://www.cppblog.com/justin-shi/aggbug/76505.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2009-03-13 19:56 <a href="http://www.cppblog.com/justin-shi/archive/2009/03/13/76505.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>卸载其它进程的DLL</title><link>http://www.cppblog.com/justin-shi/archive/2009/02/21/74536.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Sat, 21 Feb 2009 14:32:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2009/02/21/74536.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/74536.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2009/02/21/74536.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/74536.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/74536.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: #000000">typedef&nbsp;ULONG&nbsp;(WINAPI&nbsp;</span><span style="COLOR: #000000">*</span><span style="COLOR: #000000">PFNNtUnmapViewOfSection)(&nbsp;IN&nbsp;HANDLE&nbsp;ProcessHandle,IN&nbsp;PVOID&nbsp;BaseAddress&nbsp;);<br></span><span style="COLOR: #008080">&nbsp;2</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">&nbsp;3</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top>BOOL&nbsp;UnmapViewOfModule&nbsp;(&nbsp;DWORD&nbsp;dwProcessId,&nbsp;LPVOID&nbsp;lpBaseAddr&nbsp;)<br></span><span style="COLOR: #008080">&nbsp;4</span><span style="COLOR: #000000"><img id=Codehighlighter1_165_648_Open_Image onclick="this.style.display='none'; Codehighlighter1_165_648_Open_Text.style.display='none'; Codehighlighter1_165_648_Closed_Image.style.display='inline'; Codehighlighter1_165_648_Closed_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ExpandedBlockStart.gif" align=top><img id=Codehighlighter1_165_648_Closed_Image style="DISPLAY: none" onclick="this.style.display='none'; Codehighlighter1_165_648_Closed_Text.style.display='none'; Codehighlighter1_165_648_Open_Image.style.display='inline'; Codehighlighter1_165_648_Open_Text.style.display='inline';" src="http://www.cppblog.com/Images/OutliningIndicators/ContractedBlock.gif" align=top><img src="http://www.cppblog.com/Images/dot.gif"></span><span id=Codehighlighter1_165_648_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_165_648_Open_Text><span style="COLOR: #000000">{<br></span><span style="COLOR: #008080">&nbsp;5</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;HMODULE&nbsp;hModule&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;GetModuleHandle&nbsp;(&nbsp;L</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ntdll.dll</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&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;</span><span style="COLOR: #0000ff">if</span><span style="COLOR: #000000">&nbsp;(&nbsp;hModule&nbsp;</span><span style="COLOR: #000000">==</span><span style="COLOR: #000000">&nbsp;NULL&nbsp;)<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;hModule&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;LoadLibrary&nbsp;(&nbsp;L</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">ntdll.dll</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&nbsp;)&nbsp;;<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><br></span><span style="COLOR: #008080">&nbsp;9</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;PFNNtUnmapViewOfSection&nbsp;pfnNtUnmapViewOfSection&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;(PFNNtUnmapViewOfSection)GetProcAddress&nbsp;(&nbsp;hModule,&nbsp;</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">NtUnmapViewOfSection</span><span style="COLOR: #000000">"</span><span style="COLOR: #000000">&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;<br></span><span style="COLOR: #008080">11</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;HANDLE&nbsp;hProcess&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;OpenProcess&nbsp;(&nbsp;PROCESS_ALL_ACCESS,&nbsp;TRUE,&nbsp;dwProcessId&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;ULONG&nbsp;&nbsp;&nbsp;&nbsp;ret&nbsp;</span><span style="COLOR: #000000">=</span><span style="COLOR: #000000">&nbsp;pfnNtUnmapViewOfSection&nbsp;(&nbsp;hProcess,&nbsp;lpBaseAddr&nbsp;)&nbsp;;<br></span><span style="COLOR: #008080">13</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/InBlock.gif" align=top>&nbsp;&nbsp;&nbsp;&nbsp;CloseHandle&nbsp;(&nbsp;hProcess&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;</span><span style="COLOR: #0000ff">return</span><span style="COLOR: #000000">&nbsp;ret&nbsp;</span><span style="COLOR: #000000">?</span><span style="COLOR: #000000">&nbsp;FALSE&nbsp;:&nbsp;TRUE;<br></span><span style="COLOR: #008080">15</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">16</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top><br></span><span style="COLOR: #008080">17</span><span style="COLOR: #000000"><img src="http://www.cppblog.com/Images/OutliningIndicators/None.gif" align=top></span></div>
<img src ="http://www.cppblog.com/justin-shi/aggbug/74536.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2009-02-21 22:32 <a href="http://www.cppblog.com/justin-shi/archive/2009/02/21/74536.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Heap: Pleasures and Pains</title><link>http://www.cppblog.com/justin-shi/archive/2009/02/03/72832.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Tue, 03 Feb 2009 00:49:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2009/02/03/72832.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/72832.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2009/02/03/72832.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/72832.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/72832.html</trackback:ping><description><![CDATA[本文转自: <a href="http://hi.baidu.com/csuhkx/blog/item/267418d3614cf9013bf3cf55.html">http://hi.baidu.com/csuhkx/blog/item/267418d3614cf9013bf3cf55.html</a><br><br>这篇文章是翻译MSDN上一篇叫《Heap: Pleasures and Pains》的文章的 <br><br>
<p>Murali R. Krishnan<br>Microsoft Corporation</p>
<p>1999 年 2 月</p>
<p><strong>摘要：</strong> 讨论常见的堆性能问题以及如何防范它们。（共 9 页）</p>
<h2>前言</h2>
<p>您是否是动态分配的 C/C++ 对象忠实且幸运的用户？您是否在模块间的往返通信中频繁地使用了&#8220;自动化&#8221;？您的程序是否因堆分配而运行起来很慢？不仅仅您遇到这样的问题。几乎所有项目迟早都会遇到堆问题。大家都想说，&#8220;我的代码真正好，只是堆太慢&#8221;。那只是部分正确。更深入理解堆及其用法、以及会发生什么问题，是很有用的。</p>
<h2><a name=heap3_whatis></a>什么是堆？</h2>
<p>（如果您已经知道什么是堆，可以跳到<a href="http://www.microsoft.com/china/MSDN/library/archives/library/techart/heap3.asp#heap3_commonproblems"><font color=#800080><u>&#8220;什么是常见的堆性能问题？&#8221;</u></font></a>部分）</p>
<p>在程序中，使用堆来动态分配和释放对象。在下列情况下，调用堆操作： </p>
<ol>
    <li>事先不知道程序所需对象的数量和大小。<br><br>
    <li>对象太大而不适合堆栈分配程序。 </li>
</ol>
<p>堆使用了在运行时分配给代码和堆栈的内存之外的部分内存。下图给出了堆分配程序的不同层。</p>
<p><img style="WIDTH: 345px; HEIGHT: 359px" height=359 alt="" src="http://www.cppblog.com/images/cppblog_com/justin-shi/6156/o_heap1.jpg" width=345 border=0></p>
<p><strong>GlobalAlloc/GlobalFree：</strong>Microsoft Win32 堆调用，这些调用直接与每个进程的默认堆进行对话。</p>
<p><strong>LocalAlloc/LocalFree：</strong>Win32 堆调用（为了与 Microsoft Windows NT 兼容），这些调用直接与每个进程的默认堆进行对话。</p>
<p><strong>COM 的 IMalloc 分配程序（或 CoTaskMemAlloc / CoTaskMemFree）：</strong>函数使用每个进程的默认堆。自动化程序使用&#8220;组件对象模型 (COM)&#8221;的分配程序，而申请的程序使用每个进程堆。</p>
<p><strong>C/C++ 运行时 (CRT) 分配程序：</strong>提供了 <strong>malloc()</strong> 和 <strong>free()</strong> 以及 <strong>new</strong> 和 <strong>delete</strong> 操作符。如 Microsoft Visual Basic 和 Java 等语言也提供了新的操作符并使用垃圾收集来代替堆。CRT 创建自己的私有堆，驻留在 Win32 堆的顶部。</p>
<p>Windows NT 中，Win32 堆是 Windows NT 运行时分配程序周围的薄层。所有 API 转发它们的请求给 NTDLL。</p>
<p>Windows NT 运行时分配程序提供 Windows NT 内的核心堆分配程序。它由具有 128 个大小从 8 到 1,024 字节的空闲列表的前端分配程序组成。后端分配程序使用虚拟内存来保留和提交页。</p>
<p>在图表的底部是&#8220;虚拟内存分配程序&#8221;，操作系统使用它来保留和提交页。所有分配程序使用虚拟内存进行数据的存取。</p>
<p>分配和释放块不就那么简单吗？为何花费这么长时间？</p>
<h2><a name=heap3_heapimplementation></a>堆实现的注意事项</h2>
<p>传统上，操作系统和运行时库是与堆的实现共存的。在一个进程的开始，操作系统创建一个默认堆，叫做&#8220;进程堆&#8221;。如果没有其他堆可使用，则块的分配使用&#8220;进程堆&#8221;。语言运行时也能在进程内创建单独的堆。（例如，C 运行时创建它自己的堆。）除这些专用的堆外，应用程序或许多已载入的动态链接库 (DLL) 之一可以创建和使用单独的堆。Win32 提供一整套 API 来创建和使用私有堆。有关<a href="http://msdn.microsoft.com/library/psdk/winbase/memman_0ulv.htm"><font color=#800080><u>堆函数（英文）</u></font></a>的详尽指导，请参见 MSDN。</p>
<p>当应用程序或 DLL 创建私有堆时，这些堆存在于进程空间，并且在进程内是可访问的。从给定堆分配的数据将在同一个堆上释放。（不能从一个堆分配而在另一个堆释放。）</p>
<p>在所有虚拟内存系统中，堆驻留在操作系统的&#8220;虚拟内存管理器&#8221;的顶部。语言运行时堆也驻留在虚拟内存顶部。某些情况下，这些堆是操作系统堆中的层，而语言运行时堆则通过大块的分配来执行自己的内存管理。不使用操作系统堆，而使用虚拟内存函数更利于堆的分配和块的使用。</p>
<p>典型的堆实现由前、后端分配程序组成。前端分配程序维持固定大小块的空闲列表。对于一次分配调用，堆尝试从前端列表找到一个自由块。如果失败，堆被迫从后端（保留和提交虚拟内存）分配一个大块来满足请求。通用的实现有每块分配的开销，这将耗费执行周期，也减少了可使用的存储空间。</p>
<p>Knowledge Base 文章 Q10758，&#8220;用 calloc() 和 malloc() 管理内存&#8221; （搜索文章编号）, 包含了有关这些主题的更多背景知识。另外，有关堆实现和设计的详细讨论也可在下列著作中找到：&#8220;Dynamic Storage Allocation: A Survey and Critical Review&#8221;，作者 Paul R. Wilson、Mark S. Johnstone、Michael Neely 和 David Boles；&#8220;International Workshop on Memory Management&#8221;, 作者 Kinross, Scotland, UK, 1995 年 9 月(http://www.cs.utexas.edu/users/oops/papers.html)（英文）。</p>
<p>Windows NT 的实现（Windows NT 版本 4.0 和更新版本） 使用了 127 个大小从 8 到 1,024 字节的 8 字节对齐块空闲列表和一个&#8220;大块&#8221;列表。&#8220;大块&#8221;列表（空闲列表[0]） 保存大于 1,024 字节的块。空闲列表容纳了用双向链表链接在一起的对象。默认情况下，&#8220;进程堆&#8221;执行收集操作。（收集是将相邻空闲块合并成一个大块的操作。）收集耗费了额外的周期，但减少了堆块的内部碎片。</p>
<p>单一全局锁保护堆，防止多线程式的使用。（请参见&#8220;Server Performance and Scalability Killers&#8221;中的第一个注意事项, George Reilly 所著，在 &#8220;MSDN Online Web Workshop&#8221;上（站点：<a href="http://msdn.microsoft.com/workshop/server/iis/tencom.asp"><u><font color=#0066cc>http://msdn.microsoft.com/workshop/server/iis/tencom.asp（英文）</font></u></a>。）单一全局锁本质上是用来保护堆数据结构，防止跨多线程的随机存取。若堆操作太频繁，单一全局锁会对性能有不利的影响。</p>
<h2><a name=heap3_commonproblems></a>什么是常见的堆性能问题？</h2>
<p>以下是您使用堆时会遇到的最常见问题： </p>
<ul type=disc>
    <li><strong>分配操作造成的速度减慢。</strong>光分配就耗费很长时间。最可能导致运行速度减慢原因是空闲列表没有块，所以运行时分配程序代码会耗费周期寻找较大的空闲块，或从后端分配程序分配新块。<br><br>
    <li><strong>释放操作造成的速度减慢。</strong>释放操作耗费较多周期，主要是启用了收集操作。收集期间，每个释放操作&#8220;查找&#8221;它的相邻块，取出它们并构造成较大块，然后再把此较大块插入空闲列表。在查找期间，内存可能会随机碰到，从而导致高速缓存不能命中，性能降低。<br><br>
    <li><strong>堆竞争造成的速度减慢。</strong>当两个或多个线程同时访问数据，而且一个线程继续进行之前必须等待另一个线程完成时就发生竞争。竞争总是导致麻烦；这也是目前多处理器系统遇到的最大问题。当大量使用内存块的应用程序或 DLL 以多线程方式运行（或运行于多处理器系统上）时将导致速度减慢。单一锁定的使用—常用的解决方案—意味着使用堆的所有操作是序列化的。当等待锁定时序列化会引起线程切换上下文。可以想象交叉路口闪烁的红灯处走走停停导致的速度减慢。
    <p class=tl>竞争通常会导致线程和进程的上下文切换。上下文切换的开销是很大的，但开销更大的是数据从处理器高速缓存中丢失，以及后来线程复活时的数据重建。</p>
    <li><strong>堆破坏造成的速度减慢。</strong>造成堆破坏的原因是应用程序对堆块的不正确使用。通常情形包括释放已释放的堆块或使用已释放的堆块，以及块的越界重写等明显问题。（破坏不在本文讨论范围之内。有关内存重写和泄漏等其他细节，请参见 <a href="http://msdn.microsoft.com/library/devprods/vs6/visualc/vccore/debughm.htm"><font color=#800080><u>Microsoft Visual C++(R) 调试文档</u></font></a> 。）<br><br>
    <li><strong>频繁的分配和重分配造成的速度减慢。</strong>这是使用脚本语言时非常普遍的现象。如字符串被反复分配，随重分配增长和释放。不要这样做，如果可能，尽量分配大字符串和使用缓冲区。另一种方法就是尽量少用连接操作。 </li>
</ul>
<p>竞争是在分配和释放操作中导致速度减慢的问题。理想情况下，希望使用没有竞争和快速分配/释放的堆。可惜，现在还没有这样的通用堆，也许将来会有。</p>
<p>在所有的服务器系统中（如 IIS、MSProxy、DatabaseStacks、网络服务器、 Exchange 和其他）, 堆锁定实在是个大瓶颈。处理器数越多，竞争就越会恶化。</p>
<h2><a name=heap3_protect></a>尽量减少堆的使用</h2>
<p>现在您明白使用堆时存在的问题了，难道您不想拥有能解决这些问题的超级魔棒吗？我可希望有。但没有魔法能使堆运行加快—因此不要期望在产品出货之前的最后一星期能够大为改观。如果提前规划堆策略，情况将会大大好转。调整使用堆的方法，减少对堆的操作是提高性能的良方。</p>
<p>如何减少使用堆操作？通过利用数据结构内的位置可减少堆操作的次数。请考虑下列实例：</p>
<pre><code>struct ObjectA {
&nbsp;&nbsp;&nbsp; // objectA 的数据
}
struct ObjectB {
&nbsp;&nbsp;&nbsp; // objectB 的数据
}
// 同时使用 objectA 和 objectB
//
// 使用指针
//
struct ObjectB {
&nbsp;&nbsp;&nbsp; struct ObjectA * pObjA;
&nbsp;&nbsp;&nbsp; // objectB 的数据
}
//
// 使用嵌入
//
struct ObjectB {
&nbsp;&nbsp;&nbsp; struct ObjectA pObjA;
&nbsp;&nbsp;&nbsp; // objectB 的数据
}
//
// 集合 &#8211; 在另一对象内使用 objectA 和 objectB
//
struct ObjectX {
&nbsp;&nbsp;&nbsp; struct ObjectA&nbsp;&nbsp; objA;
&nbsp;&nbsp;&nbsp; struct ObjectB&nbsp;&nbsp; objB;
}
</code></pre>
<ol>
    <li><strong>避免使用指针关联两个数据结构。</strong>如果使用指针关联两个数据结构，前面实例中的对象 A 和 B 将被分别分配和释放。这会增加额外开销—我们要避免这种做法。<br><br>
    <li><strong>把带指针的子对象嵌入父对象。</strong>当对象中有指针时，则意味着对象中有动态元素（百分之八十）和没有引用的新位置。嵌入增加了位置从而减少了进一步分配/释放的需求。这将提高应用程序的性能。<br><br>
    <li><strong>合并小对象形成大对象（聚合）。</strong>聚合减少分配和释放的块的数量。如果有几个开发者，各自开发设计的不同部分，则最终会有许多小对象需要合并。集成的挑战就是要找到正确的聚合边界。<br><br>
    <li><strong>内联缓冲区能够满足百分之八十的需要（aka 80-20 规则）。</strong>个别情况下，需要内存缓冲区来保存字符串/二进制数据，但事先不知道总字节数。估计并内联一个大小能满足百分之八十需要的缓冲区。对剩余的百分之二十，可以分配一个新的缓冲区和指向这个缓冲区的指针。这样，就减少分配和释放调用并增加数据的位置空间，从根本上提高代码的性能。<br><br>
    <li><strong>在块中分配对象（块化）。</strong>块化是以组的方式一次分配多个对象的方法。如果对列表的项连续跟踪，例如对一个 {名称，值} 对的列表，有两种选择：选择一是为每一个&#8220;名称-值&#8221;对分配一个节点；选择二是分配一个能容纳（如五个）&#8220;名称-值&#8221;对的结构。例如，一般情况下，如果存储四对，就可减少节点的数量，如果需要额外的空间数量，则使用附加的链表指针。
    <p class=tl>块化是友好的处理器高速缓存，特别是对于 L1-高速缓存，因为它提供了增加的位置 —不用说对于块分配，很多数据块会在同一个虚拟页中。</p>
    <li><strong>正确使用 _amblksiz。</strong>C 运行时 (CRT) 有它的自定义前端分配程序，该分配程序从后端（Win32 堆）分配大小为 _amblksiz 的块。将 _amblksiz 设置为较高的值能潜在地减少对后端的调用次数。这只对广泛使用 CRT 的程序适用。 </li>
</ol>
<p>使用上述技术将获得的好处会因对象类型、大小及工作量而有所不同。但总能在性能和可升缩性方面有所收获。另一方面，代码会有点特殊，但如果经过深思熟虑，代码还是很容易管理的。</p>
<h3>其他提高性能的技术</h3>
<p>下面是一些提高速度的技术： </p>
<ol>
    <li><strong>使用 Windows NT5 堆</strong>
    <p class=tl>由于几个同事的努力和辛勤工作，1998 年初 Microsoft Windows(R) 2000 中有了几个重大改进：</p>
    <ul type=disc>
        <li><strong>改进了堆代码内的锁定。</strong>堆代码对每堆一个锁。全局锁保护堆数据结构，防止多线程式的使用。但不幸的是，在高通信量的情况下，堆仍受困于全局锁，导致高竞争和低性能。Windows 2000 中，锁内代码的临界区将竞争的可能性减到最小,从而提高了可伸缩性。<br><br>
        <li><strong>使用 &#8220;Lookaside&#8221;列表。</strong>堆数据结构对块的所有空闲项使用了大小在 8 到 1,024 字节（以 8-字节递增）的快速高速缓存。快速高速缓存最初保护在全局锁内。现在，使用 lookaside 列表来访问这些快速高速缓存空闲列表。这些列表不要求锁定，而是使用 64 位的互锁操作，因此提高了性能。<br><br>
        <li><strong>内部数据结构算法</strong>也得到改进。 </li>
    </ul>
    <p class=tl>这些改进避免了对分配高速缓存的需求，但不排除其他的优化。使用 Windows NT5 堆评估您的代码；它对小于 1,024 字节 (1 KB) 的块（来自前端分配程序的块）是最佳的。<strong>GlobalAlloc()</strong> 和 <strong>LocalAlloc()</strong> 建立在同一堆上，是存取每个进程堆的通用机制。如果希望获得高的局部性能，则使用 Heap(R) API 来存取每个进程堆，或为分配操作创建自己的堆。如果需要对大块操作，也可以直接使用 <strong>VirtualAlloc() </strong>/<strong> VirtualFree()</strong> 操作。</p>
    <p class=tl>上述改进已在 Windows 2000 beta 2 和 Windows NT 4.0 SP4 中使用。改进后，堆锁的竞争率显著降低。这使所有 Win32 堆的直接用户受益。CRT 堆建立于 Win32 堆的顶部，但它使用自己的小块堆，因而不能从 Windows NT 改进中受益。（Visual C++ 版本 6.0 也有改进的堆分配程序。）</p>
    <li><strong>使用分配高速缓存</strong>
    <p class=tl>分配高速缓存允许高速缓存分配的块，以便将来重用。这能够减少对进程堆（或全局堆）的分配/释放调用的次数，也允许最大限度的重用曾经分配的块。另外，分配高速缓存允许收集统计信息,以便较好地理解对象在较高层次上的使用。</p>
    <p class=tl>典型地，自定义堆分配程序在进程堆的顶部实现。自定义堆分配程序与系统堆的行为很相似。主要的差别是它在进程堆的顶部为分配的对象提供高速缓存。高速缓存设计成一套固定大小（如 32 字节、64 字节、128 字节等）。这一个很好的策略，但这种自定义堆分配程序丢失与分配和释放的对象相关的&#8220;语义信息&#8221;。 </p>
    <p class=tl>与自定义堆分配程序相反，&#8220;分配高速缓存&#8221;作为每类分配高速缓存来实现。除能够提供自定义堆分配程序的所有好处之外，它们还能够保留大量语义信息。每个分配高速缓存处理程序与一个目标二进制对象关联。它能够使用一套参数进行初始化，这些参数表示并发级别、对象大小和保持在空闲列表中的元素的数量等。分配高速缓存处理程序对象维持自己的私有空闲实体池（不超过指定的阀值）并使用私有保护锁。合在一起，分配高速缓存和私有锁减少了与主系统堆的通信量，因而提供了增加的并发、最大限度的重用和较高的可伸缩性。</p>
    <p class=tl>需要使用清理程序来定期检查所有分配高速缓存处理程序的活动情况并回收未用的资源。如果发现没有活动，将释放分配对象的池，从而提高性能。</p>
    <p class=tl>可以审核每个分配/释放活动。第一级信息包括对象、分配和释放调用的总数。通过查看它们的统计信息可以得出各个对象之间的语义关系。利用以上介绍的许多技术之一，这种关系可以用来减少内存分配。</p>
    <p class=tl>分配高速缓存也起到了调试助手的作用，帮助您跟踪没有完全清除的对象数量。通过查看动态堆栈返回踪迹和除没有清除的对象之外的签名，甚至能够找到确切的失败的调用者。</p>
    <li><strong>MP 堆</strong>
    <p class=tl>MP 堆是对多处理器友好的分布式分配的程序包，在 Win32 SDK（Windows NT 4.0 和更新版本）中可以得到。最初由 JVert 实现，此处堆抽象建立在 Win32 堆程序包的顶部。MP 堆创建多个 Win32 堆，并试图将分配调用分布到不同堆，以减少在所有单一锁上的竞争。</p>
    <p class=tl>本程序包是好的步骤 —一种改进的 MP-友好的自定义堆分配程序。但是，它不提供语义信息和缺乏统计功能。通常将 MP 堆作为 SDK 库来使用。如果使用这个 SDK 创建可重用组件，您将大大受益。但是，如果在每个 DLL 中建立这个 SDK 库，将增加工作设置。</p>
    <li><strong>重新思考算法和数据结构</strong>
    <p class=tl>要在多处理器机器上伸缩，则算法、实现、数据结构和硬件必须动态伸缩。请看最经常分配和释放的数据结构。试问，&#8220;我能用不同的数据结构完成此工作吗？&#8221;例如，如果在应用程序初始化时加载了只读项的列表，这个列表不必是线性链接的列表。如果是动态分配的数组就非常好。动态分配的数组将减少内存中的堆块和碎片，从而增强性能。</p>
    <p class=tl>减少需要的小对象的数量减少堆分配程序的负载。例如，我们在服务器的关键处理路径上使用五个不同的对象，每个对象单独分配和释放。一起高速缓存这些对象，把堆调用从五个减少到一个，显著减少了堆的负载，特别当每秒钟处理 1,000 个以上的请求时。</p>
    <p class=tl>如果大量使用&#8220;Automation&#8221;结构，请考虑从主线代码中删除&#8220;Automation BSTR&#8221;，或至少避免重复的 BSTR 操作。（BSTR 连接导致过多的重分配和分配/释放操作。）</p>
    </li>
</ol>
<h2>摘要</h2>
<p>对所有平台往往都存在堆实现，因此有巨大的开销。每个单独代码都有特定的要求，但设计能采用本文讨论的基本理论来减少堆之间的相互作用。 </p>
<ol>
    <li>评价您的代码中堆的使用。<br><br>
    <li>改进您的代码，以使用较少的堆调用：分析关键路径和固定数据结构。<br><br>
    <li>在实现自定义的包装程序之前使用量化堆调用成本的方法。<br><br>
    <li>如果对性能不满意，请要求 OS 组改进堆。更多这类请求意味着对改进堆的更多关注。<br><br>
    <li>要求 C 运行时组针对 OS 所提供的堆制作小巧的分配包装程序。随着 OS 堆的改进，C 运行时堆调用的成本将减小。<br><br>
    <li>操作系统（Windows NT 家族）正在不断改进堆。请随时关注和利用这些改进。 </li>
</ol>
<p>Murali Krishnan 是 Internet Information Server (IIS) 组的首席软件设计工程师。从 1.0 版本开始他就设计 IIS，并成功发行了 1.0 版本到 4.0 版本。Murali 组织并领导 IIS 性能组三年 (1995-1998), 从一开始就影响 IIS 性能。他拥有威斯康星州 Madison 大学的 M.S.和印度 Anna 大学的 B.S.。工作之外，他喜欢阅读、打排球和家庭烹饪。</p>
<br>
<img src ="http://www.cppblog.com/justin-shi/aggbug/72832.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2009-02-03 08:49 <a href="http://www.cppblog.com/justin-shi/archive/2009/02/03/72832.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>WindowFeatures（WIN32开发必看）</title><link>http://www.cppblog.com/justin-shi/archive/2008/08/16/59052.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Sat, 16 Aug 2008 05:38:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/08/16/59052.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/59052.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/08/16/59052.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/59052.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/59052.html</trackback:ping><description><![CDATA[<p>这篇文章翻译至MSDN2005，给自己学习，也给所有觉得它有用的人，文中难免有翻译不到位或者错误的地方，望高手指正。译者：欧昊川（转载麻烦注明出处及译者）<wbr></wbr><br><br>2008年5月4日<wbr></wbr><br><br>这个概述讨论了窗口的一些特性，如窗口类型、状态、大小及位置。<wbr></wbr><br><br><strong><wbr></wbr>1、窗口类型（WindowStyles）</strong><wbr></wbr><wbr></wbr><br><strong><wbr></wbr>这一节描述层叠窗口、弹出窗口、子窗口、分层窗口、仅处理消息的窗口这五种类型。</strong><wbr></wbr><br><br>1.1层叠窗口（OverlappedWindows）<wbr></wbr><br>层叠窗口是一个具有标题栏、边框和客户区的顶层窗口；也就是说它适合做为应用程序主窗口。它也可以具有一个系统菜单，最小和最大化按钮，以及滚动条。一个层叠窗口被典型地用于包含所有上述组件的应用程序主窗口。<wbr></wbr><br><br>通过在CreateWindowEx中指定WS_OVERLAPPED或者WS_OVERLAPPEDWINDOW样式，一个应用程序就能创建一个层叠窗口。假如你使用第一个样式，那么创建的窗口就具有一个标题栏和边框；假如你使用第二个，那么窗口就具有一个标题栏，可以调整大小的边框，系统菜单，以及最大最小化按钮。<wbr></wbr><br><br>1.2弹出窗口（Pop-upWindows）<wbr></wbr><br>弹出窗口是一个非凡的层叠窗口，它被用于显示在应用程序主窗口之外的对话框，消息框以及其他临时窗口。标题栏对弹出窗口来说是可选的；除此之外，弹出窗口跟具有WS_OVERLAPPED样式的层叠窗口一样。<wbr></wbr><br><br>你可以通过在CreateWindowEx中指定WS_POPUP样式来创建一个弹出窗口。假如要使用标题栏，就加入WS_CAPTION样式。使用WS_POPUPWINDOW样式来创建一个含有边框和系统菜单的弹出窗口。WS_CAPTION样式必须与WS_POPUPWINDOW样式一起使用才能使系统菜单可见。<wbr></wbr><br><br>1.3子窗口（ChildWindows）<wbr></wbr><br>子窗口具有WS_CHILD样式并且它被限制在其父窗口的客户区中。应用程序典型地使用子窗口来把其父窗口的客户区划分成几个功能区域。你可以通过在CreateWindowEx中指定WS_CHILD样式来创建子窗口。<wbr></wbr><br><br>子窗口必须具有一个父窗口。父窗口可以是一个层叠窗口，弹出窗口，或者另外一个子窗口。你可以在CreateWindowEx中指定父窗口。假如你在CreateWindowEx中指定了WS_CHILD样式但是没有指定父窗口，那么系统不会创建这个子窗口。<wbr></wbr><br><br>子窗口只具有一个客户区而没有其他特性，除非这些特性被明确的请求。应用程序可以为子窗口添加标题栏，系统菜单，最小化最大化按钮，边框，以及滚动条。但是子窗口不能具有自定义菜单。假如应用程序指定了一个自定义菜单句柄，那么无论是在它注册这个子窗口类还是创建这个子窗口时，这个菜单句柄都被忽略。假如没有指定边框样式，系统将创建一个无边框窗口。应用程序可以使用无边框的子窗口来划分父窗口的客户区假如想保持这种划分对用户是不可见的话。<wbr></wbr><br><br><strong><wbr></wbr>下面一节讨论窗口的布置、裁剪、与父窗口的关系、消息四个主题。</strong><wbr></wbr><wbr></wbr><br><br>1.4窗口布置（Positioning）<wbr></wbr><br>系统总是相对于父窗口客户区的左上角来放置子窗口。子窗口的任何部分都不会出现在其父窗口的边框之外。假如应用程序创建一个比父窗口大的子窗口，或者移动子窗口使得一个或者所有子窗口超出了父窗口的边框，那么系统会裁剪子窗口，即在父窗口边框之外的部分不被显示。对父窗口产生影响的行为同样会影响子窗口，这些行为如下：</p>
<p>&#160;</p>
<img src ="http://www.cppblog.com/justin-shi/aggbug/59052.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-08-16 13:38 <a href="http://www.cppblog.com/justin-shi/archive/2008/08/16/59052.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Window下拖放操作Drag &amp; Drop 全解析</title><link>http://www.cppblog.com/justin-shi/archive/2008/08/14/58866.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Thu, 14 Aug 2008 11:19:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/08/14/58866.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/58866.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/08/14/58866.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/58866.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/58866.html</trackback:ping><description><![CDATA[<p><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><strong>OLE</strong><strong>拖放实现</strong></font></span></p>
<p><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>MFC</font>本身的<font face=Tahoma>CView</font>类是支持拖放操作的，通过研究<font face=Tahoma>CView</font>类的源码，大体知道它的实现原理是这样的：<font face=Tahoma>CView</font>类中有一个<font face=Tahoma>COleDropTarget</font>类的对象，在视图窗口初始化时，调用<font face=Tahoma>COleDropTarget</font>类成员函数<font face=Tahoma>Register()</font>，以此在系统中注册该视图窗口为拖放接收窗口。当进行拖放操作的鼠标指针处于视图窗口范围内时，<font face=Tahoma>COleDropTarge</font>类会做出反应，它的<font face=Tahoma>OnDragEnter</font>、<font face=Tahoma>OnDragOver</font>、<font face=Tahoma>OnDropEx</font>、<font face=Tahoma>OnDrop</font>等成员函数被依次调用，这些函数默认均是调用与其相对应的<font face=Tahoma>CView</font>类成员函数<font face=Tahoma>OnDragEnter</font>、<font face=Tahoma>OnDragOver</font>、<font face=Tahoma>OnDropEx</font>、<font face=Tahoma>OnDrop</font>等，程序员只需重载这些<font face=Tahoma>CView</font>类成员函数，即可对拖动的过程及结果进行控制。</font></span></p>
<p><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>因为<font face=Tahoma>COleDropTarget</font>默认只对<font face=Tahoma>CView</font>提供支持，所以如果要让其他的窗口支持拖放，我们必须同时对要支持拖放的窗口类和<font face=Tahoma>COleDropTarget</font>类进行派生。把对拖放操作具体进行处理的代码封装成派生窗口类的成员函数，然后重载<font face=Tahoma>COleDropTarget</font>中对应的五个虚函数，当它接收到拖放动作时，调用窗口派生类的处理函数即可。但这里有一个问题，就是我们怎么知道何时调用派生类的处理函数呢？答案是运用<font face=Tahoma>RTTI</font>技术。如果<font face=Tahoma>COleDropTarget</font>派生类收到的窗口指针类型，就是我们派生的窗口类，那么就调用它的处理函数，否则调用基类进行处理。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>首先生成一个对话框工程，添加二个新类。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>第一个类名为<font face=Tahoma>CListCtrlEx</font>，父类为<font face=Tahoma>CListCtrl</font>。添加完毕后，在<font face=Tahoma>CListCtrlEx</font>的定义头文件中加入<font face=Tahoma>DECLARE_DYNAMIC(CListCtrlEx)</font>，在其实现文件中加入<font face=Tahoma>IMPLEMENT_DYNAMIC(CListCtrlEx,CListCtrl)</font>，这样就对<font face=Tahoma>CListCtrlEx</font>类添加了<font face=Tahoma>RTTI</font>运行期类型识别（<font face=Tahoma>Run Time Type Information</font>）支持。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>第二个类名为<font face=Tahoma>COleDropTargetEx</font>，父类为<font face=Tahoma>COleDataTarget</font>。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>在<font face=Tahoma>CListCtrlEx</font>中添加<font face=Tahoma>COleDropTargetEx</font>类的对象，并添加下列公有虚函数的声明：</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual BOOL Initialize();</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, DROPEFFECT dropList, CPoint point);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual void OnDragLeave(CWnd* pWnd);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>Initialize</font>函数用于注册<font face=Tahoma>CListCtrlEx</font>成为拖放接收窗口；</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>OnDragOver</font>在拖放鼠标进入窗口时被调用。此函数的返回值决定了后续的动作的类型：如果返回<font face=Tahoma>DROPEFFECT_MOVE</font>，则产生一个剪切动作；如果返回<font face=Tahoma>DROPEFFECT_COPY</font>，则产生一个复制动作，如果返回<font face=Tahoma>DROPEFFECT_NONE</font>，则不会产生拖放动作，因为<font face=Tahoma>OnDropEx</font>、<font face=Tahoma>OnDrop</font>函数将不会被调用（<font face=Tahoma>OnDragLeave</font>函数仍会被调用）。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>OnDropEx</font>函数会在<font face=Tahoma>OnDrop</font>函数之前调用，如果<font face=Tahoma>OnDropEx</font>函数没有对拖放动作进行处理，则应用程序框架会接着调用<font face=Tahoma>OnDrop</font>函数进行处理。所以必须要在派生类中重载<font face=Tahoma>OnDropEx</font>函数——即使什么动作都都没有做——否则我们的<font face=Tahoma>OnDrop</font>函数将不会被执行到，因为没有重载的话，将会调用基类的<font face=Tahoma>OnDropEx</font>函数，而基类的<font face=Tahoma>OnDropEx</font>函数对拖放是进行了处理的——尽管不是我们所想要的动作。当然你也可以把对拖放进行处理的动作放在<font face=Tahoma>OnDropEx</font>中——那样就不需要重载<font face=Tahoma>OnDrop</font>了。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>OnDragLeave</font>函数会在鼠标离开窗口时被调用，在此可以进行一些简单的清理工作。譬如在<font face=Tahoma>OnDragEnter</font>或者<font face=Tahoma>OnDragOver</font>函数中，我们改变了光标的形态，那么此时我们就应该把光标恢复过来。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>这些函数中最重要的是<font face=Tahoma>OnDrop</font>函数，拖放动作将在此进行处理，它的全部源码如下：</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>BOOL CListCtrlEx::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>{</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UINT&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nFileCount = 0;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HDROP&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hDropFiles = NULL;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HGLOBAL &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hMemData = NULL;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma size=2>&nbsp;</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AfxMessageBox("OnDrop");</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pDataObject-&gt;IsDataAvailable(CF_HDROP)) </font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hMemData = pDataObject-&gt;GetGlobalData(CF_HDROP);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hDropFiles = (HDROP)GlobalLock((HGLOBAL)hMemData); //</font>锁定内存块</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(hDropFiles != NULL)</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; char chTemp[_MAX_PATH+1] = {0};</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; nFileCount = DragQueryFile(hDropFiles, 0xFFFFFFFF, NULL, 0);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(UINT nCur=0; nCur&lt;nFileCount; ++nCur) //</font>遍历取得每个文件名</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&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; ZeroMemory(chTemp, _MAX_PATH+1);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DragQueryFile(hDropFiles, nCur, (LPTSTR)chTemp, _MAX_PATH+1);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&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; AddAllFiles(chTemp);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GlobalUnlock(hMemData);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return TRUE;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return FALSE;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>}</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>在第二个类<font face=Tahoma>COleDropTarget</font>中添加如下对应的函数：</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp; virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp; virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, DROPEFFECT dropList, CPoint point);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; virtual void OnDragLeave(CWnd* pWnd);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>它们的动作都差不多：先用<font face=Tahoma>RTTI</font>判断窗口指针<font face=Tahoma>pWnd</font>的类型，如果是<font face=Tahoma>CListCtrlEx</font>，则调用<font face=Tahoma>CListCtrlEx</font>中对应的处理函数，否则调用基类的处理函数。以<font face=Tahoma>OnDrop</font>为例：</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>BOOL COleDropTargetEx::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>{</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CListCtrlEx*&nbsp;&nbsp;&nbsp;&nbsp; pListCtrlEx = NULL;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT_VALID(this);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(IsWindow(pWnd-&gt;m_hWnd));</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(pWnd-&gt;IsKindOf(RUNTIME_CLASS(CListCtrlEx)))</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pListCtrlEx = (CListCtrlEx*)pWnd;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return pListCtrlEx-&gt;OnDrop(pWnd, pDataObject, dropEffect, point);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return COleDropTarget::OnDrop(pWnd, pDataObject, dropEffect, point);&nbsp;&nbsp;&nbsp;&nbsp; </font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>}</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2></font></font></span>&nbsp;</p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>//倒霉的64K限制，只能再截断了：（<br><br></font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>至此，我们成功地为<font face=Tahoma>CListCtrlEx</font>添加了文件拖入操作的支持。一个完整的拖放操作，还包括拖出动作，所以必须要为该类再添加拖出操作，即，将列表中的某一项或者多项拖出成为一个文件。这就需要用到另一个类：<font face=Tahoma>COleDataSource</font>。具体步骤如下：<br></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>在<font face=Tahoma>CListCtrlEx</font>中加入一个<font face=Tahoma>COleDataSource</font>的实例，并映射列表框的<font face=Tahoma>LVN_BEGINDRAG</font>消息处理函数，在此我们添加拖出操作的代码。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>实现拖出非常简单，只需要依次调用<font face=Tahoma>COleDataSource</font>的三个函数即可：<font face=Tahoma>Empty</font>用于清空原先对象中缓存的数据，<font face=Tahoma>CacheGlobalData</font>用来缓存数据以进行拖放操作，最后调用<font face=Tahoma>DoDragDrop</font>启动本次拖放操作。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>但在调用之前，必须要做一些准备工作。主要的任务就是创建一个<font face=Tahoma>DROPFILES</font>结构体，并拷贝要拖放的文件名到结构体后的内存中。<font face=Tahoma>DROPFILES</font>结构体定义了<font face=Tahoma>CF_HDROP</font>剪贴板格式，紧跟它后面的是一系列被拖放文件的路径名。它的定义如下：</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>typedef struct _DROPFILES</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>{</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp; DWORD &nbsp;&nbsp;&nbsp; pFiles;&nbsp; //</font>文件名起始地址</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp; POINT &nbsp;&nbsp;&nbsp;&nbsp; pt;&nbsp;&nbsp;&nbsp;&nbsp; //</font>鼠标放下的位置，坐标由<font face=Tahoma>fNC</font>成员指定</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp; BOOL&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fNC;&nbsp;&nbsp;&nbsp; //</font>为<font face=Tahoma>TRUE</font>表示适用屏幕坐标系，否则使用客户坐标系</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp; BOOL &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fWide;&nbsp; //</font>文件名字符串是否使用宽字符</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>} DROPFILES, FAR* LPDROPFILES; </font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>拖放之前的准备动作的代码如下：</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>uBufferSize = sizeof(DROPFILES) + uBufferSize + 1;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp; hMemData = GlobalAlloc(GPTR,uBufferSize);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp; ASSERT(hMemData != NULL);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDropFiles = (LPDROPFILES)GlobalLock(hMemData); //</font>锁定之<font face=Tahoma>,</font>并设置相关成员</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ASSERT(lpDropFiles != NULL);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDropFiles-&gt;pFiles = sizeof(DROPFILES);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>#ifdef _UNICODE</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDropFiles-&gt;fWide = TRUE;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>#else</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lpDropFiles-&gt;fWide = FALSE;</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>#endif</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma size=2>&nbsp;</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //</font>把选中的所有文件名依次复制到<font face=Tahoma>DROPFILES</font>结构体后面<font face=Tahoma>(</font>全局内存中<font face=Tahoma>)</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pItemPos = strSelectedList.GetHeadPosition();</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pszStart = (char*)((LPBYTE)lpDropFiles + sizeof(DROPFILES));</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while(pItemPos != NULL)</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lstrcpy(pszStart, (LPCTSTR)strSelectedList.GetNext(pItemPos));</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; pszStart = strchr(pszStart,'\0') + 1; //</font>下次的起始位置是上一次结尾<font face=Tahoma>+1</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>准备完毕之后就可以进行拖放了，拖放动作有<font face=Tahoma>DoDragDrop</font>函数触发，其原型如下：</font></span></p>
<p><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>DROPEFFECT DoDragDrop( </font></font></span></p>
<p><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>DWORD dwEffects = DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK, LPCRECT lpRectStartDrag = NULL, </font></font></span></p>
<p><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>COleDropSource* pDropSource = NULL </font></font></span></p>
<p><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>这里，<font face=Tahoma>dwEffects</font>指定了允许施加于本<font face=Tahoma>COleDataSource</font>实例之上的动作集：剪切、复制或无动作。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp; lpRectStartDrag</font>指示拖放操作真正开始的矩形，如果鼠标没有移出该矩形，则拖放操作视作放弃处理。如果本成员设为<font face=Tahoma>NULL</font>，则该起始矩形将为一个像素大小。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>&nbsp;&nbsp;&nbsp; pDropSource</font>表明拖放所使用的<font face=Tahoma>COleDataSource</font>对象。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>而该函数的返回值，则表明本次拖放操作所实际产生的效果，至于具体产生何种效果，则由系统决定。譬如在拖放时按住<font face=Tahoma>Shift</font>键，将产生剪切效果；按住<font face=Tahoma>Ctrl</font>键，将产生复制效果，等等。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>拖放的代码如下：</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_oleDataSource.Empty();</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_oleDataSource.CacheGlobalData(CF_HDROP, hMemData);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font face=Tahoma><font size=2>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DropResult = m_oleDataSource.DoDragDrop(DROPEFFECT_MOVE|DROPEFFECT_COPY);</font></font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2>最后一点要注意的是，在<font face=Tahoma>Windows NT 4.0</font>以上的系统中，即使实际产生的是<font face=Tahoma>DROPEFFECT_MOVE</font>动作，<font face=Tahoma>DoDragDrop</font>函数也只返回<font face=Tahoma>DROPEFFECT_NONE</font>。产生这个问题的原因在于，<font face=Tahoma>Windows NT 4.0</font>的<font face=Tahoma>Shell</font>会直接移动文件本身来对移动操作进行优化。返回值<font face=Tahoma>DROPEFFECT_MOVE</font>最初的含义，就是通知执行拖放操作的应用程序去删除原位置上的文件。但是因为<font face=Tahoma>Shell</font>已经替应用程序完成了这个（删除）动作，所以，函数返回<font face=Tahoma>DROPEFFECT_NONE</font>。要想知道文件是否真的被移动了也很简单，只要在函数返回之后检查一下原位置上的文件是否存在就可以了。</font></span></p>
<p align=left><span id=ArticleContent1_ArticleContent1_lblContent><font size=2><font face=Tahoma>Windows 9x</font>系列的操作系统也会对移动进行同样的优化动作，但是它不会返回<font face=Tahoma>DROPEFFECT_NONE</font>来代替<font face=Tahoma>DROPEFFECT_MOVE</font>。详细的解释参见<font face=Tahoma>MS</font>知识库<font face=Tahoma>Q182219</font>。</font></span></p>
<img src ="http://www.cppblog.com/justin-shi/aggbug/58866.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-08-14 19:19 <a href="http://www.cppblog.com/justin-shi/archive/2008/08/14/58866.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SHGetFileInfo </title><link>http://www.cppblog.com/justin-shi/archive/2008/08/13/58782.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Wed, 13 Aug 2008 15:11:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/08/13/58782.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/58782.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/08/13/58782.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/58782.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/58782.html</trackback:ping><description><![CDATA[SHGetFileInfo函数<br>function SHGetFileInfo(pszPath: PAnsiChar; dwFileAttributes: DWORD;<br>&nbsp;&nbsp;var psfi: TSHFileInfo; cbFileInfo, uFlags: UINT): DWORD; stdcall;<br>pszPath 参数:指定的文件名。<br>当uFlags的取值中不包含 SHGFI_PIDL时,可直接指定;<br>当uFlags的取值中包含 SHGFI_PIDL时pszPath要通过计算获得,不能直接指定;<br>dwFileAttributes参数:文件属性。<br>仅当uFlags的取值中包含SHGFI_USEFILEATTRIBUTES时有效,一般不用此参数;<br>psfi 参数:返回获得的文件信息,是一个记录类型,有以下字段:<br>&nbsp;&nbsp;_SHFILEINFOA = record<br>&nbsp; &nbsp; hIcon: HICON;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; { out: icon }&nbsp;&nbsp;//文件的图标句柄<br>&nbsp; &nbsp; iIcon: Integer;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;{ out: icon index }&nbsp; &nbsp;&nbsp;&nbsp;//图标的系统索引号<br>&nbsp; &nbsp; dwAttributes: DWORD;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;{ out: SFGAO_ flags }&nbsp; &nbsp; //文件的属性值<br>&nbsp; &nbsp; szDisplayName: array [0..MAX_PATH-1] of&nbsp;&nbsp;AnsiChar; { out: display name (or path) }&nbsp;&nbsp;//文件的显示名<br>&nbsp; &nbsp; szTypeName: array [0..79] of AnsiChar;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; { out: type name }&nbsp; &nbsp;&nbsp; &nbsp;//文件的类型名<br>&nbsp;&nbsp;end;<br>cbFileInfo 参数:psfi的比特值;<br>uFlags 参数:指明需要返回的文件信息标识符,常用的有以下常数:<br>&nbsp; &nbsp; SHGFI_ICON;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;//获得图标<br>&nbsp; &nbsp; SHGFI_DISPLAYNAME;&nbsp; &nbsp; //获得显示名<br>&nbsp; &nbsp; SHGFI_TYPENAME;&nbsp; &nbsp;&nbsp; &nbsp; //获得类型名<br>&nbsp; &nbsp; SHGFI_ATTRIBUTES;&nbsp; &nbsp;&nbsp;&nbsp;//获得属性<br>&nbsp; &nbsp; SHGFI_LARGEICON;&nbsp; &nbsp;&nbsp; &nbsp;//获得大图标<br>&nbsp; &nbsp; SHGFI_SMALLICON;&nbsp; &nbsp;&nbsp; &nbsp;//获得小图标<br>&nbsp; &nbsp; SHGFI_PIDL;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;// pszPath是一个标识符<br>函数SHGetFileInfo()的返回值也随uFlags的取值变化而有所不同。<br>可见通过调用SHGetFileInfo()可以由psfi参数得到文件的图标句柄。但要注意在uFlags参数中不使用SHGFI_PIDL时,SHGetFileInfo()不能获得&#8220;我的电脑&#8221;等虚似文件夹的信息。<br>应该注意的是，在调用SHGetFileInfo()之前，必须使用 CoInitialize 或者OleInitialize 初始化COM,否则表面上能够使用，但是会造成不安全或者丧失部分功能。例如，一个常见的例子：如果不初始化COM,那么调用该函数就无法得到.htm/.mht/.xml文件的图标。<br>以下是两个例子：<br>1.获得系统图标列表：<br>//取得系统图标列表<br>uses<br>ShellAPI<br>var<br>&nbsp;&nbsp;ImageListHandle : THandle;<br>&nbsp;&nbsp;FileInfo: TSHFileInfo;<br>//小图标 <br>ImageListHandle := SHGetFileInfo('C:\',<br>&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;FileInfo,<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;SizeOf(FileInfo),<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;SHGFI_SYSICONINDEX or SHGFI_SMALLICON);<br>//把图标列表同一个名叫ListView1的ListView控件的小图标关联。&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>SendMessage(ListView1.Handle, LVM_SETIMAGELIST, LVSIL_SMALL, ImageListHandle);&nbsp;&nbsp;<br>//大图标&nbsp; &nbsp; <br>ImageListHandle := SHGetFileInfo('C:\',<br>&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;FileInfo,<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;SizeOf(FileInfo),<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;SHGFI_SYSICONINDEX or SHGFI_LARGEICON);<br>//把图标列表同一个名叫ListView1的ListView控件的大图标关联。&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br>SendMessage(ListView1.Handle, LVM_SETIMAGELIST, LVSIL_NORMAL, ImageListHandle); <br>2.获得一个文件的显示名和图标<br>var<br>&nbsp;&nbsp;sfi: TSHFileInfo;<br>IconIndex : Integer;<br>//取图标的索引号等信息<br>SHGetFileInfo(PAnsiChar(FileName),<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; 0,<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; sfi,<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; sizeof(TSHFileInfo),<br>&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; ShellAPI.SHGFI_DISPLAYNAME or ShellAPI.SHGFI_TYPENAME or ShellAPI.SHGFI_LARGEICON or ShellAPI.SHGFI_ICON);<br>//显示名和图标在系统图标列表中的编号就分别在sfi.szDisplayName和sfi.iIcon中 <br><br>
<img src ="http://www.cppblog.com/justin-shi/aggbug/58782.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-08-13 23:11 <a href="http://www.cppblog.com/justin-shi/archive/2008/08/13/58782.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GetSystemMetrics()用法</title><link>http://www.cppblog.com/justin-shi/archive/2008/08/10/58469.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Sun, 10 Aug 2008 13:42:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/08/10/58469.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/58469.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/08/10/58469.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/58469.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/58469.html</trackback:ping><description><![CDATA[<span class=Tit><font size=2>下面是GetSystemMetrics函数参数nIndex的定义：<br><br>　　SM_ARRANGE Flags specifying how the system arranged minimized windows. For more information about minimized windows, see the following Remarks section.<br><br>　　SM_CLEANBOOT 返回系统启动方式:<br><br>　　0 正常启动<br><br>　　1 安全模式启动<br><br>　　2 网络安全模式启动<br><br>　　SM_CMOUSEBUTTONS 返回值为系统支持的鼠标键数，返回0，则系统中没有安装鼠标。<br><br>　　SM_CXBORDER,<br><br>　　SM_CYBORDER 返回以像素值为单位的Windows窗口边框的宽度和高度，如果Windows的为3D形态，则<br><br>　　等同于SM_CXEDGE参数<br><br>　　SM_CXCURSOR,<br><br>　　SM_CYCURSOR 返回以像素值为单位的标准光标的宽度和高度<br><br>　　SM_CXDLGFRAME,<br><br>　　SM_CYDLGFRAME 等同与SM_CXFIXEDFRAME and SM_CYFIXEDFRAME<br><br>　　SM_CXDOUBLECLK,<br><br>　　SM_CYDOUBLECLK 以像素值为单位的双击有效的矩形区域<br><br>　　SM_CXEDGE,SM_CYEDGE 以像素值为单位的3D边框的宽度和高度<br><br>　　SM_CXFIXEDFRAME,<br><br>　　SM_CYFIXEDFRAME 围绕具有标题但无法改变尺寸的窗口（通常是一些对话框）的边框的厚度<br><br>　　SM_CXFRAME,SM_CYFRAME 等同于SM_CXSIZEFRAME and SM_CYSIZEFRAME<br><br>　　SM_CXFULLSCREEN,<br><br>　　SM_CYFULLSCREEN 全屏幕窗口的窗口区域的宽度和高度<br><br>　　SM_CXHSCROLL,<br><br>　　SM_CYHSCROLL 水平滚动条的高度和水平滚动条上箭头的宽度<br><br>　　SM_CXHTHUMB 以像素为单位的水平滚动条上的滑动块宽度<br><br>　　SM_CXICON,SM_CYICON 系统缺省的图标的高度和宽度（一般为32*32）<br><br>　　SM_CXICONSPACING,<br><br>　　SM_CYICONSPACING 以大图标方式查看Item时图标之间的间距，这个距离总是大于等于<br><br>　　SM_CXICON and SM_CYICON.<br><br>　　SM_CXMAXIMIZED,<br><br>　　SM_CYMAXIMIZED 处于顶层的最大化窗口的缺省尺寸<br><br>　　SM_CXMAXTRACK,<br><br>　　SM_CYMAXTRACK 具有可改变尺寸边框和标题栏的窗口的缺省最大尺寸，如果窗口大于这个<br><br>　　尺寸，窗口是不可移动的。<br><br>　　SM_CXMENUCHECK,<br><br>　　SM_CYMENUCHECK 以像素为单位计算的菜单选中标记位图的尺寸<br><br>　　SM_CXMENUSIZE,<br><br>　　SM_CYMENUSIZE 以像素计算的菜单栏按钮的尺寸<br><br>　　SM_CXMIN,SM_CYMIN 窗口所能达到的最小尺寸<br><br>　　SM_CXMINIMIZED,<br><br>　　SM_CYMINIMIZED 正常的最小化窗口的尺寸<br><br>　　SM_CXMINTRACK,<br><br>　　SM_CYMINTRACK 最小跟踪距离，当使用者拖动窗口移动距离小于这个值，窗口不会移动。<br><br>SM_CXSCREEN,<br><br>　　SM_CYSCREEN 以像素为单位计算的屏幕尺寸。<br><br>　　SM_CXSIZE,SM_CYSIZE 以像素计算的标题栏按钮的尺寸<br><br>　　SM_CXSIZEFRAME,<br><br>　　SM_CYSIZEFRAME 围绕可改变大小的窗口的边框的厚度<br><br>　　SM_CXSMICON,<br><br>　　SM_CYSMICON 以像素计算的小图标的尺寸，小图标一般出现在窗口标题栏上。<br><br>　　SM_CXVSCROLL,<br><br>　　SM_CYVSCROLL 以像素计算的垂直滚动条的宽度和垂直滚动条上箭头的高度<br><br>　　SM_CYCAPTION 以像素计算的普通窗口标题的高度<br><br>　　SM_CYMENU 以像素计算的单个菜单条的高度<br><br>　　SM_CYSMCAPTION 以像素计算的窗口小标题栏的高度<br><br>　　SM_CYVTHUMB 以像素计算的垂直滚动条中滚动块的高度<br><br>　　SM_DBCSENABLED 如果为TRUE或不为0的值表明系统安装了双字节版本的USER.EXE,为FALSE或0则不是。<br><br>　　SM_DEBUG 如果为TRUE或不为0的值表明系统安装了debug版本的USER.EXE,为FALSE或0则不是。<br><br>　　SM_MENUDROPALIGNMENT 如果为TRUE或不为0的值下拉菜单是右对齐的否则是左对齐的。<br><br>　　SM_MOUSEPRESENT 如果为TRUE或不为0的值则安装了鼠标，否则没有安装。<br><br>　　SM_MOUSEWHEELPRESENT 如果为TRUE或不为0的值则安装了滚轮鼠标，否则没有安装。(Windows NT only)<br><br>　　SM_SWAPBUTTON 如果为TRUE或不为0的值则鼠标左右键交换，否则没有。 <br></font></span>
<img src ="http://www.cppblog.com/justin-shi/aggbug/58469.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-08-10 21:42 <a href="http://www.cppblog.com/justin-shi/archive/2008/08/10/58469.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>把C++类成员方法直接作为线程回调函数</title><link>http://www.cppblog.com/justin-shi/archive/2008/07/22/56813.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Mon, 21 Jul 2008 19:13:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/07/22/56813.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/56813.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/07/22/56813.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/56813.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/56813.html</trackback:ping><description><![CDATA[<p>我以前写线程时要么老老实实照着声明写,要么使用C++类的静态成员函数来作为回调函数,经常会因为线程代码而破坏封装.之前虽然知道类成员函数的展开形式，但从没想过利用过它，昨天看深入ATL时无意中学会了这一招:)&nbsp;</p>
<p>类成员方法是一个比较特殊的函数，它在编译时会被转化成普通函数，比如有TMyClass类:<br>class TMyClass{<br>&nbsp;&nbsp;&nbsp; void Func();<br>};</p>
<p>这个TMyClass::Func最终会转化成 void Func(TMyClass *this); 也就是说在原第一个参数前插入指向对象本身的this指针。</p>
<p>我们可以利用这个特性写一个非静态类成员方法来直接作为线程回调函数，先看_beginthread函数的定义:<br>unsigned long _RTLENTRY _EXPFUNC _beginthread (void (_USERENTRY *__start)(void *),unsigned __stksize, void *__arg);<br>其中的第一个参数就是作为线程执行主体的回调函数。它的原型是:void Func(void *)，这个void*参数是作为自定义数据传入的。对比一下上面所说的TMyClass::Func的最终形式，它正好可以符合这里的要求。</p>
<p>现在做个实验:<br>#include &lt;stdio.h&gt;<br>#include &lt;process.h&gt;</p>
<p>class TMyClass{<br>&nbsp;&nbsp;&nbsp; int m_nCount;<br>&nbsp;&nbsp;&nbsp; int m_nId;<br>public:<br>&nbsp;&nbsp;&nbsp; TMyClass(int nId,int nCount)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :m_nId(nId),m_nCount(nCount)<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; void _USERENTRY ThreadProc()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 类成员方法<br>&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0; i&lt;m_nCount; i++)&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 根据m_nCount成员打印一排数字<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printf("Class%d : %d\n",m_nId,i);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>};</p>
<p>int main(int argc, char* argv[])<br>{<br>&nbsp;&nbsp;&nbsp; union {&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>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (_USERENTRY *ThreadProc)(void *);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void (_USERENTRY TMyClass::*MemberProc)();<br>&nbsp;&nbsp;&nbsp; } Proc;&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; // 尽管联合里的两种函数类型现在看起来有很大不同，但它们的最终形式是相同的。</p>
<p>&nbsp;&nbsp;&nbsp; TMyClass MyClass1(1,10),MyClass2(2,5); // 产生两个TMyClass对象</p>
<p>&nbsp;&nbsp;&nbsp; Proc.MemberProc = &amp;TMyClass::ThreadProc;&nbsp;&nbsp; // 转换，Proc.ThreadProc就是对应的普通函数指针了</p>
<p>&nbsp;&nbsp;&nbsp; _beginthread(Proc.ThreadProc,4096,&amp;MyClass1);&nbsp;&nbsp; // 开始线程，这里的Proc.ThreadProc实际上是TMyClass::ThreadProc, 它要的this指针是我们给的&amp;MyClass1。<br>&nbsp;&nbsp;&nbsp; _beginthread(Proc.ThreadProc,4096,&amp;MyClass2);<br>&nbsp;&nbsp;&nbsp; system("pause");<br>&nbsp;&nbsp;&nbsp; return 0;<br>}</p>
<p>运行！神奇吧？:-)</p>
<p>其实不止线程回调函数，其实只要是形如Func(void*,...)的回调函数都可以用这种方法直接使用类成员方法。(前提是第一个void*是自定义数据，也就是说它不能有其它功能)。<br><br>转自:http://blog.csdn.net/waiting4you/archive/2007/12/29/2000796.aspx</p>
<img src ="http://www.cppblog.com/justin-shi/aggbug/56813.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-07-22 03:13 <a href="http://www.cppblog.com/justin-shi/archive/2008/07/22/56813.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>滚动控件(ScrollBar)</title><link>http://www.cppblog.com/justin-shi/archive/2008/07/21/56730.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Mon, 21 Jul 2008 01:57:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/07/21/56730.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/56730.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/07/21/56730.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/56730.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/56730.html</trackback:ping><description><![CDATA[<p align=center><font size=5>滚动控件(ScrollBar)<br><br></font><br>　　滚动条(ScrollBar)主要用来从某一预定义值范围内快速有效地进行选择。滚动条分垂直滚动条和水平滚动条两种。在滚动条内有一个滚动框，用来表示当前的值。用鼠标单击滚动条，可以使滚动框移动一页，鼠标单击滚动条两端的剪头可以使滚动框移动一行，也可以直接拖动滚动框。许多窗口控件如列表框和组合框等都带有滚动条子窗口。Win32的滚动条支持比例滚动框，即用滚动框的大小来反映页相对于整个范围的大小。<br>　　当CreateWindowEx创建滚动条时，其风格常数中带SBS_VERT为水平滚动条，不带SBS_VERT或带SBS_HORZ为垂直滚动条。<br>　　创建控件时应初始化滚动条的各种参数。<br>　　应用程序可以通过调用SendMessage向控件发送如下消息来设定控件各种参数。<br>
<table cellSpacing=1 cellPadding=1 width="97%" align=center bgColor=#ffffff border=0>
    <tbody>
        <tr align=middle bgColor=#cccccc>
            <td>uMsg</td>
            <td>wParam</td>
            <td>lParam</td>
            <td>说明</td>
        </tr>
        <tr bgColor=#cccccc>
            <td width=110 rowSpan=7>SBM_ENABLE_ARROWS</td>
            <td width=110>ESB_DISABLE_BOTH</td>
            <td>0</td>
            <td>禁止双向滚动剪头</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>ESB_DISABLE_DOWN</td>
            <td>0</td>
            <td>禁止向下滚动剪头</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>ESB_DISABLE_LTUP</td>
            <td>0</td>
            <td>禁止向上和向左滚动剪头</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>ESB_DISABLE_LEFT</td>
            <td>0</td>
            <td>禁止向左滚动剪头</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>ESB_DISABLE_RTDN</td>
            <td>0</td>
            <td>禁止向下和向右滚动剪头</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>ESB_DISABLE_UP</td>
            <td>0</td>
            <td>禁止向上滚动剪头</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>ESB_ENABLE_BOTH</td>
            <td>0</td>
            <td>允许双向滚动剪头(撤消各种禁止)</td>
        </tr>
        <tr bgColor=#cccccc>
            <td rowSpan=2>SBM_SETPOS</td>
            <td rowSpan=2>指定位置</td>
            <td>TRUE</td>
            <td>设置滚动框位置，并重绘控件</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>FALSE</td>
            <td>设置滚动框位置，不重绘控件</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SBM_SETRANGE</td>
            <td>最小值</td>
            <td>最大值</td>
            <td>设置滚动框位置的变化范围</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SBM_SETRANGEREDRAW</td>
            <td>最小值</td>
            <td>最大值</td>
            <td>设置滚动框位置的变化范围，并重绘控件</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SBM_SETSCROLLINFO</td>
            <td>TRUE或FALSE</td>
            <td>SCROLLINFO结构指针</td>
            <td>本消息通过一个SCROLLINFO结构来同时指定控件的多种参数，具体指定哪些参数由结构中的fMask成员确定。wParam指定是否重绘控件，详见&#8220;SCROLLINFO结构&#8221;</td>
        </tr>
    </tbody>
</table>
　　当用户在滚动条控件上进行各种操作时，其父窗口将收到WM_HSCROLL或WM_VSCROLL通知消息，同时wParam的低16位带有如下表的消息代码(nScrollCode)，wParam的高16位带滚动框的指定位置(nPos)，该值在消息代码等于SB_THUMBPOSITION或SB_THUMBTRACK时才有效。lParam带控件句柄(hwndScrollBar)。<br>　　应用程序可以根据消息代码做相应的操作，重新设置滚动框位置，控件本身是不会改变滚动框位置的。
<table cellSpacing=1 cellPadding=1 width="97%" align=center bgColor=#ffffff border=0>
    <tbody>
        <tr align=middle bgColor=#cccccc>
            <td>消息代码</td>
            <td width=190>动作</td>
            <td>响应</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SB_LINEUP<br>SB_LINELEFT</td>
            <td>用户点击了向上(左)剪头</td>
            <td>滚动框位置减一，客户窗口向上(左)滚动一行。<br>注：这两个代码数值相等，因此可以混用，下同。</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SB_LINEDOWN<br>SB_LINERIGHT</td>
            <td>用户点击了向下(右)剪头</td>
            <td>滚动框位置加一，客户窗口向下(右)滚动一行。</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SB_PAGEUP<br>SB_PAGELEFT</td>
            <td>用户点击了滚动框以上(左)剪杆</td>
            <td>滚动框位置减去一个大单位，客户窗口向上(左)滚动一页。 </td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SB_PAGEDOWN<br>SB_PAGERIGHT</td>
            <td>用户点击了滚动框以下(右)剪杆</td>
            <td>滚动框位置加上一个大单位，客户窗口向下(右)滚动一页。 </td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SB_THUMBPOSITION</td>
            <td>用户拖动并释放滚动框到指定位置</td>
            <td>设定滚动框到指定位置。客户窗口滚动到指定位置。</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SB_THUMBTRACK</td>
            <td>用户正在拖动滚动框</td>
            <td>设定滚动框到指定位置。客户窗口滚动到指定位置。如果应用程序需要快速浏览窗口，可以响应本消息重绘窗口，如果不需要快速浏览，可以等待收到SB_THUMBPOSITION消息时重绘窗口。 </td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SB_ENDSCROLL</td>
            <td>用户释放按下剪头或剪杆的鼠标</td>
            <td>无须做任何响应 </td>
        </tr>
    </tbody>
</table>
　　应用程序可以通过调用SendMessage向控件发送如下消息来取得当前控件各种参数。
<table cellSpacing=1 cellPadding=1 width="97%" align=center bgColor=#ffffff border=0>
    <tbody>
        <tr align=middle bgColor=#cccccc>
            <td>uMsg</td>
            <td width=110>wParam</td>
            <td width=110>lParam</td>
            <td>说明</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SBM_GETPOS</td>
            <td>0</td>
            <td>0</td>
            <td>返回滚动框当前位置。</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SBM_GETRANGE</td>
            <td>最小值地址指针</td>
            <td>最大值地址指针</td>
            <td>在指定地址中填入32位的滚动框位置的变化范围</td>
        </tr>
        <tr bgColor=#cccccc>
            <td>SBM_GETSCROLLINFO</td>
            <td>0</td>
            <td>SCROLLINFO结构指针</td>
            <td>在一个SCROLLINFO结构中返回控件的多种参数，必须事先设定结构的fMask成员来确定具体要取得哪些参数。详见&#8220;SCROLLINFO结构&#8221;</td>
        </tr>
    </tbody>
</table>
　　当控件需要重画时向每父窗口发送WM_CTLCOLORSCROLLBAR消息,同时在wParam中带控件的设备场景句柄(hDC)，lParam中带控件句柄。如果应用程序响应这个消息并返回一个画刷(brush)句柄，控件将根据这个句柄绘制背景色。<br><br>SCROLLINFO结构：
<table>
    <tbody>
        <tr>
            <td>
            <pre>SCROLLINFO STRUCT
            cbSize        DWORD      ?
            fMask         DWORD      ?
            nMin          DWORD      ?
            nMax          DWORD      ?
            nPage         DWORD      ?
            nPos          DWORD      ?
            nTrackPos     DWORD      ?
            SCROLLINFO ENDS</pre>
            </td>
        </tr>
    </tbody>
</table>
成员说明：<br>　　cbSize: SCROLLINFO结构长度字节数，该值在设置和查询参数时都必须填写。<br>　　fMask: 指定结构中的哪些成员是有效，该值共有如下5种选择，可以选择多种用&#8220;OR&#8221;组合起来，该值在设置和查询参数时都必须填写。<br>　　　　SIF_ALL　　　　　　:整个结构都有效<br>　　　　SIF_DISABLENOSCROLL:该值仅在设定参数时使用，视控件参数设定的需要来对本结构的成员进行取舍。<br>　　　　SIF_PAGE　　　　　 :nPage成员有效<br>　　　　SIF_POS　　　　　　:nPos成员有效<br>　　　　SIF_RANGE　　　　　:nMin和nMax成员有效<br>　　nMin:滚动范围最小值<br>　　nMax:滚动范围最大值<br>　　nPage:页尺寸，用来确定比例滚动框的大小<br>　　nPos:滚动框的位置<br>　　nTrackPos:拖动时滚动框的位置，该参数只能查询，不能设置。<br>
<script src="http://xh.go.nease.net/nnselect.js" type=text/javascript>
</script>
<script language=JavaScript src="http://secure-cn.imrworldwide.com/v51.js" type=text/javascript>
</script>
<noscript></noscript><img height=1 alt="" src="http://secure-cn.imrworldwide.com/cgi-bin/m?rnd=1114584693135&amp;ci=cn-netease&amp;cg=0&amp;sr=1152x864&amp;cd=32&amp;lg=zh-cn&amp;je=y&amp;ck=y&amp;tz=8&amp;ct=lan&amp;hp=n&amp;tl=&amp;si=http%3A//jianzp.go.nease.net/scroll.htm&amp;rp=http%3A//jianzp.go.nease.net/" width=1>&nbsp;</p>
<img src ="http://www.cppblog.com/justin-shi/aggbug/56730.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-07-21 09:57 <a href="http://www.cppblog.com/justin-shi/archive/2008/07/21/56730.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何让类的成员函数作为回调函数</title><link>http://www.cppblog.com/justin-shi/archive/2008/07/06/55448.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Sat, 05 Jul 2008 19:58:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/07/06/55448.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/55448.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/07/06/55448.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/55448.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/55448.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为什么类(class)的成员函(member function)数不能作为回调函数(callback function)首先来看看回调函数有怎样的特点。windows中，回调函都显式(explicit)使用CALLBACK修饰符(decorator)修饰(decorated)。实际上CALLBACK就是_stdcall参数传...&nbsp;&nbsp;<a href='http://www.cppblog.com/justin-shi/archive/2008/07/06/55448.html'>阅读全文</a><img src ="http://www.cppblog.com/justin-shi/aggbug/55448.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-07-06 03:58 <a href="http://www.cppblog.com/justin-shi/archive/2008/07/06/55448.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>关于GetDC与GetWindowDC</title><link>http://www.cppblog.com/justin-shi/archive/2008/06/26/54627.html</link><dc:creator>幽幽</dc:creator><author>幽幽</author><pubDate>Thu, 26 Jun 2008 00:27:00 GMT</pubDate><guid>http://www.cppblog.com/justin-shi/archive/2008/06/26/54627.html</guid><wfw:comment>http://www.cppblog.com/justin-shi/comments/54627.html</wfw:comment><comments>http://www.cppblog.com/justin-shi/archive/2008/06/26/54627.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/justin-shi/comments/commentRss/54627.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/justin-shi/services/trackbacks/54627.html</trackback:ping><description><![CDATA[<p><span><font face="Times New Roman" size=2>=============================================================</font></span></p>
<p><font size=2><span><font face="Times New Roman">GetDc</font></span><span>函</span><span>数：</span><span>用于获得</span><span><font face="Times New Roman">hWnd</font></span><span>参数所指定窗口的客户区域的一个设备环境。</span></font></p>
<p><font size=2><span>所获得的设备环境可以是通用、类或者私有类型，具体由指定窗口的类风格决定。对于通用设备环境，</span><span><font face="Times New Roman">GetDc</font></span><span>函数每次获取一个设备环境时都会用默认属性对它进行初始化。该函数获得的类和私有设备环境会与它们最后一次的设置保持一致。当设备环境不再需要时，应该调用</span><span><font face="Times New Roman">ReleaseDC</font></span><span>函数将其释放。</span></font><font size=2><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>GetWindowDC</font></span><span>函数：返回</span><span><font face="Times New Roman">hWnd</font></span><span>参数所指定的窗口的设备环境。</span></font></p>
<p><font size=2><span>获得的设备环境覆盖了整个窗口（包括非客户区），例如标题栏、菜单、滚动条，以及边框。这使得程序能够在非客户区域实现自定义图形，例如自定义标题或者边框。当不再需要该设备环境时，需要调用</span><span><font face="Times New Roman">ReleaseDC</font></span><span>函数释放设备环境。注意，该函数只获得通用设备环境，该设备环境的任何属性改变都不会反映到窗口的私有或者类设备环境中（如果窗口有的话）</span></font><font size=2><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp; ----------</font></span><span>摘自《</span><span><font face="Times New Roman">Delphi&nbsp;&nbsp;&nbsp; Win32</font></span><span>核心</span><span><font face="Times New Roman">API</font></span><span>参考》</span></font></p>
<p><span><font face="Times New Roman" size=2>=============================================================</font></span></p>
<img src ="http://www.cppblog.com/justin-shi/aggbug/54627.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/justin-shi/" target="_blank">幽幽</a> 2008-06-26 08:27 <a href="http://www.cppblog.com/justin-shi/archive/2008/06/26/54627.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>