Операции с буфером обмена

Окно должно использовать буфер обмена при вырезании, копировании или вставке данных. Окно помещает данные в буфер обмена для операций вырезания и копирования и извлекает данные из буфера обмена для операций вставки. В следующих разделах описаны эти операции и связанные проблемы.

Чтобы поместить данные в буфер обмена или получить их из буфера обмена, окно должно сначала открыть буфер обмена с помощью функции OpenClipboard. Одновременно может открыться только одно окно буфера обмена. Чтобы узнать, какое окно открыто в буфере обмена, вызовите функцию GetOpenClipboardWindow . По завершении окно должно закрыть буфер обмена, вызвав функцию CloseClipboard.

В этом разделе рассматриваются следующие разделы.

Операции вырезания и копирования

Чтобы разместить сведения в буфере обмена, окно сначала очищает любое предыдущее содержимое буфера обмена с помощью функции EmptyClipboard. Эта функция отправляет сообщение WM_DESTROYCпакет интерфейса пользователя BOARD предыдущему владельцу буфера обмена, освобождает ресурсы, связанные с данными в буфере обмена, и назначает владение буфером обмена в окне с открытым буфером обмена. Чтобы узнать, какое окно владеет буфером обмена, вызовите функцию GetClipboardOwner .

После очистки буфера обмена окно помещает данные в буфер обмена в максимально возможном количестве форматов буфера обмена, упорядоченных из наиболее описательного формата буфера обмена до наименьшего описательного. Для каждого формата окно вызывает функцию SetClipboardData , указав идентификатор формата и глобальный дескриптор памяти. Дескриптор памяти может иметь значение NULL, указывая, что окно отрисовывает данные по запросу. Дополнительные сведения см. в разделе "Отложенная отрисовка".

Операции вставки

Чтобы получить сведения о вставке из буфера обмена, окно сначала определяет формат буфера обмена для извлечения. Как правило, окно перечисляет доступные форматы буфера обмена с помощью функции EnumClipboardFormats и использует первый формат, который он распознает. Этот метод выбирает лучший доступный формат в соответствии с набором приоритетов при размещении данных в буфер обмена.

Кроме того, окно может использовать функцию GetPriorityClipboardFormat. Эта функция определяет лучший доступный формат буфера обмена в соответствии с указанным приоритетом. Окно, которое распознает только один формат буфера обмена, может просто определить, доступен ли этот формат с помощью функции IsClipboardFormatAvailable.

После определения используемого формата буфера обмена окно вызывает функцию GetClipboardData. Эта функция возвращает дескриптор в глобальный объект памяти, содержащий данные в указанном формате. Окно может кратко заблокировать объект памяти, чтобы изучить или скопировать данные. Однако окно не должно освободить объект или оставить его заблокированным в течение длительного периода времени.

Владение буфером обмена

Владелец буфера обмена — это окно, связанное с информацией в буфере обмена. Окно становится владельцем буфера обмена, когда он помещает данные в буфер обмена, в частности, при вызове функции EmptyClipboard . Окно остается владельцем буфера обмена, пока не будет закрыто или другое окно очищает буфер обмена.

При очистке буфера обмена владелец буфера обмена получает сообщение WM_DESTROYCпакет интерфейса пользователя BOARD. Ниже приведены некоторые причины, по которым окно может обработать это сообщение:

  • Окно отложено отрисовки одного или нескольких форматов буфера обмена. В ответ на сообщение WM_DESTROYCпакет интерфейса пользователя BOARD окно может освободить ресурсы, выделенные для отображения данных по запросу. Дополнительные сведения о отрисовке данных см. в разделе "Отложенная отрисовка".
  • Окно помещло данные в буфер обмена в формате закрытого буфера обмена. Данные для форматов закрытого буфера обмена не освобождаются системой при очистке буфера обмена. Поэтому владелец буфера обмена должен освободить данные при получении сообщения WM_DESTROYCпакет интерфейса пользователя BOARD. Дополнительные сведения о форматах закрытого буфера обмена см. в разделе "Форматы буфера обмена".
  • Окно помещло данные в буфер обмена с помощью формата буфера обмена CF_OWNERDISPLAY . В ответ на сообщение WM_DESTROYCпакет интерфейса пользователя BOARD окно может освободить ресурсы, которые использовались для отображения сведений в окне просмотра буфера обмена. Дополнительные сведения об этом альтернативном формате см. в разделе "Формат отображения владельца".

Отложенная отрисовка

При размещении формата буфера обмена в буфере обмена окно может отложить отрисовку данных в этом формате до тех пор, пока данные не потребуются. Для этого приложение может указать ЗНАЧЕНИЕ NULL для параметра hData функции SetClipboardData. Это полезно, если приложение поддерживает несколько форматов буфера обмена, некоторые или все из которых используются для отрисовки. Передав дескриптор NULL , окно отрисовывает сложные форматы буфера обмена только в том случае, если они нужны.

Если окно задерживает отображение формата буфера обмена, оно должно быть готово к отображению формата по запросу до тех пор, пока он является владельцем буфера обмена. Система отправляет владельцу буфера обмена сообщение WM_RENDERFORMAT при получении запроса для определенного формата, который не был отрисован. При получении этого сообщения окно должно вызвать функцию SetClipboardData , чтобы поместить глобальный дескриптор памяти в буфер обмена в запрошенном формате.

Приложение не должно открывать буфер обмена перед вызовом SetClipboardData в ответ на сообщение WM_RENDERFORMAT. Открытие буфера обмена не требуется, и любая попытка сделать это завершится ошибкой, так как буфер обмена в настоящее время открыт приложением, которое запрашивает отрисовку формата.

Если владелец буфера обмена будет уничтожен и отложен отрисовка некоторых или всех форматов буфера обмена, он получает сообщение WM_RENDERALLFORMATS. После получения этого сообщения окно должно открыть буфер обмена, проверка, что он по-прежнему является владельцем буфера обмена с функцией GetClipboardOwner, а затем помещает допустимые дескрипторы памяти в буфер обмена для всех форматов буфера обмена, которые он предоставляет. Это гарантирует, что эти форматы остаются доступными после уничтожения владельца буфера обмена.

В отличие от WM_RENDERFORMAT, приложение, отвечающее на WM_RENDERALLFORMATS, должно открыть буфер обмена перед вызовом SetClipboardData для размещения всех глобальных дескрипторов памяти в буфер обмена.

Любые форматы буфера обмена, которые не отображаются в ответ на сообщение WM_RENDERALLFORMATS перестают быть доступными другим приложениям и больше не перечисляются функциями буфера обмена.

Руководство по отложенной отрисовке

Отложенная отрисовка — это функция производительности, которая позволяет приложению избежать выполнения работы для отрисовки данных буфера обмена в формате, который никогда не может запрашиваться. Однако использование отложенной отрисовки включает в себя следующие компромиссы, которые следует учитывать:

  • Использование отложенной отрисовки повышает сложность приложения, требуя обработки двух сообщений окна отрисовки, как описано выше.
  • Использование отложенной отрисовки означает, что приложение теряет возможность реагировать на пользовательский интерфейс, если отрисовка данных занимает достаточно времени, чтобы оно заметно для пользователя. При отложенной отрисовке, если данные в конечном итоге необходимы, окно должно отобразить данные при обработке сообщения окна отрисовки, как описано выше. В результате, если данные очень трудоемки для отрисовки, приложение может стать явно неответственным (зависать) во время отрисовки, так как другие сообщения окна не могут обрабатываться во время обработки сообщения окна отрисовки. Приложение, которое не использует отложенную отрисовку, может вместо этого отрисовывать данные в фоновом потоке, чтобы сохранить ответ пользовательского интерфейса во время отрисовки, возможно, предоставляя параметры хода выполнения или отмены, которые недоступны при использовании отложенной отрисовки.
  • При использовании отложенной отрисовки добавьте небольшое количество накладных расходов, если данные в конечном итоге необходимы. При использовании отрисовки задержки окно изначально вызывает функцию SetClipboardData с дескриптором NULL, а если данные позже необходимы, окно должно отвечать на сообщение окна и вызывать функцию SetClipboardData во второй раз с дескриптором отрисованных данных, как описано выше. В результате, если данные в конечном итоге необходимы, использование отложенной отрисовки добавляет затраты на обработку сообщения окна и вызов функции SetClipboardData во второй раз. Эта стоимость небольшая, но не нулевая. Если приложение поддерживает только один формат буфера обмена и если данные всегда запрашиваются в конечном итоге, использование отложенной отрисовки просто добавляет это небольшое количество затрат (стоимость зависит от оборудования; оценка составляет от 10 до 100 микросекунд). Однако если данные малы, затраты на использование отложенной отрисовки могут превышать затраты на отрисовку данных, что может победить цель использования отложенной отрисовки для повышения производительности. (При тестировании данных, которые уже в последней форме, затраты на использование отложенной отрисовки последовательно превысили затраты на копирование данных в буфер обмена, если данные были 100 КиБ или меньше. Это тестирование не включает затраты на отрисовку данных, просто чтобы скопировать его после отрисовки.)
  • Отложенная отрисовка — это чистое преимущество производительности, если она экономит больше времени, чем добавляется в затраты. Чтобы определить затраты на задержку отрисовки, измерение лучше всего, но 10 до 100 микросекунд является оценкой. Чтобы вычислить экономию отложенной отрисовки для каждого формата буфера обмена, измеряйте затраты на отрисовку данных в этом формате и определите, насколько часто этот формат в конечном итоге запрашивается (на основе сообщений окна, описанных выше). Умножьте стоимость отрисовки данных на процент времени, когда данные в конечном итоге не запрашиваются (до очистки буфера обмена или изменения его содержимого), чтобы определить экономию отложенной отрисовки для каждого формата буфера обмена. Отложенная отрисовка — это чистое преимущество производительности, если экономия превышает затраты.
  • В качестве конкретного руководства для приложений, которые поддерживают только один формат буфера обмена, например текст, где данные не являются значительно дорогостоящими для отрисовки, рассмотрите возможность размещения данных непосредственно в буфере обмена, если размер данных составляет 4 КиБ или меньше.

Память и буфер обмена

Объект памяти, который должен быть помещен в буфер обмена, должен быть выделен с помощью функции GlobalAlloc с флагом GMEM_MOVEABLE.

После того как объект памяти помещается в буфер обмена, владение этим дескриптором памяти передается в систему. Если буфер обмена очищается и объект памяти имеет один из следующих форматов буфера обмена, система освобождает объект памяти, вызывая указанную функцию:

Функция для свободного объекта Формат буфера обмена
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 , само приложение должно освободить объект памяти.