剪貼簿作業

剪下、複製或貼上數據時,視窗應該使用剪貼簿。 視窗會將數據放在剪貼簿上以進行剪下和複製作業,並從剪貼簿擷取數據以進行貼上作業。 下列各節說明這些作業和相關問題。

若要將數據放在剪貼簿上或從剪貼簿擷取數據,視窗必須先使用 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
none
CF_OWNERDISPLAY
當剪貼簿清空 CF_OWNERDISPLAY 物件時,應用程式本身必須釋放記憶體物件。