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


Объект данных оболочки

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

Принцип работы объектов данных

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

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

Форматы буфера обмена

Каждый элемент данных в объекте данных имеет связанный формат, который обычно называется форматом буфера обмена. Существует ряд стандартных форматов буфера обмена, объявленных в Winuser.h, которые соответствуют часто используемым типам данных. Форматы буфера обмена представляют собой целые числа, но обычно на них ссылается эквивалентное имя, которое имеет форму CF_XXX. Например, формат буфера обмена для текста ANSI CF_TEXT.

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

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

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

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

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

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

Структура FORMATETC

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

  • Элемент данных по-прежнему определяется по формату буфера обмена в элементе cfFormat .

  • Передача данных не ограничивается объектами глобальной памяти. Элемент tymed используется для указания механизма передачи данных, содержащегося в связанной структуре STGMEDIUM . Ему присваивается одно из TYMED_XXX значений .

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

  • Член dwAspect обычно имеет значение DVASPECT_CONTENT. Однако в Shlobj.h определены три значения, которые можно использовать для передачи данных оболочки.

    Значение Описание
    DVASPECT_COPY Используется для указания того, что формат представляет копию данных.
    DVASPECT_LINK Используется, чтобы указать, что формат представляет ярлык для данных.
    DVASPECT_SHORTNAME Используется с форматом CF_HDROP для запроса пути к файлу с именами, сокращенными до формата 8.3.

     

  • Элемент ptd не используется для передачи данных оболочки и обычно имеет значение NULL.

Структура STGMEDIUM

Структура STGMEDIUM предоставляет доступ к передаваемым данным. Для данных оболочки поддерживаются три механизма передачи данных:

  • Объект глобальной памяти.
  • Интерфейс IStream .
  • Интерфейс IStorage .

Элемент tymed структуры STGMEDIUM — это TYMED_XXX значение, определяющее механизм передачи данных. Второй элемент — это указатель, используемый целевым объектом для извлечения данных. Указатель может быть одного из различных типов в зависимости от значения tymed . В следующей таблице приведены три значения с тимами , которые используются для передачи данных оболочки, а также соответствующие имена членов STGMEDIUM .

Tymed Value Имя участника Описание
TYMED_HGLOBAL Hglobal Указатель на объект глобальной памяти. Этот тип указателя обычно используется для передачи небольших объемов данных. Например, оболочка использует глобальные объекты памяти для передачи коротких текстовых строк, таких как имена файлов или URL-адреса.
TYMED_ISTREAM pstm Указатель на интерфейс IStream . Этот тип указателя является предпочтительным для большинства операций передачи данных оболочки, так как для него требуется относительно мало памяти по сравнению с TYMED_HGLOBAL. Кроме того, механизм передачи данных TYMED_ISTREAM не требует, чтобы источник хранил свои данные каким-либо конкретным способом.
TYMED_ISTORAGE pstg Указатель на интерфейс IStorage . Целевой объект вызывает методы интерфейса для извлечения данных. Как и TYMED_ISTREAM, для этого типа указателя требуется относительно мало памяти. Однако, поскольку TYMED_ISTORAGE менее гибкий, чем TYMED_ISTREAM, он используется не так часто.

 

Создание объекта данных источником

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

  1. Вызовите RegisterClipboardFormat , чтобы получить допустимое значение формата буфера обмена для каждого формата оболочки, который будет включен в объект данных. Помните, что CF_HDROP уже является допустимым форматом буфера обмена и его не нужно регистрировать.
  2. Для каждого передаваемого формата либо поместите связанные данные в объект глобальной памяти, либо создайте объект, предоставляющий доступ к этим данным через интерфейс IStream или IStorage . Интерфейсы IStream и IStorage создаются с использованием стандартных методов COM. Сведения об обработке объектов глобальной памяти см. в статье Добавление объекта глобальной памяти в объект данных.
  3. Создание структур FORMATETC и STGMEDIUM для каждого формата.
  4. Создание экземпляра объекта данных.
  5. Загрузите данные в объект данных, вызвав метод IDataObject::SetData для каждого поддерживаемого формата и передав структуры ФОРМАТА FORMATETC и STGMEDIUM .
  6. При передаче данных буфера обмена вызовите OleSetClipboard , чтобы поместить указатель на интерфейс IDataObject объекта данных в буфере обмена. Для переноса перетаскивания инициируйте цикл перетаскивания , вызвав DoDragDrop. Указатель IDataObject будет передан в целевой объект перетаскивания при удалении данных, завершая цикл перетаскивания.

Теперь объект данных готов к передаче в целевой объект. Для передачи данных буфера обмена объект просто удерживается до тех пор, пока целевой объект не запросит его, вызвав OleGetClipboard. При переносе данных перетаскиванием объект данных отвечает за создание значка для представления данных и их перемещение при перемещении пользователем курсора. Пока объект находится в цикле перетаскивания, источник получает сведения о состоянии через интерфейс IDropSource . Дополнительные сведения см. в разделе Реализация IDropSource.

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

Добавление объекта глобальной памяти в объект данных

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

  1. Создайте структуру FORMATETC . Задайте для элемента cfFormat соответствующее значение формата буфера обмена, а для элемента с символами — значение TYMED_HGLOBAL.
  2. Создайте структуру STGMEDIUM . Задайте для элемента с символами значение TYMED_HGLOBAL.
  3. Создайте объект глобальной памяти, вызвав GlobalAlloc для выделения блока памяти подходящего размера.
  4. Назначьте блок данных для передачи на адрес, возвращаемый GlobalAlloc.
  5. Назначьте адрес объекта глобальной памяти элементу hGlobal структуры STGMEDIUM .
  6. Загрузите формат в объект данных, вызвав IDataObject::SetData и передав структуры FORMATETC и STGMEDIUM , созданные на предыдущих шагах.

Следующий пример функции создает глобальный объект памяти, содержащий значение DWORD, и загружает его в объект данных. Параметр pdtobj является указателем на интерфейс IDataObject объекта данных, cf — значение формата буфера обмена, а dw — значение данных.

STDAPI DataObj_SetDWORD(IDataObject *pdtobj, UINT cf, DWORD dw)
{
    FORMATETC fmte = {(CLIPFORMAT) cf, 
                      NULL, 
                      DVASPECT_CONTENT, 
                      -1, 
                      TYMED_HGLOBAL};
    STGMEDIUM medium;

    HRESULT hres = E_OUTOFMEMORY;
    DWORD *pdw = (DWORD *)GlobalAlloc(GPTR, sizeof(DWORD));
    
    if (pdw)
    {
        *pdw = dw;       
        medium.tymed = TYMED_HGLOBAL;
        medium.hGlobal = pdw;
        medium.pUnkForRelease = NULL;

        hres = pdtobj->SetData(&fmte, &medium, TRUE);
 
        if (FAILED(hres))
            GlobalFree((HGLOBAL)pdw);
    }
    return hres;
}

Реализация IDataObject

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

  • Загрузка данных в объект данных.
  • Извлечение данных из объекта данных.
  • Определение типов данных в объекте данных.
  • Предоставление обратной связи объекту данных о результатах передачи данных.

IDataObject поддерживает ряд методов. В этом разделе рассматривается реализация трех наиболее важных методов для объектов данных оболочки : SetData, EnumFormatEtc и GetData. Дополнительные сведения о других методах см. в справочнике по IDataObject .

SetData - метод

Основная функция метода IDataObject::SetData заключается в том, чтобы разрешить источнику загружать данные в объект данных. Для каждого включаемого формата источник создает структуру FORMATETC для идентификации формата и структуру STGMEDIUM для хранения указателя на данные. Затем источник вызывает метод объекта IDataObject::SetData и передает структуры ФОРМАТА FORMATETC и STGMEDIUM . Метод должен хранить эти сведения, чтобы они были доступны, когда целевой объект вызывает IDataObject::GetData для извлечения данных из объекта .

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

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

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

Метод EnumFormatEtc

Когда целевой объект получает объект данных, он обычно вызывает FORMATETC , чтобы определить, какие форматы содержит объект. Метод создает объект перечисления OLE и возвращает указатель на интерфейс IEnumFORMATETC объекта. Затем целевой объект использует интерфейс для перечисления доступных форматов.

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

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

Так как IDataObject::EnumFormatEtc позволяет целевому объекту определить типы данных, нет необходимости перечислять несколько CFSTR_FILECONTENTS формате. Если целевому объекту необходимо знать, сколько из этих форматов содержит объект данных, целевой объект может получить эти сведения из соответствующего формата CFSTR_FILEDESCRIPTOR. Дополнительные сведения о реализации IDataObject::EnumFormatEtc см. в справочной документации по методу .

Метод GetData

Целевой объект вызывает IDataObject::GetData для извлечения определенного формата данных. Целевой объект задает формат путем передачи соответствующей структуры FORMATETC . IDataObject::GetData возвращает структуру STGMEDIUM формата.

Целевой объект может задать для элемента структурыFORMATETC определенное значение TYMED_XXXX, чтобы указать, какой механизм передачи данных будет использоваться для извлечения данных. Однако целевой объект также может сделать более универсальный запрос и позволить объекту данных принять решение. Чтобы запросить у объекта данных выбор механизма передачи данных, целевой объект задает все поддерживаемые значения TYMED_XXX . IDataObject::GetData выбирает один из этих механизмов передачи данных и возвращает соответствующую структуру STGMEDIUM . Например, для tymed обычно задано значение TYMED_HGLOBAL | TYMED_ISTREAM | TYMED_ISTORAGE запросить любой из трех механизмов передачи данных оболочки.

Примечание

Так как может быть несколько форматов CFSTR_FILECONTENTS , членов cfFormat и tymed структуры FORMATETC недостаточно, чтобы указать, какую структуру STGMEDIUM должен возвращать IDataObject::GetData . Для формата CFSTR_FILECONTENTS IDataObject::GetData также должен проверить член lIndex структуры FORMATETC, чтобы вернуть правильную структуру STGMEDIUM.

 

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

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

Реализация IDropSource

Источник должен создать объект, предоставляющий интерфейс IDropSource . Этот интерфейс позволяет источнику обновить изображение перетаскивания , указывающее текущее положение курсора, и предоставить системе обратную связь о том, как завершить операцию перетаскивания. IDropSource имеет два метода: GiveFeedback и QueryContinueDrag.

GiveFeedback - метод

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

Когда курсор входит или покидает целевое окно и перемещается по целевому окну, система периодически вызывает интерфейс IDropTarget целевого объекта. Целевой объект отвечает значением DROPEFFECT , которое пересылается источнику с помощью метода GiveFeedback . При необходимости источник может изменить внешний вид курсора на основе значения DROPEFFECT . Дополнительные сведения см. в справочниках по GiveFeedback и DoDragDrop .

QueryContinueDrag - метод

Этот метод вызывается при изменении состояния кнопки мыши или клавиатуры, когда объект данных находится в цикле перетаскивания. Он уведомляет источник о том, была ли нажата клавиша ESC, и предоставляет текущее состояние клавиш-модификаторов клавиатуры, таких как CTRL или SHIFT. Возвращаемое значение метода QueryContinueDrag указывает одно из трех действий:

  • S_OK. Продолжение операции перетаскивания
  • DRAGDROP_S_DROP. Удалите данные. Затем система вызывает метод IDropTarget::D rop целевого объекта.
  • DRAGDROP_S_CANCEL. Завершите цикл перетаскивания, не удалив данные. Обычно это значение возвращается, если была нажата клавиша ESCAPE.

Дополнительные сведения см. в справочниках QueryContinueDrag и DoDragDrop .

Как целевой объект обрабатывает объект данных

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

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

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

  • Система вызывает IDropTarget::D ragEnter , когда курсор входит в целевое окно.
  • Система периодически вызывает IDropTarget::D ragOver , когда курсор проходит через целевое окно, чтобы присвоить целевому объекту текущее положение курсора.
  • Система вызывает IDropTarget::D ragLeave , когда курсор покидает целевое окно.
  • Система вызывает IDropTarget::D rop , когда пользователь удаляет объект данных в целевом окне.

Описание реализации этих методов см. в разделе IDropTarget.

При сбросе данных IDropTarget::D rop предоставляет целевому объекту указатель на интерфейс IDataObject объекта данных. Затем целевой объект использует этот интерфейс для извлечения данных из объекта данных.

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

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

  • Вызовите IDataObject::EnumFormatEtc. Объект данных создает стандартный объект перечисления OLE и возвращает указатель на интерфейс IEnumFORMATETC .
  • Используйте методы IEnumFORMATETC для перечисления форматов, содержащихся в объекте данных. Эта операция обычно извлекает одну структуру FORMATETC для каждого формата, содержащегося в объекте. Однако объект перечисления обычно возвращает только одну структуру FORMATETC для формата CFSTR_FILECONTENTS , независимо от количества таких форматов, содержащихся в объекте данных.
  • Выберите один или несколько форматов для извлечения и сохраните их структуры FORMATETC .

Чтобы получить определенный формат, передайте связанную структуру FORMATETC в IDataObject::GetData. Этот метод возвращает структуру STGMEDIUM , которая предоставляет доступ к данным. Чтобы указать конкретный механизм передачи данных, задайте для структуры FORMATETC значение, соответствующее TYMED_XXXX. Чтобы запросить у объекта данных выбор механизма передачи данных, целевой объект задает значения TYMED_XXX для каждого механизма передачи данных, который может обрабатывать целевой объект. Объект данных выбирает один из этих механизмов передачи данных и возвращает соответствующую структуру STGMEDIUM .

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

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

Извлечение объекта глобальной памяти из объекта данных

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

  1. Создайте структуру FORMATETC . Задайте для элемента cfFormat соответствующее значение формата буфера обмена, а для элемента с символами — значение TYMED_HGLOBAL.

  2. Создайте пустую структуру STGMEDIUM .

  3. Вызовите метод IDataObject::GetData и передайте указатели на структуры FORMATETC и STGMEDIUM .

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

  4. Назначьте данные локальной переменной, вызвав GlobalLock и передав элемент hGlobal структуры STGMEDIUM .

  5. Вызовите GlobalUnlock , чтобы освободить блокировку объекта глобальной памяти.

  6. Вызовите ReleaseStgMedium , чтобы освободить объект глобальной памяти.

Примечание

Для освобождения объекта глобальной памяти, а не GlobalFree, необходимо использовать ReleaseStgMedium.

 

В следующем примере показано, как извлечь значение DWORD , хранящееся в виде объекта глобальной памяти, из объекта данных. Параметр pdtobj является указателем на интерфейс IDataObject объекта данных, cf — формат буфера обмена, определяющий нужные данные, а pdwOut используется для возврата значения данных.

STDAPI DataObj_GetDWORD(IDataObject *pdtobj, UINT cf, DWORD *pdwOut)
{    STGMEDIUM medium;
   FORMATETC fmte = {(CLIPFORMAT) cf, NULL, DVASPECT_CONTENT, -1, 
       TYMED_HGLOBAL};
    HRESULT hres = pdtobj->GetData(&fmte, &medium);
    if (SUCCEEDED(hres))
   {
       DWORD *pdw = (DWORD *)GlobalLock(medium.hGlobal);
       if (pdw)
       {
           *pdwOut = *pdw;
           GlobalUnlock(medium.hGlobal);
       }
       else
       {
           hres = E_UNEXPECTED;
       }
       ReleaseStgMedium(&medium);
   }
   return hres;
}

Реализация IDropTarget

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

При использовании обычных операций перетаскивания целевой объект сообщает объекту данных о результатах операции, задавая параметру pdwEffectобъекта IDropTarget::D rop соответствующее значение DROPEFFECT . При использовании объектов данных оболочки целевому объекту также может потребоваться вызвать IDataObject::SetData. Обсуждение того, как целевые объекты должны реагировать на различные сценарии передачи данных, см. в разделе Обработка сценариев передачи данных оболочки.

В следующих разделах кратко рассматривается реализация методов IDropTarget::D ragEnter, IDropTarget::D ragOver и IDropTarget::D rop . Дополнительные сведения см. в справочной документации.

Метод DragEnter

Система вызывает метод IDropTarget::D ragEnter , когда курсор входит в целевое окно. Его параметры предоставляют целевому объекту расположение курсора, состояние клавиш-модификаторов клавиатуры, таких как клавиша CTRL, и указатель на интерфейс IDataObject объекта данных. Целевой объект отвечает за использование этого интерфейса, чтобы определить, может ли он принимать любые форматы, содержащиеся в объекте данных. Если это возможно, обычно значение pdwEffect остается без изменений. Если не удается принять данные из объекта данных, параметру pdwEffect присваивается значение DROPEFFECT_NONE. Система передает значение этого параметра в интерфейс IDropSource объекта данных, чтобы позволить ему отобразить соответствующее изображение перетаскивания.

Целевые объекты не должны использовать метод IDataObject::GetData для отрисовки данных оболочки до их удаления. Полная отрисовка данных объекта для каждого такого вхождения может привести к остановке курсора перетаскивания. Чтобы избежать этой проблемы, некоторые объекты оболочки содержат формат CFSTR_INDRAGLOOP . Извлекая этот формат, целевые объекты могут проверка состояние цикла перетаскивания, избегая интенсивной отрисовки данных объекта. Значение данных формата — это значение DWORD , которое имеет ненулевое значение, если объект данных находится в цикле перетаскивания. Значение данных формата равно нулю, если данные были удалены.

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

Пока курсор находится над целевым окном, целевой объект может использовать вспомогательный объект перетаскивания , чтобы заменить перетаскивание изображения объекта данных собственным. Если это так, IDropTarget::D ragEnter должен вызывать IDropTargetHelper::D ragEnter для передачи сведений, содержащихся в параметрах DragEnter , во вспомогательный объект перетаскивания.

Метод DragOver

По мере перемещения курсора в целевом окне система периодически вызывает метод IDropTarget::D ragOver . Его параметры предоставляют целевому объекту расположение курсора и состояние клавиш-модификаторов клавиатуры, таких как клавиша CTRL. IDropTarget::D ragOver имеет те же обязанности, что и IDropTarget::D ragEnter, и реализации обычно очень похожи.

Если целевой объект использует вспомогательный объект перетаскивания, IDropTarget::D ragOver должен вызвать IDropTargetHelper::D ragOver , чтобы перенаправить сведения, содержащиеся в параметрах DragOver , во вспомогательный объект перетаскивания.

Метод Drop

Система вызывает метод IDropTarget::D rop , чтобы уведомить целевой объект о том, что пользователь убрал данные, как правило, путем освобождения кнопки мыши. IDropTarget::D rop имеет те же параметры, что и IDropTarget::D ragEnter. В ответ целевой объект обычно извлекает один или несколько форматов из объекта данных. По завершении целевой объект должен задать для параметра pdwEffect значение DROPEFFECT , указывающее результат операции. Для некоторых типов передачи данных оболочки целевой объект также должен вызывать IDataObject::SetData , чтобы передать в объект данных формат с дополнительными сведениями о результате операции. Подробное обсуждение см. в разделе Обработка сценариев передачи данных оболочки.

Если целевой объект использует вспомогательный объект перетаскивания, IDropTarget::D rop должен вызвать IDropTargetHelper::D rop для пересылки сведений, содержащихся в параметрах IDropTargetHelper::D ragOver , во вспомогательный объект перетаскивания.

Использование вспомогательного объекта перетаскивания

Вспомогательный объект перетаскивания (CLSID_DragDropHelper) экспортируется оболочкой, чтобы целевые объекты могли указать изображение перетаскивания, когда оно находится над целевым окном. Чтобы использовать вспомогательный объект перетаскивания, создайте внутрипроцессный объект сервера, вызвав CoCreateInstance с идентификатором класса (CLSID) CLSID_DragDropHelper. Вспомогательный объект перетаскивания предоставляет два интерфейса, которые используются следующим образом:

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

Использование интерфейса IDragSourceHelper

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

  • Целевые объекты перетаскивания с окном могут зарегистрировать сообщение DI_GETDRAGIMAGE окна, инициализировав вспомогательный объект перетаскивания с помощью IDragSourceHelper::InitializeFromWindow. Когда целевой объект получает сообщение DI_GETDRAGIMAGE, обработчик помещает сведения о растровом изображении перетаскивания в структуру SHDRAGIMAGE , которая передается в качестве значения lParam сообщения.
  • Целевые объекты удаления без окон указывают растровое изображение при инициализации вспомогательного объекта перетаскивания с помощью IDragSourceHelper::InitializeFromBitmap.

Использование интерфейса IDropTargetHelper

Этот интерфейс позволяет целевому объекту перетаскивания уведомлять вспомогательный объект перетаскивания, когда курсор входит в целевой объект или покидает его. Пока курсор находится над целевым окном, IDropTargetHelper позволяет целевому объекту предоставить вспомогательному объекту перетаскивания сведения, получаемые целевым объектом через интерфейс IDropTarget .

Четыре метода IDropTargetHelperIDropTargetHelper::D ragEnter, IDropTargetHelper::D ragLeave, IDropTargetHelper::D ragOver и IDropTargetHelper::D rop — связаны с методом IDropTarget с тем же именем. Чтобы использовать вспомогательный объект перетаскивания, каждый из методов IDropTarget должен вызывать соответствующий метод IDropTargetHelper для пересылки информации во вспомогательный объект перетаскивания. Пятый метод IDropTargetHelper , IDropTargetHelper::Show, уведомляет вспомогательный объект перетаскивания о том, чтобы он отображал или скрывал изображение перетаскивания. Этот метод используется при перетаскивании целевого окна в режиме видео с низкой глубиной цвета. Это позволяет целевому объекту скрыть изображение перетаскивания во время рисования окна.