Поделиться через


Обработка сценариев передачи данных в среде оболочки

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

Примечание.

Хотя каждый из этих сценариев обсуждает определенную операцию передачи данных, многие из них применяются к различным связанным сценариям. Например, основное различие между большинством буферов обмена и передачей перетаскивания заключается в том, как объект данных поступает в целевой объект. После того как целевой объект имеет указатель на интерфейс IDataObject объекта данных, процедуры извлечения информации в значительной степени одинаковы для обоих типов передачи данных. Однако некоторые сценарии ограничены определенным типом операции. Дополнительные сведения см. в отдельном сценарии.

 

Общие рекомендации

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

Для источников данных:

  • Форматы буфера обмена в оболочке, за исключением CF_HDROP, не определены. Каждый формат, который требуется использовать, должен быть зарегистрирован путем вызова RegisterClipboardFormat.
  • Форматы объектов данных предоставляются в порядке предпочтения из источника. Перечислите объект данных и выберите первый, который можно использовать.
  • Включите столько форматов, сколько можно поддерживать. Как правило, вы не знаете, где будет удален объект данных. Эта практика повышает вероятность того, что объект данных будет содержать формат, который может принять целевой объект удаления.
  • Существующие файлы должны предлагаться в формате CF_HDROP .
  • Предоставляйте данные, подобные файлам, с форматами CFSTR_FILECONTENTS и CFSTR_FILEDESCRIPTOR. Такой подход позволяет целевому объекту создавать файл из объекта данных без необходимости знать ничего о базовом хранилище данных. Обычно данные должны отображаться в виде интерфейса IStream . Этот механизм передачи данных является более гибким, чем глобальный объект памяти и использует гораздо меньше памяти.
  • Источники должны предлагать формат CFSTR_SHELLIDLIST при перетаскивании элементов Shell. Объекты данных для элементов можно получить с помощью методов IShellFolder::GetUIObjectOf или IShellItem::BindToHandler. Источники данных могут создать стандартную реализацию объекта данных, которая поддерживает формат CFSTR_SHELLIDLIST с помощью SHCreateDataObject.
  • Целевые объекты, которые хотят анализировать элементы, что перетаскиваются с использованием модели программирования элементов оболочки, могут преобразовать IDataObject в IShellItemArray с помощью SHCreateShellItemArrayFromDataObject.
  • Используйте стандартные курсоры обратной связи.
  • Поддержка перетаскивания влево и вправо.
  • Используйте сам объект данных из внедренного объекта. Этот подход позволяет приложению получать любые дополнительные форматы, которые объект данных должен предложить и избежать создания дополнительного слоя хранения. Например, внедренный объект с сервера A перетаскивается из сервера или контейнера B и удаляется на контейнер C. C должен создать внедренный объект сервера A, а не внедренный объект сервера B, содержащий внедренный объект сервера A.
  • Помните, что оболочка может использовать оптимизированные операции перемещения или операции удаления при вставке при перемещении файлов. Приложение должно иметь возможность распознавать эти операции и реагировать соответствующим образом.

Для целевых объектов данных:

  • Форматы буфера обмена оболочки , за исключением CF_HDROP, не определены. Каждый формат, который требуется использовать, должен быть зарегистрирован путем вызова RegisterClipboardFormat.
  • Реализуйте и зарегистрируйте целевой объект удаления OLE. По возможности избегайте использования целевых объектов Windows 3.1 или сообщения WM_DROPFILES.
  • Форматы, содержащиеся в объекте данных, зависят от того, откуда поступает объект. Так как обычно заранее не известно, откуда поступает объект данных, не предполагается, что будет присутствовать определенный формат. Объект данных должен перечислять форматы в порядке качества, начиная с лучшего. Таким образом, чтобы получить лучший доступный формат, приложения обычно перечисляют доступные форматы и используют первый формат в перечислении, который они могут поддерживать.
  • Поддержка перетаскивания правой кнопкой мыши. Контекстное меню перетаскивания можно настроить, создав обработчик перетаскивания.
  • Если приложение примет существующие файлы, он должен иметь возможность обрабатывать CF_HDROP формат.
  • Как правило, приложения, принимаюющие файлы, также должны обрабатывать форматы CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Хотя файлы из файловой системы имеют формат CF_HDROP, файлы от поставщиков, таких как расширения пространства имен, обычно используют CFSTR_FILECONTENTS/CFSTR_FILEDESCRIPTOR. Примерами являются папки Windows CE, папки ПРОТОКОЛА FTP, веб-папки и папки CAB. Источник обычно реализует интерфейс IStream для представления данных из хранилища в виде файла.
  • Помните, что оболочка может использовать оптимизированные операции перемещения или удаление при вставке при перемещении файлов. Приложение должно иметь возможность распознавать эти операции и реагировать соответствующим образом.

Копирование имен файлов из буфера обмена в приложение

Сценарий. Пользователь выбирает один или несколько файлов в проводнике Windows и копирует их в буфер обмена. Приложение извлекает имена файлов и вставляет их в документ.

Этот сценарий можно использовать, например, чтобы разрешить пользователю создавать HTML-ссылку, вырезав и вставив файл в приложение. Затем приложение может извлечь имя файла из объекта данных и обработать его, чтобы создать тег привязки.

Когда пользователь выбирает файл в проводнике Windows и копирует его в буфер обмена, оболочка создает объект данных. Затем он вызывает OleSetClipboard, чтобы поместить указатель на интерфейс объекта данных IDataObject в буфер обмена.

Когда пользователь выбирает команду "Вставить" в меню или панели инструментов приложения:

  1. Вызовите OleGetClipboard, чтобы получить интерфейс IDataObject данного объекта.
  2. Вызовите IDataObject::EnumFormatEtc , чтобы запросить объект перечислителя.
  3. Используйте интерфейс IEnumFORMATETC объекта перечислителя для перечисления форматов, содержащихся в объекте данных.

Примечание.

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

 

Извлечение имен файлов из объекта данных

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

Самый простой способ получения имен файлов из объекта данных — это формат CF_HDROP :

  1. Вызовите IDataObject::GetData. Установите для члена cfFormat структуры FORMATETC значение CF_HDROP, а для члена tymed значение TYMED_HGLOBAL. Для элемента dwAspect обычно задано значение DVASPECT_CONTENT. Однако если вам нужно иметь путь к файлу в коротком формате (8.3), задайте для dwAspect значение DVASPECT_SHORT.

    Когда IDataObject::GetData завершает выполнение, член hGlobal структуры STGMEDIUM указывает на глобальный объект памяти, содержащий данные.

  2. Создайте переменную HDROP и установите её на член hGlobal структуры STGMEDIUM. Переменная HDROP теперь является дескриптором структуры DROPFILES, за которой следует строка с двойным завершающим символом NULL, содержащая полные пути к скопированным файлам.

  3. Определите, сколько путей к файлам находятся в списке, вызвав DragQueryFile с параметром iFile, заданным для 0xFFFFFFFF. Функция возвращает количество путей к файлам в списке. Индекс пути к файлу на основе нуля в этом списке используется на следующем шаге для определения определенного пути.

  4. Извлеките пути к файлам из глобального объекта памяти, вызвав DragQueryFile один раз для каждого файла, указав iFile в индекс файла.

  5. Обработайте пути к файлам по мере необходимости и вставьте их в приложение.

  6. Вызовите ReleaseStgMedium и передайте указатель на структуру STGMEDIUM, которую вы передали в IDataObject::GetData на шаге 1. После выпуска структуры значение HDROP, созданное на шаге 2, больше не является допустимым и не должно использоваться.

Копирование содержимого удаленного файла в приложение

Сценарий: Пользователь перетаскивает один или несколько файлов из проводника Windows и помещает их в окно вашего приложения. Приложение извлекает содержимое файла (s) и вставляет его в приложение.

Этот сценарий использует перетаскивание для передачи файлов из проводника Windows в приложение. Перед операцией приложение должно:

  1. Вызовите RegisterClipboardFormat, чтобы зарегистрировать любые необходимые форматы буфера обмена оболочки.
  2. Вызовите RegisterDragDrop, чтобы зарегистрировать целевое окно и интерфейс IDropTarget вашего приложения.

После запуска операции пользователь выбирает один или несколько файлов и начинает перетаскивать их:

  1. Обозреватель Windows создает объект данных и загружает в него поддерживаемые форматы.
  2. Обозреватель Windows вызывает DoDragDrop , чтобы инициировать цикл перетаскивания.
  3. Когда перетаскиваемое изображение достигает целевого окна, система уведомляет вас, вызвав IDropTarget::DragEnter.
  4. Чтобы определить, что содержит объект данных, вызовите метод IDataObject::EnumFormatEtc. Используйте объект перечислителя, возвращаемый методом, чтобы перечислить форматы, содержащиеся в объекте данных. Если приложение не хочет принимать ни один из этих форматов, верните DROPEFFECT_NONE. В этом сценарии приложение должно игнорировать любые объекты данных, которые не содержат форматы, используемые для передачи файлов, например CF_HDROP.
  5. Когда пользователь удаляет данные, система вызывает IDropTarget::D rop.
  6. Используйте интерфейс IDataObject для извлечения содержимого файлов.

Существует несколько различных способов извлечения содержимого объекта Shell из объекта данных. Как правило, используйте следующий порядок:

  • Если файл содержит формат CF_TEXT , данные — это текст ANSI. Вы можете использовать формат CF_TEXT для извлечения данных, а не открытия самого файла.
  • Если файл содержит связанный или внедренный объект OLE, объект данных содержит формат CF_EMBEDDEDOBJECT. Используйте стандартные методы OLE для извлечения данных. Файлы-черновики всегда содержат формат CF_EMBEDDEDOBJECT.
  • Если объект Shell находится из файловой системы, объект данных содержит CF_HDROP формат с именами файлов. Извлеките имя файла из CF_HDROP и вызовите OleCreateFromFile, чтобы создать связанный или внедренный объект. Сведения о том, как получить имя файла из формата CF_HDROP, см. в разделе "Копирование имен файлов" из буфера обмена в приложение.
  • Если объект данных содержит формат CFSTR_FILEDESCRIPTOR, можно извлечь содержимое файла из CFSTR_FILECONTENTS формата файла. Сведения об этой процедуре см. в разделе "Использование формата CFSTR_FILECONTENTS для извлечения данных из файла".
  • До версии Shell 4.71 приложение указало, что оно передает тип ярлыка, задав FD_LINKUI в dwFlags элемента FILEDESCRIPTOR структуры. Для более новых версий оболочки предпочтительный способ указать, что ярлыки переносятся, — использовать формат CFSTR_PREFERREDDROPEFFECT, установленный на DROPEFFECT_LINK. Этот подход гораздо эффективнее, чем извлечение структуры FILEDESCRIPTOR только для проверки флага.

Если процесс извлечения данных будет длительным, может потребоваться выполнить операцию асинхронно в фоновом потоке. Затем основной поток может продолжаться без ненужных задержек. Сведения об асинхронном извлечении данных см. в разделе «Перетаскивание и опускание объектов оболочки асинхронно».

Использование формата CFSTR_FILECONTENTS для извлечения данных из файла

Формат CFSTR_FILECONTENTS предоставляет очень гибкий и мощный способ передачи содержимого файла. Не обязательно, чтобы данные хранились в одном файле. Все, что необходимо для этого формата, заключается в том, что объект данных представляет данные целевому объекту, как если бы он был файлом. Например, фактические данные могут быть разделом текстового документа или блоком данных, извлеченными из базы данных. Целевой объект может рассматривать данные как файл и не должен знать ничего о базовом механизме хранения.

Расширения пространства имен обычно используют CFSTR_FILECONTENTS для передачи данных, так как этот формат не предполагает какого-либо конкретного механизма хранения. Расширение пространства имен может использовать любой удобный механизм хранения и использовать этот формат, чтобы представить свои объекты приложениям, как будто они были файлами.

Механизм передачи данных для CFSTR_FILECONTENTS обычно TYMED_ISTREAM. Для передачи указателя интерфейса IStream требуется гораздо меньше памяти, чем загрузка данных в глобальный объект памяти, а IStream — это более простой способ представления данных, чем IStorage.

Формат CFSTR_FILECONTENTS всегда сопровождается форматом CFSTR_FILEDESCRIPTOR . Сначала необходимо проверить содержимое этого формата. Если передается несколько файлов, объект данных фактически будет содержать несколько форматов CFSTR_FILECONTENTS , по одному для каждого файла. Формат CFSTR_FILEDESCRIPTOR содержит имя и атрибуты каждого файла и предоставляет значение индекса для каждого файла, необходимое для извлечения формата CFSTR_FILECONTENTS определенного файла.

Чтобы извлечь формат CFSTR_FILECONTENTS :

  1. Извлеките формат CFSTR_FILEDESCRIPTOR в виде значения TYMED_HGLOBAL.
  2. Элемент hGlobal возвращаемой структуры STGMEDIUM указывает на глобальный объект памяти. Заблокируйте этот объект, передав значение hGlobal в GlobalLock.
  3. Преобразуйте указатель, который возвращает GlobalLock, в указатель FILEGROUPDESCRIPTOR. Он указывает на структуру FILEGROUPDESCRIPTOR, за которой следует одна или несколько структур FILEDESCRIPTOR. Каждая структура FILEDESCRIPTOR содержит описание файла, содержащегося в одном из сопровождающих CFSTR_FILECONTENTS форматов.
  4. Проверьте структуры FILEDESCRIPTOR, чтобы определить, какой из них соответствует файлу, который требуется извлечь. Индекс, отсчитываемый от нуля, структуры FILEDESCRIPTOR используется для определения формата CFSTR_FILECONTENTS файла. Поскольку размер глобального блока памяти не определён точно в байтах, используйте элементы структуры nFileSizeLow и nFileSizeHigh, чтобы определить, сколько байтов представляет файл в глобальном объекте памяти.
  5. Вызовите IDataObject::GetData с элементом cfFormat структуры FORMATETC, установленным в значение CFSTR_FILECONTENTS, и элементом lIndex, заданным на предыдущем шаге как индекс. Элемент tymed обычно имеет значение TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE. Затем объект данных может выбрать предпочтительный механизм передачи данных.
  6. Структура STGMEDIUM, возвращаемая IDataObject::GetData, будет содержать указатель на данные файла. Изучите член tymed структуры, чтобы определить механизм передачи данных.
  7. Если tymed задано значение TYMED_ISTREAM или TYMED_ISTORAGE, используйте интерфейс для извлечения данных. Если задано значение TYMED_HGLOBAL, данные содержатся в глобальном объекте памяти. Для обсуждения способа извлечения данных из глобального объекта памяти см. раздел «Извлечение глобального объекта памяти из объекта данных» в Объекте данных оболочки.
  8. Вызовите GlobalLock , чтобы разблокировать глобальный объект памяти, заблокированный на шаге 2.

Обработка оптимизированных операций перемещения

Сценарий. Файл перемещается из файловой системы в расширение пространства имен с помощью оптимизированного перемещения.

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

При оптимизированном перемещении целевой объект использует свое представление о том, как хранятся данные для обработки всей операции перемещения. Никогда не существует второй копии данных, и нет необходимости удалять оригинальные данные из источника. Данные оболочки хорошо подходят для оптимизации перемещения, так как целевой объект может обрабатывать всю операцию с помощью API оболочки. Типичный пример — перемещение файлов. Как только у целевого объекта есть путь к файлу для перемещения, можно использовать SHFileOperation для его перемещения. Нет необходимости удалять исходный файл.

Примечание.

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

 

Оптимизированные перемещения обрабатываются следующим образом:

  1. Источник вызывает DoDragDrop с параметром dwEffect, равным DROPEFFECT_MOVE, чтобы указать, что исходные объекты можно переместить.

  2. Целевой объект получает значение DROPEFFECT_MOVE через один из его методов IDropTarget , указывающий, что перемещение разрешено.

  3. Цель либо копирует объект (неоптимизированное перемещение), либо перемещает объект (оптимизированное перемещение).

  4. Затем целевой объект сообщает источнику, нужно ли удалить исходные данные.

    Оптимизированное перемещение — это операция по умолчанию с данными, удаленными целевым объектом. Чтобы сообщить источнику, что оптимизированное перемещение выполнено:

      • Целевой объект задает значение pdwEffect, полученное с помощью метода IDropTarget::Drop, в некоторое значение, отличное от DROPEFFECT_MOVE. Обычно для него задано значение DROPEFFECT_NONE или DROPEFFECT_COPY. Значение будет возвращено источнику методом DoDragDrop.
      • Целевой объект также вызывает метод объекта данных IDataObject::SetData и передает идентификатор формата CFSTR_PERFORMEDDROPEFFECT, установленный в DROPEFFECT_NONE. Этот вызов метода необходим, так как некоторые целевые объекты удаления могут неправильно задать параметр pdwEffect DoDragDrop. Формат CFSTR_PERFORMEDDROPEFFECT — это надежный способ указать, что оптимизированное перемещение произошло.

    Если цель совершила неоптимальное перемещение, данные должны быть удалены источником данных. Чтобы сообщить источнику о том, что было выполнено неоптимальное действие:

      • Целевой объект задает значение pdwEffect, полученное с помощью метода IDropTarget::D rop, для DROPEFFECT_MOVE. Значение будет возвращено источнику методом DoDragDrop.
      • Целевой объект также вызывает метод IDataObject::SetData и передает ему идентификатор формата CFSTR_PERFORMEDDROPEFFECT, установленный на DROPEFFECT_MOVE. Этот вызов метода необходим, так как некоторые целевые объекты сброса могут неправильно задать параметр pdwEffect в DoDragDrop. Формат CFSTR_PERFORMEDDROPEFFECT — это надежный способ указать, что произошел неоптимизированный шаг.
  5. Источник проверяет два значения, которые могут быть возвращены целевым объектом. Если для обоих задано значение DROPEFFECT_MOVE, он завершает неоптимизированное перемещение, удалив исходные данные. В противном случае целевой объект сделал оптимизированное перемещение и исходные данные были удалены.

Обработка операций удаления при вставке

Сценарий. Один или несколько файлов вырезаются из папки в проводнике Windows и вставляются в расширение пространства имен. Обозреватель Windows оставляет файлы выделенными, пока он не получит отзыв о результатах операции вставки.

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

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

Примечание.

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

 

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

Базовая процедура операции "удаление при вставке" выглядит так:

  1. Источник помечает отображение выбранных данных на экране.
  2. Источник создает объект данных. Указывает на операцию вырезания, добавляя формат CFSTR_PREFERREDDROPEFFECT со значением данных DROPEFFECT_MOVE.
  3. Источник помещает объект данных в буфер обмена с помощью OleSetClipboard.
  4. Целевой объект извлекает объект данных из буфера обмена с помощью OleGetClipboard.
  5. Целевой объект извлекает данные CFSTR_PREFERREDDROPEFFECT . Если для него задано значение только DROPEFFECT_MOVE, целевой объект может выполнить оптимизированное перемещение или просто скопировать данные.
  6. Если целевой объект не выполняет оптимизированное перемещение, он вызывает метод IDataObject::SetData с форматом CFSTR_PERFORMEDDROPEFFECT, установленным в значение DROPEFFECT_MOVE.
  7. По завершении вставки целевой объект вызывает метод IDataObject::SetData с форматом CFSTR_PASTESUCCEEDED, заданным для DROPEFFECT_MOVE.
  8. Когда вызывается метод IDataObject::SetData источника с форматом CFSTR_PASTESUCCEEDED, равным DROPEFFECT_MOVE, он должен проверить, получил ли он также формат CFSTR_PERFORMEDDROPEFFECT, равный DROPEFFECT_MOVE. Если оба формата отправляются целевым объектом, источнику придется удалить данные. Если получен только формат CFSTR_PASTESUCCEEDED , источник может просто удалить данные из его отображения. Если передача завершается ошибкой, источник обновляет отображение до исходного внешнего вида.

Передача данных в виртуальные папки и из них

Сценарий. Пользователь перетаскивает объект из виртуальной папки или удаляет его из нее.

Виртуальные папки содержат объекты, которые обычно не являются частью файловой системы. Некоторые виртуальные папки, такие как корзина, могут представлять данные, хранящиеся на жестком диске, но не как обычные объекты файловой системы. Некоторые могут представлять хранимые данные, которые хранятся в удаленной системе, например на ручном компьютере или на FTP-сайте. Другие, такие как папка "Принтеры", содержат объекты, которые не представляют хранимые данные вообще. Хотя некоторые виртуальные папки являются частью системы, разработчики также могут создавать и устанавливать пользовательские виртуальные папки, реализуя расширение пространства имен.

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

Таким образом, существует две группы разработчиков, которые должны быть обеспокоены передачей данных в виртуальные папки и из них:

  • Разработчики, приложения которых должны принимать данные, передаваемые из виртуальной папки.
  • Разработчики, чьи расширения пространства имен должны корректно поддерживать передачу данных.

Прием данных из виртуальной папки

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

При передаче объекта файловой системы в приложение объект данных, как правило, содержит формат CF_HDROP с полным путём к объекту. Приложение может извлечь эту строку и использовать обычные функции файловой системы для открытия файла и извлечения его данных. Тем не менее, поскольку виртуальные папки обычно не содержат обычные объекты файловой системы, они обычно не используют CF_HDROP.

Вместо CF_HDROP данные обычно передаются из виртуальных папок с форматами CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS. Формат CFSTR_FILECONTENTS имеет два преимущества по сравнению с CF_HDROP:

  • Не предполагается конкретный метод хранения данных.
  • Формат более гибкий. Он поддерживает три механизма передачи данных: глобальный объект памяти, интерфейс IStream или интерфейс IStorage.

Глобальные объекты памяти редко используются для передачи данных в виртуальные объекты или из них, так как данные должны быть скопированы в память в целом. Передача указателя интерфейса требует почти никакой памяти и является гораздо более эффективной. С очень большими файлами указатель интерфейса может быть единственным практическим механизмом передачи данных. Как правило, данные представлены указателем IStream, так как этот интерфейс является несколько более гибким, чем IStorage. Целевой объект извлекает указатель из объекта данных и использует методы интерфейса для извлечения данных.

Дополнительные сведения об обработке форматов CFSTR_FILEDESCRIPTOR CFSTR_FILECONTENTS см. в разделе "Использование формата CFSTR_FILECONTENTS/ для извлечения данных из файла".

Передача данных в расширение пространства имен и из нее

При реализации расширения пространства имен обычно требуется поддерживать возможности перетаскивания. Следуйте рекомендациям по источникам выбросов и объектам, описанным в Общих Рекомендациях. В частности, расширение пространства имен должно:

  • Уметь обрабатывать форматы CFSTR_FILEDESCRIPTOR/CFSTR_FILECONTENTS. Эти два формата обычно используются для передачи объектов в и из расширений пространства имен.
  • Умейте обрабатывать оптимизированные перемещения. Оболочка ожидает, что объекты будут перемещены оптимизированным образом.
  • Вы можете обрабатывать удаление при вставке. Оболочка использует удаление при вставке, когда объекты перемещаются из оболочки с помощью операции вырезания и вставки.
  • Возможность обрабатывать передачу данных через интерфейс IStream или IStorage. Передача данных в виртуальную папку или из нее обычно обрабатывается путем передачи одного из этих двух указателей интерфейса, как правило , указателя IStream . Затем целевой объект вызывает методы интерфейса для извлечения данных:
      • В качестве источника данных расширение пространства имен должно извлекать данные из хранилища и передавать их через этот интерфейс целевому объекту.
      • В качестве целевого объекта удаления расширение пространства имен должно принимать данные из источника через этот интерфейс и правильно хранить его.

Удаление файлов в корзине

Сценарий: Пользователь перемещает файл на Корзину. Приложение или расширение пространства имен удаляет исходный файл.

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

В большинстве случаев перемещение объектов оболочки в корзину работает аналогично любому другому каталогу. Однако, когда пользователь удаляет файл в корзине, источник должен удалить исходный файл, даже если обратная связь из папки указывает на операцию копирования. Как правило, источник удаления не имеет способа знать, в какой папке был удален объект данных. Однако для систем Windows 2000 и более поздних версий, когда объект данных помещается в корзину, оболочка Windows вызовет метод IDataObject::SetData объекта данных с форматом CFSTR_TARGETCLSID, заданным для идентификатора класса корзины (CLSID) (CLSID_RecycleBin). Чтобы правильно обрабатывать случай с Корзиной, объект данных должен иметь возможность распознавать этот формат и передавать информацию источнику через приватный интерфейс.

Примечание.

При вызове IDataObject::SetData с форматом CFSTR_TARGETCLSID, установленным на CLSID_RecycleBin, источник данных должен закрыть все открытые дескрипторы к передаваемым объектам перед возвратом из метода. В противном случае можно создать нарушения общего доступа.

 

Создание и импорт файлов черновиков

Сценарий: Пользователь перетаскивает данные из файла данных приложения OLE и помещает их на рабочий стол или в проводник Windows.

Windows позволяет пользователям перетаскивать объект из файла данных приложения OLE и помещать его на рабочий стол или в папку файловой системы. Эта операция создает временный файл, содержащий данные или ссылку на данные. Имя файла берется из короткого имени, зарегистрированного для CLSID объекта и CF_TEXT данных. Чтобы оболочка могла создавать временный файл, содержащий данные, интерфейс IDataObject приложения должен поддерживать формат буфера обмена CF_EMBEDSOURCE. Чтобы создать файл со ссылкой, IDataObject должен поддерживать формат CF_LINKSOURCE.

Существуют также три опциональные функции, которые приложение может реализовать для поддержки скрап-файлов.

  • Поддержка поездок туда и обратно
  • Форматы кэшированных данных
  • Отложенная отрисовка

Поддержка круглого пути

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

Когда оболочка создает черновой файл, она представляет данные в виде встраиваемого объекта. При передаче лома в другой контейнер он передается как объект внедрения, даже если он возвращается в исходный документ. Ваше приложение отвечает за определение формата данных, содержащегося в ломе, и при необходимости помещает данные обратно в собственный формат.

Чтобы установить формат внедренного объекта, определите его CLSID, извлекая формат CF_OBJECTDESCRIPTOR данного объекта. Если CLSID указывает формат данных, принадлежащий приложению, он должен передавать собственные данные вместо вызова OleCreateFromData.

Кэшированные форматы данных

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

  • Чтобы разрешить перенос отходов в контейнеры, отличные от OLE, которые не могут принимать внедренные форматы объектов.
  • Чтобы обеспечить связь наборов приложений с частным форматом.
  • Чтобы упростить осуществление поездок туда и обратно.

Приложения могут добавлять форматы во временное хранилище, сохраняя их в реестре. Существует два типа кэшированных форматов:

  • Форматы приоритетного кэша. Для этих форматов данные в полном объеме копируются в буфер из объекта данных.
  • Форматы отложенного отображения. Для этих форматов объект данных не копируется в буфер обмена. Вместо этого отрисовка задерживается до тех пор, пока целевой объект не запрашивает данные. Задержка отрисовки подробно рассматривается в следующем разделе.

Чтобы добавить кэш приоритета или формат с отложенной отрисовкой, создайте вложенный ключ DataFormat под ключ CLSID приложения, которое является источником данных. В этом подразделе создайте подраздел PriorityCacheFormats или DelayRenderFormats. Для каждого приоритетного кэша или отложенного формата отрисовки создайте нумерованный подраздел, начиная с нуля. Установите значение этого ключа в виде строки с зарегистрированным именем формата или в виде значения #X, где X соответствует номеру стандартного формата буфера обмена.

В следующем примере показаны кэшированные форматы для двух приложений. Приложение MyProg1 имеет формат форматированного текста в качестве приоритетного формата кэша, а частный формат "Мой формат" используется как формат отложенной прорисовки. Приложение MyProg2 имеет формат CF_BITMAP (#8) в качестве формата кэша приоритета.

HKEY_CLASSES_ROOT
   CLSID
      {GUID}
         (Default) = MyProg1
         DataFormats
            PriorityCacheFormats
               0
                  (Default) = Rich Text Format
            DelayRenderFormats
               0
                  (Default) = My Format
      {GUID}
         (Default) = MyProg2
         DataFormats
            PriorityCacheFormats
               0
                  (Default) = #8

Дополнительные форматы можно добавить, создав дополнительные нумерованные подразделы.

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

Формат отложенной отрисовки позволяет приложению создавать файл слома, но откладывать расходы на отрисовку данных до тех пор, пока не будет запрошен целевой объект. Интерфейс IDataObject объекта предоставит форматы отложенной отрисовки назначению вместе с родными и кэшированными данными. Если целевой объект запрашивает отложенный формат отрисовки, оболочка запустит приложение и предоставит данные целевому объекту из активного объекта.

Примечание.

Так как отложенная отрисовка является несколько рискованной, её следует использовать с осторожностью. Он не будет работать, если сервер недоступен или в приложениях, которые не включены OLE.

 

Перетаскивание и удаление объектов оболочки асинхронно

Сценарий. Пользователь передает большой блок данных из источника в целевой объект. Чтобы избежать блокировки обоих приложений в течение значительного времени, целевой объект извлекает данные асинхронно.

Как правило, перетаскивание — это синхронная операция. Краткое описание

  1. Источник удаления вызывает DoDragDrop и блокирует его основной поток до тех пор, пока функция не возвращается. Блокировка основного потока обычно блокирует обработку пользовательского интерфейса.
  2. После вызова метода IDropTarget::Drop целевой объект извлекает данные из объекта данных в своем основном потоке. Обычно эта процедура блокирует обработку пользовательского интерфейса целевого объекта в течение процесса извлечения.
  3. После извлечения данных целевой объект возвращает вызов IDropTarget::Drop, система возвращает DoDragDrop, и оба потока могут продолжить работу.

Короче говоря, синхронная передача данных может блокировать основные потоки обоих приложений в течение значительного времени. В частности, оба потока должны ждать, пока целевой объект извлекает данные. Для небольших объемов данных время, необходимое для извлечения, невелико, и синхронная передача данных работает очень хорошо. Однако синхронно извлечение больших объемов данных может привести к длительным задержкам и препятствовать пользовательскому интерфейсу как целевого, так и исходного.

Интерфейс IAsyncOperation/ является необязательным интерфейсом, который можно реализовать объектом данных. Он предоставляет целевой объект удаления возможность асинхронно извлекать данные из объекта данных в фоновом потоке. После передачи извлечения данных фоновому потоку первичные потоки обоих приложений могут продолжать выполнение.

Использование IASyncOperation/IDataObjectAsyncCapability

Примечание.

Интерфейс первоначально был назван IAsyncOperation, но позже он был изменен на IDataObjectAsyncCapability. В противном случае два интерфейса идентичны.

 

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

  1. Создайте объект данных, предоставляющий IAsyncOperation/IDataObjectAsyncCapability.
  2. Чтобы указать, что поддерживается асинхронная операция, вызовите SetAsyncMode с параметром fDoOpAsync, равным VARIANT_TRUE.
  3. После возврата DoDragDrop, вызовите InOperation:
    • Если InOperation завершается сбоем или возвращает VARIANT_FALSE, происходит обычная синхронная передача данных и процесс извлечения данных завершен. Источник должен выполнить любую необходимую очистку и продолжить.
    • Если InOperation возвращает VARIANT_TRUE, данные извлекаются асинхронно. Операции очистки должны обрабатываться с помощью EndOperation.
  4. Отпустите объект данных.
  5. После завершения асинхронной передачи данных объект данных обычно уведомляет источник через закрытый интерфейс.

В следующей процедуре описывается, как целевой объект падения использует интерфейсы IAsyncOperation и IDataObjectAsyncCapability для извлечения данных асинхронно:

  1. Когда система вызывает IDropTarget::Drop, вызовите IDataObject::QueryInterface и запросите интерфейс IAsyncOperation и IDataObjectAsyncCapability (IID_IAsyncOperation/IID_IDataObjectAsyncCapability) из объекта данных.
  2. Вызовите GetAsyncMode. Если метод возвращает VARIANT_TRUE, объект данных поддерживает асинхронное извлечение данных.
  3. Создайте отдельный поток для обработки извлечения данных и вызова StartOperation.
  4. Верните вызов IDropTarget::Drop, как при обычной операции передачи данных. DoDragDrop возвращает и разблокирует источник удаления. Не вызывайте IDataObject::SetData , чтобы указать результат оптимизированной операции перемещения или удаления при вставке. Дождитесь завершения операции.
  5. Извлеките данные в фоновом потоке. Основной поток целевого объекта разблокирован и свободен для продолжения.
  6. Если передача данных была оптимизированным перемещением или удалением при вставке, вызовите IDataObject::SetData, чтобы указать результат.
  7. Уведомите объект данных о завершении извлечения путем вызова EndOperation.