??xml version="1.0" encoding="utf-8" standalone="yes"?> 串口通讯中的DCBl构 typedef struct _DCB {// dcb DWORD DCBlength; // sizeof(DCB) DORD BaudRate; // current baud rate 指定当前的L特率 DWORD fBinary: 1; // binary mode, no EOF check 指定是否允许二进制模式WIN95中须为TRUE DWORD fParity: 1; // enable parity checking 指定奇偶校验是否允许 DWORD fOutxCtsFlow:1; // CTS output flow control 指定CTS是否用于发送控制。当为TRUE是CTS为OFFQ发送将被挂赗?/font> DTR_CONTROL_DISABLE 值将DTR|ؓ(f)OFF,DTR_CONTROL_ENABLE值将DTR|ؓ(f)ON, DTR_CONTROL_HANDSHAKE 允许DTR"握手", DWORD fDsrSensitivity:1;// DSR sensitivity 当该gؓ(f)TRUE时DSR为OFF时接收的字节被忽?/font> DWORD fTXContinueOnXoff:1; // XOFF continues Tx DWORD fInX: 1; // XON/XOFF in flow control DWORD fRtsControl:2; // RTS flow control RTS_CONTROL_HANDSHAKE?当接收缓冲区于半满时RTS为ON当接收缓冲区过四分之三满时RTS为OFF WORD wReserved; // not currently used 未?必须? WORD XonLim; // transmit XON threshold BYTE Parity; // 0-4=no,odd,even,mark,space 指定端口当前使用的奇偶校验方?可能? StopBits; char XoffChar; // Tx and Rx XOFF character 指定用于发送和接收字符XOFF?/font> char ErrorChar; // error replacement character本字W用来代替接收到的奇偶校验发生错误时的?/font> char EofChar; // end of input character 当没有用二q制模式?本字W可用来指示数据的结?/font> char EvtChar; // received event character 当接收到此字W时,?x)生一个事?/font> WORD wReserved1; // reserved; do not use 未?/font> } DCB; Murali R. Krishnan 1999 q?2 ?/p>
摘要Q?/strong> 讨论常见的堆性能问题以及(qing)如何防范它们。(?9 ) (zhn)是否是动态分配的 C/C++ 对象忠实且幸q的用户Q?zhn)是否在模块间的往q通信中频J地使用?#8220;自动?#8221;Q?zhn)的程序是否因堆分配而运行v来很慢?不仅仅?zhn)遇到q样的问题。几乎所有项目迟早都?x)遇到堆问题。大安惌Q?#8220;我的代码真正好,只是堆太?#8221;。那只是部分正确。更深入理解堆及(qing)其用法、以?qing)?x)发生什么问题,是很有用的?/p>
Q如果?zhn)已经知道什么是堆,可以跛_“什么是常见的堆性能问题Q?#8221;部分Q?/p>
在程序中Q用堆来动态分配和释放对象。在下列情况下,调用堆操作:(x) 堆用了在运行时分配l代码和堆栈的内存之外的部分内存。下囄Z堆分配程序的不同层?/p>
GlobalAlloc/GlobalFreeQ?/strong>Microsoft Win32 堆调用,q些调用直接与每个进E的默认堆进行对话?/p>
LocalAlloc/LocalFreeQ?/strong>Win32 堆调用(Z?Microsoft Windows NT 兼容Q,q些调用直接与每个进E的默认堆进行对话?/p>
COM ?IMalloc 分配E序Q或 CoTaskMemAlloc / CoTaskMemFreeQ:(x)函数使用每个q程的默认堆。自动化E序使用“lg对象模型 (COM)”的分配程序,而申L(fng)E序使用每个q程堆?/p>
C/C++ q行?(CRT) 分配E序Q?/strong>提供?malloc() ?free() 以及(qing) new ?delete 操作W。如 Microsoft Visual Basic ?Java {语a也提供了新的操作Wƈ使用垃圾攉来代替堆。CRT 创徏自己的私有堆Q驻留在 Win32 堆的剙?/p>
Windows NT 中,W(xu)in32 堆是 Windows NT q行时分配程序周围的薄层。所?API 转发它们的请求给 NTDLL?/p>
Windows NT q行时分配程序提?Windows NT 内的核心堆分配程序。它由具?128 个大从 8 ?1,024 字节的空闲列表的前端分配E序l成。后端分配程序用虚拟内存来保留和提交页?/p>
在图表的底部?#8220;虚拟内存分配E序”Q操作系l用它来保留和提交c(din)所有分配程序用虚拟内存进行数据的存取?/p>
分配和释攑֝不就那么单吗Qؓ(f)何花费这么长旉Q?/p>
传统上,操作pȝ和运行时库是与堆的实现共存的。在一个进E的开始,操作pȝ创徏一个默认堆Q叫?#8220;q程?#8221;。如果没有其他堆可用,则块的分配?#8220;q程?#8221;。语aq行时也能在q程内创建单独的堆。(例如QC q行时创建它自己的堆。)除这些专用的堆外Q应用程序或许多已蝲入的动态链接库 (DLL) 之一可以创徏和用单独的堆。Win32 提供一整套 API 来创建和使用U有堆。有?a >堆函敎ͼ英文Q?/u>的详指|请参?MSDN?/p>
当应用程序或 DLL 创徏U有堆时Q这些堆存在于进E空_(d)q且在进E内是可讉K的。从l定堆分配的数据在同一个堆上释放。(不能从一个堆分配而在另一个堆释放。) 在所有虚拟内存系l中Q堆ȝ在操作系l的“虚拟内存理?#8221;的顶部。语aq行时堆也驻留在虚拟内存剙。某些情况下Q这些堆是操作系l堆中的层,而语aq行时堆则通过大块的分配来执行自己的内存管理。不使用操作pȝ堆,而用虚拟内存函数更利于堆的分配和块的用?/p>
典型的堆实现由前、后端分配程序组成。前端分配程序维持固定大块的空闲列表。对于一ơ分配调用,堆尝试从前端列表扑ֈ一个自由块。如果失败,堆被q从后端Q保留和提交虚拟内存Q分配一个大块来满h。通用的实现有每块分配的开销Q这耗费执行周期Q也减少了可使用的存储空间?/p>
Knowledge Base 文章 Q10758Q?#8220;?calloc() ?malloc() 理内存” Q搜索文章编P, 包含了有兌些主题的更多背景知识。另外,有关堆实现和设计的详l讨Z可在下列著作中找刎ͼ(x)“Dynamic Storage Allocation: A Survey and Critical Review”Q作?Paul R. Wilson、Mark S. Johnstone、Michael Neely ?David BolesQ?#8220;International Workshop on Memory Management”, 作?Kinross, Scotland, UK, 1995 q?9 ?http://www.cs.utexas.edu/users/oops/papers.html)Q英文)?/p>
Windows NT 的实玎ͼWindows NT 版本 4.0 和更新版本) 使用?127 个大从 8 ?1,024 字节?8 字节寚w块空闲列表和一?#8220;大块”列表?#8220;大块”列表Q空闲列表[0]Q?保存大于 1,024 字节的块。空闲列表容U了用双向链表链接在一L(fng)对象。默认情况下Q?#8220;q程?#8221;执行攉操作。(攉是将盔RI闲块合q成一个大块的操作。)攉耗费了额外的周期Q但减少了堆块的内部片?/p>
单一全局锁保护堆Q防止多U程式的使用。(请参?#8220;Server Performance and Scalability Killers”中的W一个注意事? George Reilly 所著,?“MSDN Online Web Workshop”上(站点Q?a >http://msdn.microsoft.com/workshop/server/iis/tencom.aspQ英文)。)单一全局锁本质上是用来保护堆数据l构Q防止跨多线E的随机存取。若堆操作太频繁Q单一全局锁会(x)Ҏ(gu)能有不利的影响?/p>
以下是?zhn)使用堆时会(x)遇到的最常见问题Q? 竞争通常?x)导致线E和q程的上下文切换。上下文切换的开销是很大的Q但开销更大的是数据从处理器高速缓存中丢失Q以?qing)后来线E复zL的数据重建?/p>
竞争是在分配和释放操作中D速度减慢的问题。理x况下Q希望用没有竞争和快速分?释放的堆。可惜,现在q没有这L(fng)通用堆,也许来?x)有?/p>
在所有的服务器系l中Q如 IIS、MSProxy、DatabaseStacks、网l服务器?Exchange 和其他), 堆锁定实在是个大瓉。处理器数越多,竞争p?x)恶化?/p>
现在(zhn)明白用堆时存在的问题了,N(zhn)不x有能解决q些问题的超U魔吗Q我可希望有。但没有法能堆运行加快—因此不要期望在产品之前的最后一星期能够大ؓ(f)改观。如果提前规划堆{略Q情况将?x)大大好转。调整用堆的方法,减少对堆的操作是提高性能的良斏V?/p>
如何减少使用堆操作?通过利用数据l构内的位置可减堆操作的次数。请考虑下列实例Q?/p>
块化是友好的处理器高速缓存,特别是对?L1-高速缓存,因ؓ(f)它提供了增加的位|?—不用说对于块分配,很多数据块会(x)在同一个虚拟页中?/p>
使用上述技术将获得的好处会(x)因对象类型、大及(qing)工作量而有所不同。但总能在性能和可升羃性方面有所收获。另一斚wQ代码会(x)有点Ҏ(gu)Q但如果l过深思熟虑,代码q是很容易管理的?/p>
下面是一些提高速度的技术:(x) ׃几个同事的努力和辛勤工作Q?998 q初 Microsoft Windows(R) 2000 中有了几个重大改q:(x) q些改进避免了对分配高速缓存的需求,但不排除其他的优化。?Windows NT5 堆评估?zhn)的代码;它对?1,024 字节 (1 KB) 的块Q来自前端分配程序的块)是最佳的?strong>GlobalAlloc() 上述改进已在 Windows 2000 beta 2 ?Windows NT 4.0 SP4 中用。改q后Q堆锁的竞争率显著降低。这使所?Win32 堆的直接用户受益。CRT 堆徏立于 Win32 堆的剙Q但它用自q块堆,因而不能从 Windows NT 改进中受益。(Visual C++ 版本 6.0 也有改进的堆分配E序。) 分配高速缓存允?dng)R速缓存分配的块,以便来重用。这能够减少对进E堆Q或全局堆)的分?释放调用的次敎ͼ也允许最大限度的重用曄分配的块。另外,分配高速缓存允许收集统计信?以便较好地理解对象在较高层次上的使用?/p>
典型圎ͼ自定义堆分配E序在进E堆的顶部实现。自定义堆分配程序与pȝ堆的行ؓ(f)很相伹{主要的差别是它在进E堆的顶部ؓ(f)分配的对象提供高速缓存。高速缓存设计成一套固定大(?32 字节?4 字节?28 字节{)。这一个很好的{略Q但q种自定义堆分配E序丢失与分配和释放的对象相关的“语义信息”? 与自定义堆分配程序相反,“分配高速缓?#8221;作ؓ(f)每类分配高速缓存来实现。除能够提供自定义堆分配E序的所有好处之外,它们q能够保留大量语义信息。每个分配高速缓存处理程序与一个目标二q制对象兌。它能够使用一套参数进行初始化Q这些参数表Cƈ发别、对象大和保持在空闲列表中的元素的数量{。分配高速缓存处理程序对象维持自qU有I闲实体池(不超q指定的阀|q用私有保护锁。合在一P分配高速缓存和U有锁减了与主pȝ堆的通信量,因而提供了增加的ƈ发、最大限度的重用和较高的可~性?/p>
需要用清理程序来定期查所有分配高速缓存处理程序的zd情况q回收未用的资源。如果发现没有活动,释攑ֈ配对象的池,从而提高性能?/p>
可以审核每个分配/释放zd。第一U信息包括对象、分配和释放调用的L。通过查看它们的统计信息可以得出各个对象之间的语义关系。利用以上介l的许多技术之一Q这U关pd以用来减内存分配?/p>
分配高速缓存也起到了调试助手的作用Q帮助?zhn)跟踪没有完全清除的对象数量。通过查看动态堆栈返回踪q和除没有清除的对象之外的签名,甚至能够扑ֈ切的失败的调用者?/p>
MP 堆是对多处理器友好的分布式分配的E序包,?Win32 SDKQWindows NT 4.0 和更新版本)中可以得到。最初由 JVert 实现Q此处堆抽象建立?Win32 堆程序包的顶部。MP 堆创建多?Win32 堆,q试囑ְ分配调用分布C同堆Q以减少在所有单一锁上的竞争?/p>
本程序包是好的步?—一U改q的 MP-友好的自定义堆分配程序。但是,它不提供语义信息和缺乏统计功能。通常?MP 堆作?SDK 库来使用。如果用这?SDK 创徏可重用组Ӟ(zhn)将大大受益。但是,如果在每?DLL 中徏立这?SDK 库,增加工作设|?/p>
要在多处理器机器上~,则算法、实现、数据结构和g必须动态~。请看最l常分配和释攄数据l构。试问,“我能用不同的数据l构完成此工作吗Q?#8221;例如Q如果在应用E序初始化时加蝲了只读项的列表,q个列表不必是线性链接的列表。如果是动态分配的数组非常好。动态分配的数组减内存中的堆块和片Q从而增强性能?/p>
减少需要的对象的数量减少堆分配程序的负蝲。例如,我们在服务器的关键处理\径上使用五个不同的对象,每个对象单独分配和释放。一起高速缓存这些对象,把堆调用从五个减到一个,显著减少了堆的负载,特别当每U钟处理 1,000 个以上的h时?/p>
如果大量使用“Automation”l构Q请考虑从主U代码中删除“Automation BSTR”Q或臛_避免重复?BSTR 操作。(BSTR q接Dq多的重分配和分?释放操作。) Ҏ(gu)有^台往往都存在堆实现Q因此有巨大的开销。每个单独代码都有特定的要求Q但设计能采用本文讨论的基本理论来减堆之间的相互作用? Murali Krishnan ?Internet Information Server (IIS) l的首席软g设计工程师。从 1.0 版本开始他p?IISQƈ成功发行?1.0 版本?4.0 版本。Murali l织q?IIS 性能l三q?(1995-1998), 从一开始就影响 IIS 性能。他拥有威斯h?Madison 大学?M.S.和印?Anna 大学?B.S.。工作之外,他喜Ƣ阅诅R打排球和家庭烹饪?/p>
外壳对话?br>
外壳对话框的U密
常见的Windows的通用对话框被装在Comdlg32.dllQ这l我们的~程提供了很大的便利。但它还不够完整Q我们在pȝ里经常能看到大量的可重复使用的对话框,但在Windows的文档里你却找不到它们的调用Ҏ(gu)。而如果我们自己去做这L(fng)界面是非常费时费力的而且也是没有必要的,因ؓ(f)q些对话框实际上很容易得到。这里我要介l一些已l众所周知或不知的对话框,它们可以应用在我们的E序中ɽE序昑־非常友好和专业?br>
览文g夹对话框
?.23
大多数DelphiE序员都知道如何使用VCL的TOpenDialog控g来让用户览要打开的文件。然而有时你可能只想让用户选择文g夹而不是特定的文gQwindows已经提供了一个这L(fng)对话框如?.23所C。我们可以通过公开的函数SHBrowseForFolder来调?(q个函数定义在ShlObj单元)Q函数定义如下:(x)
function SHBrowseForFolder(var BrowseInfo: TBrowseInfo): PItemIDList; stdcall;
q个函数只有一个参敎ͼ但这个参数是一个比较复杂的记录cd
TBrowseInfo = packed record
hwndOwner: HWND;
pidlRoot: PItemIDList;
pszDisplayName: PChar;
lpszTitle: PChar;
ulFlags: UINT;
lpfn: TFNBFFCallBack;
lParam: LPARAM;
iImage: Integer;
end;
hwndOwner数据成员包含对话框的父窗体的H口句柄Q可以把它设?。PIdlRoot数据成员指向一个PIDL的指针对应于对话框初始化时的根目录。指定了PIdlRoot后,只有根目录?qing)它的子目录会(x)出现在对话框中。可以设定它为nilQ这时缺省的根目录是桌面QpszDisplayName 数据成员指向一个缓冲区可以用来储存被用户选中的文件名Q缓冲区的大至ؓ(f)MAX_PATH q个常数那么大,否则遇到特别长的文g名会(x)溢出。lpszTitle 数据对象指向一个以nulll尾的字W串Q字W串作ؓ(f)对话框的标题来显C。注意标题不要太长,否则昄时会(x)被截断。ulFlags 标志数据对象用来限制在对话框中显C的文g夹类型。可以设定它?或下列值的l合Q?br>
//在对话框中会(x)包含一个状态区Q回调函数可以通过向对话框发送消息来讑֮状?br>
BIF_STATUSTEXT
//只允?dng)R择标准文gpȝQ若选了非标准的文g夹如打印机,认按钮?x)变?br>
BIF_RETURNONLYFSDIRS = $0001;
//不选择|络文g?br>
BIF_DONTGOBELOWDOMAIN = $0002;
// l状态条留出I白
BIF_STATUSTEXT = $0004;
// 只选择文gpȝ的上U目?br>
BIF_RETURNFSANCESTORS = $0008;
//只选择计算?br>
BIF_BROWSEFORCOMPUTER = $1000;
//只选择打印?br>
BIF_BROWSEFORPRINTER = $2000;
//包括文g也可以?br>
BIF_BROWSEINCLUDEFILES = $4000;
注意Q如果你惛_话框昄lpszTitle里的用户定制的状态条信息Q必d?BIF_STATUSTEXT标识。Lpfn数据对象是一个回调函数类型的指针Q函数类型如下:(x)
TFNBFFCallBack = function(DialogHandle: HWND;
MessageID: UINT; PIDL: PItemIDList; Data: LPARAM):Integer; stdcall;
q是一个回调函敎ͼ可以用来在同用户交互时控制和更新对话框的昄。如果你不想控制对话框,可以把它设成nilQlParam 数据对象允许你在回调函数中以参数l(f)pfn形式q回一个指针(通常我们用它来返回对象)Q当然也可以把它设成?。IImage数据成员不需要设|,因ؓ(f)它是用来接收pȝ中同文g夹相关的图标列表索引的,我们q里讑֮??br>
SHBrowseForFolder函数q回一个唯一的指向被选择的文件夹的PIDL。如果文件夹是一个传l的文g对象的话Q可以用函数SHGetPathFromIDList把PIDL转换为真实的目录。同Ӟ作ؓ(f)调用者,必须负责释放被返回的item identifier listQ用IMalloc COM 接口来释放?br>
注意Q不要用FreeMem或其他方法来释放 PIDL Q这是因为外壳的内存理是独立的Q只能用IMalloc来释放?br>
现在我们已经可以昄对话框了Q那让我们更深入一步看看如何能够控制对用户动作的反应,q就要用C回调函数 TFNBFFCallBack?注意回调函数的意思就是,你只是实C它,pȝq道什么时候去调用它,好比一个守株待兔的例子?br>
DialogHandle参数代表对话框窗口句柄。通常可以用这个句柄给对话框发消息QMessageID 参数q不是一个TMessage l构的记录,它是对话框通过回调函数发给用户消息的,它可以是下面两个|(x)
BFFM_INITIALIZED = 1; // 对话框将要显C?br>
BFFM_SELCHANGED = 2; // 用户选中了某?
PIDL参数包含其他的额外信息。如果MessageID ?BFFM_INITIALIZEDQPIDL等于nil。如果MessageID是BFFM_SELCHANGEDQPIDL的值将是一个PIDL 对应于用户选择的文件夹。Data 参数包含用户付给TbrowseInfo记录中的Lparam数据成员的|通常可以传递一个对象指针。下面是一个简单的回调函数的例子:(x)
function BrowseForFolderCallback(DialogHandle: HWND;
MessageID: UINT; PIDL: PItemIDList; Data: LPARAM):
Integer;
begin
//响应对话框的通知消息
case (MessageID) of
BFFM_INITIALIZED:
DialogInitialized(DialogHandle, Data);
BFFM_SELCHANGED:
HandleNewSelection(DialogHandle, PIDL, Data);
end;
Result := 0; // 总返?.
end;
在回调函数里Q可以根据用L(fng)输入发送三个用L(fng)消息l对话框Q下面是消息ID:
// 改变对话框的状态信?br>
BFFM_SETSTATUSTEXT = WM_USER + 100;
//控制定按钮失效与否
BFFM_ENABLEOK = WM_USER + 101;
//改变选择的文件夹
BFFM_SETSELECTION = WM_USER + 102;
通常Q这些消息发送给对话框之根据用L(fng)选择更新昄Q当然你也可以发送其他的消息l对话框Q比如可以发送WM_SETTEXT消息来改变对话框的标题?br>
下面是一个发送消息的例子Q见?.11Q:(x)
PostMessage(DialogHandle, BFFM_SETSELECTION, True, LPARAM(PChar (NewPath)));
?.11
Message ID
WParam
LParam
BFFM_SETSTATUSTEXT
没有使用
一个指向新的状态信息的Pchar
BFFM_ENABLEOK
没有使用
True使得认按钮有效QFalse无效
BFFM_SETSELECTION
如果Lparam是\径则为TrueQ若Lparam是PIDL则ؓ(f)False
指向被选择的文件\径或PIDL的Pchar
另外要提到的是,Delphi也提供了对这个函数的装Q那是SelectDirectory函数?br>
关于对话?/strong>
通常我们都要在自qE序里加上一个关于对话框来显CZ些版本信息等{,W(xu)indows为我们提供了一个标准的对话框如?.24所C,可以在一定范围内对它定制Q不q它只适合昄单的标识和文本(我觉得用处极)。我们可以通过函数ShellAbout来调用它Q声明在ShellAPI单元里)Q函数定义如?
function ShellAbout(Owner: HWND; ApplicationName: PChar;
OtherText: PChar; IconHandle: HICON): Integer; stdcall;
Owner参数标识了拥有对话框的父H体句柄Q通常设ؓ(f)0Q表明没有父H体。ApplicationName 参数包含对话框的标题Q字W串中可以包?#8220;#”字符Q它能v到分割符的作用。这U情况下Q函C(x)把分割符前的字符串作为标题栏Q分割符后的部分作ؓ(f) "Microsoft"字符串后的第一行。OtherText参数包含了打显C在Microsoft 版本和版权信息后的字W串。IconHandle 参数标识了打显C在对话框上的图标标识,如果设ؓ(f)0Q函C(x)昄Windows~省的图标?br>
?.24
?.25
格式化对话框
SHFormatDrive函数?x)显CZ个格式化对话框,如图2.25所C,它是一个半公开的函数。但现在它不在微软的SDK里。然而微软承认它的存在ƈ把它从Shell32.dll里用名字公开声明QDelphi中的函数定义如下Q?br>
function SHFormatDrive(Owner: HWND; Drive: UINT;
FormatID: UINT; OptionFlags: UINT): DWORD; stdcall;
Owner参数标识拥有对话框的H体句柄Q文档中推荐不要设ؓ(f)0Q但实际上好像没什么媄响。Drive参数是用来标识打格式化的驱动器的数|它是?为底的,从A开?A:=0,B:=1依此cL。FormatID 参数允许我们指定一个格式化的模板,通常情况下,只要赋gؓ(f)SHFMT_ID_DEFAULT可以了。OptionFlags 参数是一个选项掩码Q来定格式化的选项。当前有两个选项Q?br>
SHFMT_OPT_FULL = $0001; // 快速格式化
SHFMT_OPT_SYSONLY = $0002; // 复制pȝ文g
如果函数调用p|Q会(x)q回下列错误中的一U来表明错误原因Q错误常数如下:(x)
SHFMT_NOFORMAT = $FFFFFFFD; // 驱动器无法格式化
SHFMT_CANCEL = $FFFFFFFE; //格式化被取消?br>
SHFMT_ERROR = $FFFFFFFF; //其他错误
Windows NT ?WideChar
在进一步研I未公开的函数前, 我们必须清楚一点,对于未公开的函数来说以nulll尾的字W串cd参数大多数被声明为类型指针而不是PChar。这有点像陷阱,但必L认这是事实。在Win 9X上所有的字符串类型参数声明ؓ(f)PAnsiCharQ而在Windows NT上被声明为PWideChar。如果你想你的应用程序适应所有^収ͼ你必考虑两种情况Q在q行时要判断q_cdQ这是很讨厌的,但这也是使用未公开的API的代仗?br>
选择图标对话?/strong>
?.26
我们要讨论的W一个完全未公开的函数是PickIconDlg。如?.26所C个函C(x)昄一个对话框Q用户可以用来从文g中选择一个图标资源。它通常是用文gcd~辑器来兌图标和某一文gcd的,也会(x)在快h式对话框中被调用来修改快h式的图标。这个函CShell32.dll 用?2来公开出来Q函数定义如下:(x)
function PickIconDlg(Owner: HWND; FileName: Pointer;
MaxFileNameChars: DWORD; var IconIndex: DWORD):LongBool; stdcall;
Owner参数和上面的意义cM。FileName 参数指向一个缓冲区Q包含了被浏览图标的文g名,~冲不小于MAX_PATHQ?。MaxFileNameChars 指定字符数量大小。IconIndex 常数是以0为底的图标烦引,当对话框打开时会(x)把焦点定在IconIndex对应的图标上Q函数返回后QIconIndex指向最后被选的图标索引。如果用L(fng)了取消按钮,函数q回False?br>
q行E序对话?/strong>
?.27
RunFileDlg函数是相当灵zȝQ如?.27所C就是调用开始菜单的q行子菜单后?x)显C的对话框,我们通过?1把它从Shell32.dll暴露出来。下面是函数声明Q?br>
procedure RunFileDlg(Owner: HWND; IconHandle: HICON;
WorkPath: Pointer; Caption: Pointer; Description: Pointer; Flags: UINT); stdcall;
Owner参数׃用再说了。IconHandle参数是显C在对话框上的图标句柄,如果为nilQ缺省的icon会(x)使用。WorkPath 参数指向一个字W串来指定应用程序运行的工作路径?Title 参数指向作ؓ(f)对话框标题的字符Ԍ如果为nilQ就使用~省的标题。Description 参数指向一个描q字W串Q主要是告诉用户如何dQ可以设为nilQ这时用缺省的描述?Flags参数用一l位掩码来设定对话框的属性。下面是定义Q?br>
RFF_NOBROWSE = $01; // Ud览按钮
RFF_NODEFAULT = $02; // 无缺省的选项
RFF_CALCDIRECTORY = $04; // 由文件名定工作路径
RFF_NOLABEL = $08; // L~辑框标{?br>
RFF_NOSEPARATEMEM = $20; // L在单独的内存I间q行的复选框 (只对NT有效)
q个对话框一个很好的Ҏ(gu)是允许你控制用户可以运行的应用E序。当用户选择了确认按钮,对话框的父窗体会(x)发送一个通知消息来传递将要运行的E序信息。通知消息是一?WM_NOTIFY消息Q它的通知代码讑֮为RFN_VALIDATE (-510)Q然后lParam指向一?TNM_RunFileDlg记录。定义如下:(x)
TNM_RunFileDlg = packed record
hdr: TNMHdr;
lpFile: Pointer;
lpDirectory: Pointer;
nShow: LongBool;
end;
hdr数据对象是TNMHdrcdQ它是一U标准的Windows数据cdQ每个WM_NOTIFY消息的lParam参数都会(x)指向q个数据成分。同时根据不同的消息cdQ可能一些额外的数据跟在记录后面Q标准的TNMHdr记录定义如下Q?br>
TNMHdr = packed record
hwndFrom: HWND;
idFrom: UINT;
code: UINT;
end;
记录中的hwndFrom包含发送消息的H口句柄QidFrom则包含发送消息的控g标示W,code 中包含标识被发送的消息的通知代码?br>
在TNMHdr记录后被打包的额外数据包含三个数据成分:(x)LpFile指向一个包含将要运行的文g的\径字W串QLpDirectory指向正在q行E序的工作目录字W串Q最后,nShow 用来指定要q行的应用程序是否可见?br>
对于本文中特定的消息Q只对TNMHdr记录中的Code感兴,通过验Code可以保我们收到一个运行文件校验消息,同时使我们可以存取额外的TNM_RunFileDlg数据成员。当TNMHdr记录中的code{于RFN_VALIDATE(-510)Ӟ可以获得一个TNM_RunFileDlg记录。下面是校验消息的代码:(x)
var
FileToRun: String;
...
if TheMessage.Msg = WM_NOTIFY then
if PNMHdr(TheMessage.LParam).code = RFN_VALIDATE then
WideCharToStrVar(PNM_RUNFILEDLG(
TheMessage.LParam).lpFile, FileToRun);
...
注意只有当我们已l检验TNMHdr的Code为RFN_VALIDATE后,才映LParam 参数为PNM_RunFileDlgcd?br>
通知消息的返回值决定了应用E序是否能够q行Q下面是可能的|(x)
RF_OK = $00; //允许E序q行
RF_CANCEL = $01; //取消操作Q关闭对话框
RF_RETRY = $02; //取消操作Q对话框仍然打开
查找文g对话?/strong>
?.28
调用查找文g对话框的函数是SHFindFilesQ对话框如图2.28所C。它是从Shell32.dll按烦引?0公开出来?
function SHFindFiles(SearchRoot: PItemIDList;
SavedSearchFile: PItemIDList): LongBool; stdcall;
SearchRoot 参数允许从一个特定的文g夹开始查找,同在资源理器中在文件夹上用右键点击查找菜单的效果是一L(fng)。如果设为nilQ那么查找是从桌面开始的?SavedSearchFile 参数让你指定一个以前查询保存的查找{略文gQ?.fnd文gQ,Ҏ(gu)以前的设定来查找Q若不需要的话可以设定ؓ(f)nil。如果你指定了一个非I值的SearchRoot PIDLQ那么在调用完SHFindFiles后必负责释放掉。但是有点奇怪的是,如果你指定了一个非I的SavedSearchFile PIDL参数Q函数成功调用的话,你不能去释放q个 PIDLQ否则会(x)出错Q但如果调用p|了的话,你必释攑֮?br>
同大多数对话框函C一Pq个函数是非模态的Q也是pȝ在另外一个独立的U程中启动对话框Q然后立卌回,对话框会(x)在你的程序结束后自动关闭。也是说你没有M直接的方法来告诉用户如何使用查找到的l果Q所以要想知道用h到的文g的话Q最好是让你的程序支持文件拖放,以便让用h扑ֈ的文件拖攄你?br>
查找?sh)脑对话?/strong>
同SHFindFiles比较接近的一个函数是SHFindComputerQ这个函数调用的l果同开始菜单上查找?sh)脑菜单调用的结果是一L(fng)。它的参数同SHFindFiles完全一P不同之处在于它完全忽略传递给它的参数Q很昄是保留v来ؓ(f)了将来扩展的需要。这里我们只要把参数都设成nil可以了Q另外注意这个对话框也是非模态的?SHFindComputer 是从Shell32.dll 以烦引号91公开出来的:(x)
function SHFindComputer(Reserved1: PItemIDList;
Reserved2: PItemIDList): LongBool; stdcall;
查找文g对话?/strong>
通过调用GetFileNameFromBrowse函数可以调出q个对话框,不过说实在的Q它实际上只是GetOpenFileName 函数的简单封装。而我们常用的TOpenDialog控g也是对GetOpenFileName 函数装Q这个函数我们很会(x)ȝ接用它。不q还是写出来吧,它是从Shell32.dll里按索引?3公开出来的:(x)
function GetFileNameFromBrowse(Owner: HWND;
FileName: Pointer; MaxFileNameChars: DWORD;
InitialDirectory: Pointer; DefaultExtension: Pointer;
Filter: Pointer; Caption: Pointer): LongBool; stdcall;
?.29
大多数参数对应于OPENFILENAME l构的成员。Owner参数我想׃用再重复了, FileName 参数指向一个初始化对话框编辑控制文件名的缓冲区Q函数返回后FileName包含被选择的文件\径,它的大小一般设成MAX_PATHQ?那么大。MaxFileNameChars 参数用来指定FileName~冲区的大小?InitialDirectory参数指向对话框初始化的目录名Q但如果FileName参数被指定了QInitialDirectory׃(x)被忽略而用FileName参数中的路径。DefaultExtension参数指向一个包含要搜烦的缺省扩展名的字W串。Filter参数指向一个以nulll尾的可以用来在下拉列表中限定文件类型的qo(h)字符丌ӀCaption参数指向对话框标题字W串?br>
如果用户选择了一个要打开的文Ӟ函数q回TrueQ当有错误发生,用户选择取消按钮或关闭对话框的话?x)返回False?br>
外壳对象属性对话框
另一个未公开的对话框函数是SHObjectPropertiesQ它可以用来昄外壳对象的属性,比如驱动器、文件夹或文件等Q运行效果如?.29所C。函数可以从Shell32.dll中按索引?78公开出来Q定义如下:(x)
function SHObjectProperties(Owner: HWND; Flags: UINT;
ObjectName: Pointer; InitialTabName: Pointer):LongBool; stdcall;
Flags参数用来指定ObjectName参数对应对象的类型,它可以是下列标识Q?br>
//打印?br>
OPF_PRINTERNAME = $01;
//路径
OPF_PATHNAME = $02;
ObjectName参数指向一个包含\径名的字W串或是要显C属性的打印机名。如果打印机是本地的Q可以用实际的打印机名Q如果是|络打印机,需要用完整的UNC样式名称Q比如\\COMPUTERNAME\PRINTERNAME。InitialTabName参数指向一个属性对话框中页面名U字W串Q用来指定要昄的缺省页面。如果InitialTabName参数为nilQ或不匹配Q何页面的名称Q第一个属性页面将?x)被昄?br>
如果函数调用成功?x)返回TrueQ如果失败会(x)q回False。要惌得扩展的错误信息Q可以调用API函数GetLastError。要注意的是q个对话框是非模态的Q类g查找文g对话框,所以函C被调用,p定会(x)昄一个对话框Q同时我们没有办法知道用户什么时候关闭了对话框?br>
映射|络驱动对话?/strong>
?.30
?.30昄了映网l驱动器的对话框Q我们通过SHNetConnectionDialog函数调用它(win 9x和W(xu)in NT上都支持Q,它可以按索引?60从Shell32.dll暴露出来Q函数定义如下:(x)
function SHNetConnectionDialog(Owner: HWND;
ResourceName: Pointer; ResourceType: DWORD): DWORD; stdcall;
SHStartNetConnectionDialog函数也会(x)昄同样的对话框Q但它显C的对话框是非模态的Q同时只在NT上才支持。它可以按烦引?15从Shell32.dll中公开出来Q函数定义如下:(x)
function SHStartNetConnectionDialog(Owner: HWND;
ResourceName: PWideChar; ResourceType: DWORD):DWORD; stdcall;
上面两个函数的参数完全相同。其中ResourceName参数指向一个要q接的网l资源UNC路径名。指定了q个参数的话Q显C的对话框中被预讄q接资源׃可改变了。如果这个参Cؓ(f)nilQ则在对话框中用户可以指定要q接的资源。ResourceType参数可以是下面的g一QRESOURCETYPE_DISK或RESOURCETYPE_PRINT。它的不同将?x)生成不同的对话框。参Cؓ(f)RESOURCETYPE_DISK允许我们为网l驱动资源指定一个盘W,另一个参数允许我们映一个ƈ行口名比如LPT2Z个网l打印机。然而,不知道ؓ(f)什么RESOURCETYPE_PRINT参数在NT上无效?br>
?.31
如果函数调用成功的话Q返回值是NO_ERRORQ如果用户取消的对话框,则返?-1($FFFFFFFF)Q如果调用失败则q回其他的错误代码,具体错误信息可以用GetLastError API调用获得?br>
关闭pȝ对话?/strong>
ExitWindowsDialog和RestartDialog函数可以用来昄关闭和重启系l对话框Q如?.31Q,它们同公开的ExitWindowsEx API函数没有什么太大的不同Q但在其q程中都?x)生一个对话框。ExitWindowsDialog函数可以按烦引?0从Shell32.dll中公开出来QRestartDialog函数的在Shell32.dll中的索引值则?9Q两个函数的定义如下Q?br>
procedure ExitWindowsDialog(Owner: HWND); stdcall;
function RestartDialog(Owner: HWND; Reason: Pointer; ExitType: UINT): DWORD; stdcall;
对ExitWindowsDialog函数来说Q对话框好像q不使用Owner参数作ؓ(f)父窗口,在Windows 95上,当操作成功的话ownerH口?x)收C个WM_QUIT消息。在Windows NT上,owner H口Ҏ(gu)不被使用。同时这个函数没有返回|所以没有办法知道用户选择了什么操作以?qing)操作是否被取消了?br>
RestartDialog函数更有用一些,当我们修改了pȝ的设|,q希望重新启动系l修改生效的时候可以用这个函数。Reason参数指向一个要昄在对话框中的字符Ԍ用来解释关闭pȝ的原因。ExitType参数指定关闭cdQ可以用ExitWindowsEX函数使用值的一个子集及(qing)额外的几个新|下面是它们的完全列表Q?br>
EWX_LOGOFF = $00;
EWX_SHUTDOWN = $01;
EWX_REBOOT = $02;
EW_RESTARTWINDOWS = $42;
EW_REBOOTSYSTEM = $43;
EW_EXITANDEXECAPP = $44;
如果用户选择执行关闭操作Q函数返回IDYESQ否则返回IDNO?br>
要注意的是显C在对话框中的原因字W串后M(x)跟着一个系l缺省提供的字符串用来显C确认信息,所以应该在我们的Reason字符串后附上I格或回车换行字W。另外返回g能用于确定操作的成功性,它只表明用户的选择Q如果重启操作由于某些原因失败了Q返回g然是IDYES。同时要注意的是要想调用成功Q用戯必须有SE_SHUTDOWN_NAME权限Q在NT上)?br>
~少内存对话?/strong>
SHOutOfMemoryMessageBox是一个未公开的函敎ͼ当系l内存不x可以用来昄标准的外壳信息对话框Q它在Shell32.dll中的索引值是126Q函数定义如下:(x)
function SHOutOfMemoryMessageBox(Owner: HWND;
Caption: Pointer; Style: UINT): Integer; stdcall;
它会(x)调用MessageBox APIQ同时传?个标准的参数和ERROR_OUTOFMEMORY错误消息。Caption参数指向对话框标题字W串。如果Caption为nilQ父H口的标题就?x)被使用。Style参数可以被设|ؓ(f)LMessageBox函数使用的MB_XXX常数的组合,通常讄它ؓ(f)MB_OK或MB_ICONHAND。函数调用返回值参见SDK中MessageBox函数说明?br>
当MessageBox函数被调用时QMB_SETFOREGROUND标识?x)被d到Style参数中,但如果第一ơ调用失败了的话QMessageBox函数?x)被再次调用Q这ơMB_SYSTEMMODAL 标识?x)被d到Style参数中。MB_SYSTEMMODAL同MB_ICONHAND标识l合后会(x)忽略内存状况来显C消息对话框。当内存实不Ӟ函数不会(x)昄M东西Q然而它仍然?x)返回MessageBox函数调用l果。所以我们可以根据返回值判断函数是否调用成功了?br>
I间不对话?/strong>
?.32
另一个资源相关的函数是SHHandleDiskFullQ它?x)显C磁盘不的信息对话框(如图2.32Q。我们可以在׃没有_盘I间时导致程序无法运行的条g下调用这个函敎ͼ调用后,如果回收站中有什么东西没有删除的话,对话框允许用hI回收站来释攄盘空间。它在Shell32.dll中的索引gؓ(f)185Q函数的定义如下Q?br>
procedure SHHandleDiskFull(Owner: HWND; Drive: UINT); stdcall;
Drive参数用于指定?为底的驱动器盘符?代表A:\Q?代表B:\Q依此类推。这个函数的应用比较困难Q因为当回收站中没有M东西时对话框不会(x)昄Q同时也没有Mq回DC对话框是否昄Q还无法知道用户的操作,比如它是否真的清IZ。看h比较可行的应用只能是E序自行监视盘剩余I间Q只是用这个对话框作ؓ(f)一个快速修复的工具?br>
一般外x息对话框
ShellMessageBox函数仅仅是一个对MessageBox函数的简单封装函敎ͼ它允怋用字W串资源标识W或标准的以nulll尾的字W串Q同时还允许加入支持格式化ForamtMessage函数的控制符。ShellMessageBox函数在Shell32.dll中的索引gؓ(f)183Q?br>
function ShellMessageBoxA(Module: THandle; Owner: HWND;
Text: PChar; Caption: PChar; Style: UINT;
Parameters: array of Pointer): Integer; cdecl;
更确切地说这个函数应该叫ShellMessageBoxA因ؓ(f)它只支持ANSI字符Ԍq有一个UNICODE的版本的函数ShellMessageBoxWQ它的烦引gؓ(f)182Q但它只在Windows NT上才有,函数定义如下Q?br>
function ShellMessageBoxW(Module: THandle; Owner: HWND;
Text: PWideChar; Caption: PWideChar; Style: UINT;
Parameters: array of Pointer): Integer; cdecl;
Module参数是提供字W串资源的模块句柄,句柄可以用GetModuleHandle函数获得。顾名思义Text 参数指向一个要昄在对话框中的文本Q它也可以是资源字符串IDQ文本中可以包括格式控制序列Q它?yu)?x)被在Parameters中提供的额外字符串替代。控制符格式?#8220;%#”Q其?#8220;#”是额外字W串在参C的位|,比如“%1”被W一个Parameters数组中的字符串元素替代,“%3”会(x)被第三个元素替代Q依此类推。Caption参数指向对话框标题文本,同样它也可以是资源IDQ如果参Cؓ(f)nilQOwner指定的窗口标题将被用于对话框标题。Style参数是由位掩码标识组成的Q可以设|成MessageBox函数支持的MB_XXX常数的组合。返回值同MessageBox完全一栗?br>
对于q个函数很重要的一Ҏ(gu)微Y公司使用cdecl来输个函数而不是通常?stdcall。此外,Parameters参数使用了C语言中的可变参数列表Q这意味着q个函数不是语言无关的,q得调用v来非帔R烦,因ؓ(f)Delphiq不直接支持cdecl和可变参数列表。ؓ(f)了解册个问题,Parameters参数被映ؓ(f)一个动态指针列表。同时我们还需要用嵌入式汇编来徏立cdecl样式的堆栈。由于动态指针列表的性质Q我们必至指定一个指针倹{如果不x定要替代的字W串Q简单设|ؓ(f)nil可以了?/font>
]]>
DWORD fOutxDsrFlow:1; // DSR output flow control
DWORD fDtrControl:2; // DTR flow control type
DWORD fOutX: 1; // XON/XOFF out flow control
DWORD fErrorChar: 1; // enable error replacement
DWORD fNull: 1; // enable null stripping TRUEӞ接收时去掉空Q?|字节
DWORD fAbortOnError:1; // abort reads/writes on error TRUE?有错误发生时中止d写操作RTS_CONTROL_DISABLE?RTS|ؓ(f)OFFRTS_CONTROL_ENABLE? RTS|ؓ(f)ON
RTS_CONTROL_TOGGLE?当接收缓冲区仍有剩余字节时RTS为ON ,否则~省为OFF
DWORD fDummy2:17; // reserved 未?/font>
WORD XoffLim; // transmit XOFF threshold
BYTE ByteSize; // number of bits/byte, 4-8 指定端口当前使用的数据位
char XonChar; // Tx and Rx XON character 指定用于发送和接收字符XON的?/font>
]]>
Q?Q取得宿主进E(卌注入木马的进E)的进EID dwRemoteProcessIdQ?br>
Q?Q取得DLL的完全\径,q将其{换ؓ(f)宽字W模式pszLibFileNameQ?br>
Q?Q利用Windows API OpenProcess打开宿主q程Q应该开启下列选项Q?br>
a.PROCESS_CREATE_THREADQ允许在宿主q程中创建线E;
b.PROCESS_VM_OPERATIONQ允许对宿主q程中进行VM操作Q?br>
c.PROCESS_VM_WRITEQ允许对宿主q程q行VM写?br>
Q?Q利用Windows API VirtualAllocEx函数在远E线E的VM中分配DLL完整路径宽字W所需的存储空_(d)q利用Windows API WriteProcessMemory函数完整\径写入该存储I间Q?/span>
Q?Q利用Windows API GetProcAddress取得Kernel32模块中LoadLibraryW函数的地址Q这个函数将作ؓ(f)随后启动的q程U程的入口函敎ͼ
Q?Q利用Windows API CreateRemoteThread启动q程U程Q将LoadLibraryW的地址作ؓ(f)q程U程的入口函数地址Q将宿主q程里被分配I间中存储的完整DLL路径作ؓ(f)U程入口函数的参C另其启动指定的DLLQ?/span>
Q?Q清理现场?/span>
]]>typedef ULONG (WINAPI *PFNNtUnmapViewOfSection)( IN HANDLE ProcessHandle,IN PVOID BaseAddress );
2
3BOOL UnmapViewOfModule ( DWORD dwProcessId, LPVOID lpBaseAddr )
4{
5 HMODULE hModule = GetModuleHandle ( L"ntdll.dll" ) ;
6 if ( hModule == NULL )
7 hModule = LoadLibrary ( L"ntdll.dll" ) ;
8
9 PFNNtUnmapViewOfSection pfnNtUnmapViewOfSection = (PFNNtUnmapViewOfSection)GetProcAddress ( hModule, "NtUnmapViewOfSection" ) ;
10
11 HANDLE hProcess = OpenProcess ( PROCESS_ALL_ACCESS, TRUE, dwProcessId ) ;
12 ULONG ret = pfnNtUnmapViewOfSection ( hProcess, lpBaseAddr ) ;
13 CloseHandle ( hProcess ) ;
14 return ret ? FALSE : TRUE;
15}
16
17
]]>
q篇文章是翻译MSDN上一叫《Heap: Pleasures and Pains》的文章?
Microsoft Corporation前言
什么是堆?
堆实现的注意事项
什么是常见的堆性能问题Q?/h2>
量减少堆的使用
struct ObjectA {
// objectA 的数?
}
struct ObjectB {
// objectB 的数?
}
// 同时使用 objectA ?objectB
//
// 使用指针
//
struct ObjectB {
struct ObjectA * pObjA;
// objectB 的数?
}
//
// 使用嵌入
//
struct ObjectB {
struct ObjectA pObjA;
// objectB 的数?
}
//
// 集合 – 在另一对象内?objectA ?objectB
//
struct ObjectX {
struct ObjectA objA;
struct ObjectB objB;
}
其他提高性能的技?/h3>
摘要
2008q???wbr>
q个概述讨论了窗口的一些特性,如窗口类型、状态、大及(qing)位置?wbr>
1.1层叠H口QOverlappedWindowsQ?wbr>
层叠H口是一个具有标题栏、边框和客户区的层H口Q也是说它适合做ؓ(f)应用E序ȝ口。它也可以具有一个系l菜单,最和最大化按钮Q以?qing)滚动条。一个层叠窗口被典型地用于包含所有上q组件的应用E序ȝ口?wbr>
通过在CreateWindowEx中指定WS_OVERLAPPED或者WS_OVERLAPPEDWINDOW样式Q一个应用程序就能创Z个层叠窗口。假如你使用W一个样式,那么创徏的窗口就h一个标题栏和边框;假如你用第二个Q那么窗口就h一个标题栏Q可以调整大的Ҏ(gu)Q系l菜单,以及(qing)最大最化按钮?wbr>
1.2弹出H口QPop-upWindowsQ?wbr>
弹出H口是一个非凡的层叠H口Q它被用于显C在应用E序ȝ口之外的对话框,消息框以?qing)其他?f)时窗口。标题栏对弹出窗口来说是可选的Q除此之外,弹出H口跟具有WS_OVERLAPPED样式的层叠窗口一栗?wbr>
你可以通过在CreateWindowEx中指定WS_POPUP样式来创Z个弹出窗口。假如要使用标题栏,加入WS_CAPTION样式。用WS_POPUPWINDOW样式来创Z个含有边框和pȝ菜单的弹出窗口。WS_CAPTION样式必须与WS_POPUPWINDOW样式一起用才能ɾpȝ菜单可见?wbr>
1.3子窗口(ChildWindowsQ?wbr>
子窗口具有WS_CHILD样式q且它被限制在其父窗口的客户Z。应用程序典型地使用子窗口来把其父窗口的客户区划分成几个功能区域。你可以通过在CreateWindowEx中指定WS_CHILD样式来创建子H口?wbr>
子窗口必d有一个父H口。父H口可以是一个层叠窗口,弹出H口Q或者另外一个子H口。你可以在CreateWindowEx中指定父H口。假如你在CreateWindowEx中指定了WS_CHILD样式但是没有指定父窗口,那么pȝ不会(x)创徏q个子窗口?wbr>
子窗口只h一个客户区而没有其他特性,除非q些Ҏ(gu)被明确的请求。应用程序可以ؓ(f)子窗口添加标题栏Q系l菜单,最化最大化按钮Q边框,以及(qing)滚动条。但是子H口不能h自定义菜单。假如应用程序指定了一个自定义菜单句柄Q那么无论是在它注册q个子窗口类q是创徏q个子窗口时Q这个菜单句柄都被忽略。假如没有指定边框样式,pȝ创Z个无Ҏ(gu)H口。应用程序可以用无Ҏ(gu)的子H口来划分父H口的客户区假如想保持这U划分对用户是不可见的话?wbr>
1.4H口布置QPositioningQ?wbr>
pȝL相对于父H口客户区的左上角来攄子窗口。子H口的Q何部分都不会(x)出现在其父窗口的Ҏ(gu)之外。假如应用程序创Z个比父窗口大的子H口Q或者移动子H口使得一个或者所有子H口出了父H口的边框,那么pȝ?x)裁剪子H口Q即在父H口Ҏ(gu)之外的部分不被显C。对父窗口生媄响的行ؓ(f)同样?x)媄响子H口Q这些行为如下:(x)
MFC本n?font face=Tahoma>CViewcL支持拖放操作的,通过研究CViewcȝ源码Q大体知道它的实现原理是q样的:(x)CViewcM有一?font face=Tahoma>COleDropTargetcȝ对象Q在视图H口初始化时Q调?font face=Tahoma>COleDropTargetcL员函?font face=Tahoma>Register()Q以此在pȝ中注册该视图H口为拖放接收窗口。当q行拖放操作的鼠标指针处于视囄口范围内ӞCOleDropTargecM(x)做出反应Q它?font face=Tahoma>OnDragEnter?font face=Tahoma>OnDragOver?font face=Tahoma>OnDropEx?font face=Tahoma>OnDrop{成员函数被依次调用Q这些函数默认均是调用与其相对应?font face=Tahoma>CViewcL员函?font face=Tahoma>OnDragEnter?font face=Tahoma>OnDragOver?font face=Tahoma>OnDropEx?font face=Tahoma>OnDrop{,E序员只需重蝲q些CViewcL员函敎ͼ卛_Ҏ(gu)动的q程?qing)结果进行控制?/font>
因ؓ(f)COleDropTarget默认只对CView提供支持Q所以如果要让其他的H口支持拖放Q我们必d时对要支持拖攄H口cdCOleDropTargetc进行派生。把Ҏ(gu)放操作具体进行处理的代码装成派生窗口类的成员函敎ͼ然后重蝲COleDropTarget中对应的五个虚函敎ͼ当它接收到拖攑֊作时Q调用窗口派生类的处理函数即可。但q里有一个问题,是我们怎么知道何时调用zcȝ处理函数呢?{案是运?font face=Tahoma>RTTI技术。如?font face=Tahoma>COleDropTargetzcL到的H口指针cdQ就是我们派生的H口c,那么p用它的处理函敎ͼ否则调用基类q行处理?/font>
首先生成一个对话框工程Q添加二个新cR?/font>
W一个类名ؓ(f)CListCtrlExQ父cMؓ(f)CListCtrl。添加完毕后Q在CListCtrlEx的定义头文g中加?font face=Tahoma>DECLARE_DYNAMIC(CListCtrlEx)Q在其实现文件中加入IMPLEMENT_DYNAMIC(CListCtrlEx,CListCtrl)Q这样就?font face=Tahoma>CListCtrlExcL加了RTTIq行期类型识别(Run Time Type InformationQ支持?/font>
W二个类名ؓ(f)COleDropTargetExQ父cMؓ(f)COleDataTarget?/font>
?font face=Tahoma>CListCtrlEx中添?font face=Tahoma>COleDropTargetExcȝ对象Qƈd下列公有虚函数的声明Q?/font>
virtual BOOL Initialize();
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, DROPEFFECT dropList, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
Initialize函数用于注册CListCtrlEx成ؓ(f)拖放接收H口Q?/font>
OnDragOver在拖N标进入窗口时被调用。此函数的返回值决定了后箋的动作的cdQ如果返?font face=Tahoma>DROPEFFECT_MOVEQ则产生一个剪切动作;如果q回DROPEFFECT_COPYQ则产生一个复制动作,如果q回DROPEFFECT_NONEQ则不会(x)产生拖放动作Q因?font face=Tahoma>OnDropEx?font face=Tahoma>OnDrop函数不?x)被调用Q?font face=Tahoma>OnDragLeave函数仍会(x)被调用)?/font>
OnDropEx函数?x)?font face=Tahoma>OnDrop函数之前调用Q如?font face=Tahoma>OnDropEx函数没有Ҏ(gu)攑֊作进行处理,则应用程序框架会(x)接着调用OnDrop函数q行处理。所以必要在派生类中重?font face=Tahoma>OnDropEx函数——即使什么动作都都没有做——否则我们的OnDrop函数不?x)被执行刎ͼ因?f)没有重蝲的话Q将?x)调用基cȝOnDropEx函数Q而基cȝOnDropEx函数Ҏ(gu)放是q行了处理的——尽不是我们所惌的动作。当然你也可以把Ҏ(gu)放进行处理的动作攑֜OnDropEx中——那样就不需要重?font face=Tahoma>OnDrop了?/font>
OnDragLeave函数?x)在鼠标dH口时被调用Q在此可以进行一些简单的清理工作。譬如在OnDragEnter或?font face=Tahoma>OnDragOver函数中,我们改变了光标的形态,那么此时我们应该把光标恢复q来?/font>
q些函数中最重要的是OnDrop函数Q拖攑֊作将在此q行处理Q它的全部源码如下:(x)
BOOL CListCtrlEx::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
UINT nFileCount = 0;
HDROP hDropFiles = NULL;
HGLOBAL hMemData = NULL;
AfxMessageBox("OnDrop");
if(pDataObject->IsDataAvailable(CF_HDROP))
{
hMemData = pDataObject->GetGlobalData(CF_HDROP);
hDropFiles = (HDROP)GlobalLock((HGLOBAL)hMemData); //锁定内存?/font>
if(hDropFiles != NULL)
{
char chTemp[_MAX_PATH+1] = {0};
nFileCount = DragQueryFile(hDropFiles, 0xFFFFFFFF, NULL, 0);
for(UINT nCur=0; nCur<nFileCount; ++nCur) //遍历取得每个文g?/font>
{
ZeroMemory(chTemp, _MAX_PATH+1);
DragQueryFile(hDropFiles, nCur, (LPTSTR)chTemp, _MAX_PATH+1);
AddAllFiles(chTemp);
}
}
GlobalUnlock(hMemData);
return TRUE;
}
else
{
return FALSE;
}
}
在第二个c?font face=Tahoma>COleDropTarget中添加如下对应的函数Q?/font>
virtual DROPEFFECT OnDragEnter(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDragOver(CWnd* pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
virtual DROPEFFECT OnDropEx(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, DROPEFFECT dropList, CPoint point);
virtual BOOL OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
virtual void OnDragLeave(CWnd* pWnd);
它们的动作都差不多:(x)先用RTTI判断H口指针pWnd的类型,如果?font face=Tahoma>CListCtrlExQ则调用CListCtrlEx中对应的处理函数Q否则调用基cȝ处理函数。以OnDropZQ?/font>
BOOL COleDropTargetEx::OnDrop(CWnd* pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point)
{
CListCtrlEx* pListCtrlEx = NULL;
ASSERT_VALID(this);
ASSERT(IsWindow(pWnd->m_hWnd));
if(pWnd->IsKindOf(RUNTIME_CLASS(CListCtrlEx)))
{
pListCtrlEx = (CListCtrlEx*)pWnd;
return pListCtrlEx->OnDrop(pWnd, pDataObject, dropEffect, point);
}
else
{
return COleDropTarget::OnDrop(pWnd, pDataObject, dropEffect, point);
}
}
//倒霉?4K限制Q只能再截断了:(x)Q?br>
xQ我们成功地?font face=Tahoma>CListCtrlExd了文件拖入操作的支持。一个完整的拖放操作Q还包括拖出动作Q所以必要cdd拖出操作Q即Q将列表中的某一Ҏ(gu)者多Ҏ(gu)出成Z个文件。这需要用到另一个类Q?font face=Tahoma>COleDataSource。具体步骤如下:(x)
?font face=Tahoma>CListCtrlEx中加入一?font face=Tahoma>COleDataSource的实例,q映列表框?font face=Tahoma>LVN_BEGINDRAG消息处理函数Q在此我们添加拖出操作的代码?/font>
实现拖出非常单,只需要依ơ调?font face=Tahoma>COleDataSource的三个函数即可:(x)Empty用于清空原先对象中缓存的数据Q?font face=Tahoma>CacheGlobalData用来~存数据以进行拖放操作,最后调?font face=Tahoma>DoDragDrop启动本次拖放操作?/font>
但在调用之前Q必要做一些准备工作。主要的d是创徏一?font face=Tahoma>DROPFILESl构体,q拷贝要拖放的文件名到结构体后的内存中?font face=Tahoma>DROPFILESl构体定义了CF_HDROP剪脓(chung)板格式,紧跟它后面的是一pd被拖放文件的路径名。它的定义如下:(x)
typedef struct _DROPFILES
{
DWORD pFiles; //文g名v始地址
POINT pt; //鼠标放下的位|,坐标?font face=Tahoma>fNC成员指定
BOOL fNC; //?font face=Tahoma>TRUE表示适用屏幕坐标p,否则使用客户坐标p?/font>
BOOL fWide; //文g名字W串是否使用宽字W?/font>
} DROPFILES, FAR* LPDROPFILES;
拖放之前的准备动作的代码如下Q?/font>
uBufferSize = sizeof(DROPFILES) + uBufferSize + 1;
hMemData = GlobalAlloc(GPTR,uBufferSize);
ASSERT(hMemData != NULL);
lpDropFiles = (LPDROPFILES)GlobalLock(hMemData); //锁定?font face=Tahoma>,q设|相x?/font>
ASSERT(lpDropFiles != NULL);
lpDropFiles->pFiles = sizeof(DROPFILES);
#ifdef _UNICODE
lpDropFiles->fWide = TRUE;
#else
lpDropFiles->fWide = FALSE;
#endif
//把选中的所有文件名依次复制?font face=Tahoma>DROPFILESl构体后?font face=Tahoma>(全局内存?font face=Tahoma>)
pItemPos = strSelectedList.GetHeadPosition();
pszStart = (char*)((LPBYTE)lpDropFiles + sizeof(DROPFILES));
while(pItemPos != NULL)
{
lstrcpy(pszStart, (LPCTSTR)strSelectedList.GetNext(pItemPos));
pszStart = strchr(pszStart,'\0') + 1; //下次的v始位|是上一ơ结?font face=Tahoma>+1
}
准备完毕之后可以进行拖放了Q拖攑֊作有DoDragDrop函数触发Q其原型如下Q?/font>
DROPEFFECT DoDragDrop(
DWORD dwEffects = DROPEFFECT_COPY|DROPEFFECT_MOVE|DROPEFFECT_LINK, LPCRECT lpRectStartDrag = NULL,
COleDropSource* pDropSource = NULL
);
q里Q?font face=Tahoma>dwEffects指定了允许施加于?font face=Tahoma>COleDataSource实例之上的动作集Q剪切、复制或无动作?/font>
lpRectStartDrag指示拖放操作真正开始的矩ŞQ如果鼠标没有移矩ŞQ则拖放操作视作攑ּ处理。如果本成员设ؓ(f)NULLQ则该v始矩形将Z个像素大?/font>
pDropSource表明拖放所使用?font face=Tahoma>COleDataSource对象?/font>
而该函数的返回|则表明本ơ拖放操作所实际产生的效果,至于具体产生何种效果Q则ql决定。譬如在拖放时按?font face=Tahoma>Shift键,生剪切效果;按住Ctrl键,生复制效果,{等?/font>
拖放的代码如下:(x)
m_oleDataSource.Empty();
m_oleDataSource.CacheGlobalData(CF_HDROP, hMemData);
DropResult = m_oleDataSource.DoDragDrop(DROPEFFECT_MOVE|DROPEFFECT_COPY);
最后一点要注意的是Q在Windows NT 4.0以上的系l中Q即使实际生的?font face=Tahoma>DROPEFFECT_MOVE动作Q?font face=Tahoma>DoDragDrop函数也只q回DROPEFFECT_NONE。生这个问题的原因在于Q?font face=Tahoma>Windows NT 4.0?font face=Tahoma>Shell?x)直接移动文件本w来对移动操作进行优化。返回?font face=Tahoma>DROPEFFECT_MOVE最初的含义Q就是通知执行拖放操作的应用程序去删除原位|上的文件。但是因?font face=Tahoma>Shell已经替应用程序完成了q个Q删除)动作Q所以,函数q回DROPEFFECT_NONE。要想知道文件是否真的被Ud了也很简单,只要在函数返回之后检查一下原位置上的文g是否存在可以了?/font>
Windows 9xpd的操作系l也?x)对Udq行同样的优化动作,但是它不?x)返?font face=Tahoma>DROPEFFECT_NONE来代?font face=Tahoma>DROPEFFECT_MOVE。详l的解释参见MS知识?font face=Tahoma>Q182219?/font>
cL员方法是一个比较特D的函数Q它在编译时?x)被转化成普通函敎ͼ比如有TMyClassc?
class TMyClass{
void Func();
};
q个TMyClass::Func最l会(x)转化?void Func(TMyClass *this); 也就是说在原W一个参数前插入指向对象本n的this指针?/p>
我们可以利用q个Ҏ(gu)写一个非静态类成员Ҏ(gu)来直接作为线E回调函敎ͼ先看_beginthread函数的定?
unsigned long _RTLENTRY _EXPFUNC _beginthread (void (_USERENTRY *__start)(void *),unsigned __stksize, void *__arg);
其中的第一个参数就是作为线E执行主体的回调函数。它的原型是:void Func(void *)Q这个void*参数是作定义数据传入的。对比一下上面所说的TMyClass::Func的最lŞ式,它正好可以符合这里的要求?/p>
现在做个实验:
#include <stdio.h>
#include <process.h>
class TMyClass{
int m_nCount;
int m_nId;
public:
TMyClass(int nId,int nCount)
:m_nId(nId),m_nCount(nCount)
{
}
void _USERENTRY ThreadProc() // cL员方?br> {
for(int i=0; i<m_nCount; i++) // Ҏ(gu)m_nCount成员打印一排数?br> {
printf("Class%d : %d\n",m_nId,i);
}
}
};
int main(int argc, char* argv[])
{
union { // 联合c,用于转换cL员方法指针到普通函数指针(试过~译器不允许在这两种函数之间强制转换Q,不知道有没有更好的方法?br> void (_USERENTRY *ThreadProc)(void *);
void (_USERENTRY TMyClass::*MemberProc)();
} Proc; // 管联合里的两种函数cd现在看v来有很大不同Q但它们的最lŞ式是相同的?/p>
TMyClass MyClass1(1,10),MyClass2(2,5); // 产生两个TMyClass对象
Proc.MemberProc = &TMyClass::ThreadProc; // 转换QProc.ThreadProc是对应的普通函数指针了
_beginthread(Proc.ThreadProc,4096,&MyClass1); // 开始线E,q里的Proc.ThreadProc实际上是TMyClass::ThreadProc, 它要的this指针是我们给?amp;MyClass1?br> _beginthread(Proc.ThreadProc,4096,&MyClass2);
system("pause");
return 0;
}
q行Q神奇吧Q?-)
其实不止U程回调函数Q其实只要是形如Func(void*,...)的回调函数都可以用这U方法直接用类成员Ҏ(gu)?前提是第一个void*是自定义数据Q也是说它不能有其它功??br>
转自:http://blog.csdn.net/waiting4you/archive/2007/12/29/2000796.aspx
uMsg | wParam | lParam | 说明 |
SBM_ENABLE_ARROWS | ESB_DISABLE_BOTH | 0 | 止双向滚动剪头 |
ESB_DISABLE_DOWN | 0 | 止向下滚动剪头 | |
ESB_DISABLE_LTUP | 0 | 止向上和向左滚动剪?/td> | |
ESB_DISABLE_LEFT | 0 | 止向左滚动剪头 | |
ESB_DISABLE_RTDN | 0 | 止向下和向x动剪?/td> | |
ESB_DISABLE_UP | 0 | 止向上滚动剪头 | |
ESB_ENABLE_BOTH | 0 | 允许双向滚动剪头(撤消各种止) | |
SBM_SETPOS | 指定位置 | TRUE | 讄滚动框位|,ql控?/td> |
FALSE | 讄滚动框位|,不重l控?/td> | ||
SBM_SETRANGE | 最?/td> | 最大?/td> | 讄滚动框位|的变化范围 |
SBM_SETRANGEREDRAW | 最?/td> | 最大?/td> | 讄滚动框位|的变化范围Qƈ重绘控g |
SBM_SETSCROLLINFO | TRUE或FALSE | SCROLLINFOl构指针 | 本消息通过一个SCROLLINFOl构来同时指定控件的多种参数Q具体指定哪些参数由l构中的fMask成员定。wParam指定是否重绘控gQ详?#8220;SCROLLINFOl构” |
消息代码 | 动作 | 响应 |
SB_LINEUP SB_LINELEFT |
用户点击了向??剪头 | 滚动框位|减一Q客L(fng)口向??滚动一行?br>注:(x)q两个代码数值相{,因此可以L(fng)Q下同?/td> |
SB_LINEDOWN SB_LINERIGHT |
用户点击了向??剪头 | 滚动框位|加一Q客L(fng)口向??滚动一行?/td> |
SB_PAGEUP SB_PAGELEFT |
用户点击了滚动框以上(?剪杆 | 滚动框位|减M个大单位Q客L(fng)口向??滚动一c(din)? |
SB_PAGEDOWN SB_PAGERIGHT |
用户点击了滚动框以下(?剪杆 | 滚动框位|加上一个大单位Q客L(fng)口向??滚动一c(din)? |
SB_THUMBPOSITION | 用户拖动q放滚动框到指定位|?/td> | 讑֮滚动框到指定位置。客L(fng)口滚动到指定位置?/td> |
SB_THUMBTRACK | 用户正在拖动滚动?/td> | 讑֮滚动框到指定位置。客L(fng)口滚动到指定位置。如果应用程序需要快速浏览窗口,可以响应本消息重l窗口,如果不需要快速浏览,可以{待收到SB_THUMBPOSITION消息旉l窗口? |
SB_ENDSCROLL | 用户释放按下剪头或剪杆的鼠标 | 无须做Q何响? |
uMsg | wParam | lParam | 说明 |
SBM_GETPOS | 0 | 0 | q回滚动框当前位|?/td> |
SBM_GETRANGE | 最值地址指针 | 最大值地址指针 | 在指定地址中填?2位的滚动框位|的变化范围 |
SBM_GETSCROLLINFO | 0 | SCROLLINFOl构指针 | 在一个SCROLLINFOl构中返回控件的多种参数Q必M先设定结构的fMask成员来确定具体要取得哪些参数。详?#8220;SCROLLINFOl构” |
SCROLLINFO STRUCT cbSize DWORD ? fMask DWORD ? nMin DWORD ? nMax DWORD ? nPage DWORD ? nPos DWORD ? nTrackPos DWORD ? SCROLLINFO ENDS |
GetDc?/span>敎ͼ(x)用于获得hWnd参数所指定H口的客户区域的一个设备环境?/span>
所获得的设备环境可以是通用、类或者私有类型,具体由指定窗口的c风格决定。对于通用讑֤环境Q?/span>GetDc函数每次获取一个设备环境时都会(x)用默认属性对它进行初始化。该函数获得的类和私有设备环境会(x)与它们最后一ơ的讄保持一致。当讑֤环境不再需要时Q应该调?/span>ReleaseDC函数其释放?/span>
GetWindowDC函数Q返?/span>hWnd参数所指定的窗口的讑֤环境?/span>
获得的设备环境覆盖了整个H口Q包括非客户区)Q例如标题栏、菜单、滚动条Q以?qing)边框。这使得E序能够在非客户区域实现自定义图形,例如自定义标题或者边框。当不再需要该讑֤环境Ӟ需要调?/span>ReleaseDC函数释放讑֤环境。注意,该函数只获得通用讑֤环境Q该讑֤环境的Q何属性改变都不会(x)反映到窗口的U有或者类讑֤环境中(如果H口有的话)
----------摘自?/span>Delphi Win32核心API参考?/span>
=============================================================