剪贴板作
在剪切、复制或粘贴数据时,窗口应使用剪贴板。 窗口在剪贴板上放置用于剪切和复制作的数据,并从剪贴板中检索用于粘贴作的数据。 以下部分介绍了这些作和相关问题。
若要将数据放在剪贴板上或从剪贴板中检索数据,窗口必须首先使用 OpenClipboard 函数打开剪贴板。 一次只能打开一个窗口。 若要找出打开剪贴板的窗口,请调用 GetOpenClipboardWindow 函数。 完成后,窗口必须通过调用 CloseClipboard 函数关闭剪贴板。
本节将讨论以下主题。
剪切和复制作
若要在剪贴板上放置信息,窗口首先使用 EmptyClipboard 函数清除以前的任何剪贴板内容。 此函数将 WM_DESTROYCLIPBOARD 消息发送到上一个剪贴板所有者,释放与剪贴板上数据关联的资源,并将剪贴板所有权分配给打开剪贴板的窗口。 若要找出哪个窗口拥有剪贴板,请调用 GetClipboardOwner 函数。
清空剪贴板后,窗口会将剪贴板上的数据置于尽可能多的剪贴板格式中,从最描述性的剪贴板格式排序为最不描述性。 对于每个格式,该窗口调用 SetClipboardData 函数,并指定格式标识符和全局内存句柄。 内存句柄可以为 NULL,指示窗口在请求时呈现数据。 有关详细信息,请参阅 延迟呈现。
粘贴作
若要从剪贴板中检索粘贴信息,窗口首先确定要检索的剪贴板格式。 通常,窗口通过使用 EnumClipboardFormats 函数枚举可用的剪贴板格式,并使用它识别的第一种格式。 此方法根据在剪贴板上放置数据时设置的优先级集选择最佳可用格式。
或者,窗口可以使用 GetPriorityClipboardFormat 函数。 此函数根据指定的优先级标识最佳可用剪贴板格式。 仅识别一个剪贴板格式的窗口只需使用 IsClipboardFormatAvailable 函数来确定该格式是否可用。
确定要使用的剪贴板格式后,窗口将调用 GetClipboardData 函数。 此函数返回包含指定格式数据的全局内存对象的句柄。 窗口可以短暂锁定内存对象,以便检查或复制数据。 但是,窗口不应释放对象或将其锁定很长一段时间。
剪贴板所有权
剪贴板所有者 是与剪贴板上的信息关联的窗口。 窗口在剪贴板上放置数据时成为剪贴板所有者,特别是当窗口调用 EmptyClipboard 函数时。 窗口保持剪贴板所有者,直到它关闭或另一个窗口清空剪贴板。
当剪贴板清空时,剪贴板所有者会收到 WM_DESTROYCLIPBOARD 消息。 以下是窗口可能处理此消息的一些原因:
- 窗口延迟呈现一个或多个剪贴板格式。 为了响应 WM_DESTROYCLIPBOARD 消息,窗口可能会释放它分配的资源,以便根据请求呈现数据。 有关数据呈现的详细信息,请参阅 延迟呈现。
- 窗口以专用剪贴板格式将数据放置在剪贴板上。 当剪贴板被清空时,系统不会释放专用剪贴板格式的数据。 因此,剪贴板所有者在收到 WM_DESTROYCLIPBOARD 消息时应释放数据。 有关专用剪贴板格式的详细信息,请参阅 剪贴板格式。
- 使用 CF_OWNERDISPLAY 剪贴板格式将数据放置在剪贴板上。 为了响应 WM_DESTROYCLIPBOARD 消息,窗口可能会释放它用于在剪贴板查看器窗口中显示信息的资源。 有关此替代格式的详细信息,请参阅 所有者显示格式。
延迟呈现
在剪贴板上放置剪贴板格式时,窗口可能会延迟以该格式呈现数据,直到需要数据。 为此,应用程序可以为 setClipboardData函数的 hData参数指定 NULL。 如果应用程序支持多种剪贴板格式(部分或全部)呈现,则这非常有用。 通过传递 NULL 句柄,窗口仅在需要时才呈现复杂的剪贴板格式。
如果窗口延迟呈现剪贴板格式,则必须准备好在请求时呈现格式,只要它是剪贴板所有者。 当收到尚未呈现的特定格式的请求时,系统会向剪贴板所有者发送 WM_RENDERFORMAT 消息。 收到此消息后,该窗口应调用 SetClipboardData 函数,以请求的格式将全局内存句柄放置在剪贴板上。
在调用 setClipboardData 以响应 WM_RENDERFORMAT 消息之前,应用程序不得打开剪贴板。 打开剪贴板并不是必需的,任何尝试这样做都会失败,因为应用程序当前正在打开剪贴板,应用程序请求呈现格式。
如果剪贴板所有者即将销毁并延迟呈现部分或所有剪贴板格式,则会收到 WM_RENDERALLFORMATS 消息。 收到此消息后,窗口应打开剪贴板,检查它是否仍然是具有 GetClipboardOwner 函数的剪贴板所有者,然后将有效内存句柄放在剪贴板上,以获取它提供的所有剪贴板格式。 这可确保这些格式在剪贴板所有者销毁后保持可用。
与 WM_RENDERFORMAT不同,响应 WM_RENDERALLFORMATS 的应用程序应在调用 SetClipboardData 之前打开剪贴板, 将任何全局内存句柄放在剪贴板上。
响应 WM_RENDERALLFORMATS 消息时未呈现的任何剪贴板格式不再可供其他应用程序使用,剪贴板函数不再枚举。
延迟呈现指南
延迟呈现是一项性能功能,使应用程序能够避免以从未请求的格式呈现剪贴板数据。 但是,使用延迟呈现涉及应考虑的以下权衡:
- 使用延迟呈现会增加应用程序的复杂性,因此需要它处理两条呈现窗口消息,如上所述。
- 使用延迟呈现意味着,如果呈现数据需要足够长的时间,应用程序将失去保持 UI 响应的选项,使其对用户明显。 由于呈现延迟,如果最终需要数据,窗口必须在处理呈现窗口消息时呈现数据,如上所述。 因此,如果呈现数据非常耗时,则呈现时应用程序可能会明显无响应(挂起),因为处理呈现窗口消息时无法处理其他窗口消息。 不使用延迟呈现的应用程序可能会改为选择在后台线程上呈现数据,以便在呈现发生时保留 UI 响应,也许提供进度或取消选项,在使用延迟呈现时不可用。
- 如果最终需要数据,则使用延迟呈现会增加少量开销。 使用延迟呈现时,窗口最初使用 NULL 句柄调用 SetClipboardData 函数,如果稍后需要数据,则该窗口必须响应窗口消息并再次调用 SetClipboardData 函数,并再次使用呈现的数据句柄来调用函数,如上所述。 因此,如果最终需要数据,则使用延迟呈现会增加处理窗口消息并再次调用 SetClipboardData 函数的成本。 此成本很小,但不是零。 如果应用程序仅支持单个剪贴板格式,并且如果始终请求数据,则使用延迟呈现只会增加少量开销(成本因硬件而异;估计介于 10 到 100 微秒之间)。 但是,如果数据较小,使用延迟呈现的开销可能会超过呈现数据的成本,这可能会使使用延迟呈现来改善性能的目的。 (在测试中,对于已采用最终形式的数据,使用延迟呈现的开销一直超过将数据复制到剪贴板的成本(如果数据为 100 KiB 或更少)。此测试不包括呈现数据的成本,只需在呈现数据后复制它即可。
- 如果延迟呈现比增加开销多,则延迟呈现是一个净性能优势。 若要确定延迟呈现的开销,最好测量,但估计为 10 到 100 微秒。 若要计算每个剪贴板格式的延迟呈现的节省,请测量以该格式呈现数据的成本,并确定最终请求该格式的频率(基于上述窗口消息)。 将呈现数据的成本乘以最终请求数据的时间百分比(在剪贴板被清空或其内容更改之前)来确定每个剪贴板格式延迟呈现的节省。 如果节省超过开销成本,延迟呈现是净性能优势。
- 作为具体准则,对于仅支持单个剪贴板格式的应用程序(例如文本),其中数据不显著昂贵,因此,如果数据大小为 4 KiB 或更少,请考虑将数据直接放置在剪贴板上。
内存和剪贴板
应使用具有 GMEM_MOVEABLE 标志的 GlobalAlloc 函数来分配要放置在剪贴板上的内存对象。
将内存对象放置在剪贴板上后,该内存句柄的所有权将传输到系统。 当剪贴板清空并且内存对象具有以下剪贴板格式之一时,系统会通过调用指定的函数释放内存对象:
用于释放对象的函数 | 剪贴板格式 |
---|---|
DeleteMetaFile |
CF_DSPENHMETAFILE CF_DSPMETAFILEPICT CF_ENHMETAFILE CF_METAFILEPICT |
DeleteObject |
CF_BITMAP CF_DSPBITMAP CF_PALETTE |
GlobalFree |
CF_DIB CF_DIBV5 CF_DSPTEXT CF_OEMTEXT CF_TEXT CF_UNICODETEXT |
没有 |
CF_OWNERDISPLAY 当剪贴板清空 CF_OWNERDISPLAY 对象时,应用程序本身必须释放内存对象。 |