Blog. Just Blog

Продвинутая работа с буфером обмена на Ассемблере

Версия для печати Добавить в Избранное Отправить на E-Mail | Категория: Образ мышления: Assembler | Автор: ManHunter
Продвинутая работа с буфером обмена на Ассемблере
Продвинутая работа с буфером обмена на Ассемблере

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

Поскольку работать будем с COM-объектами, нам понадобится несколько структур, констант и интерфейсов. С некоторыми вы уже знакомы по предыдущим статьям, а что-то будет в новинку.
  1. struct STGMEDIUM
  2.         tymed           dd ?
  3.         hGlobal         dd ?
  4.         pUnkForRelease  dd ?
  5. ends
  6.  
  7. struct FORMATETC
  8.         cfFormat        dd ?
  9.         lptd            dd ?
  10.         dwAspect        dd ?
  11.         lindex          dd ?
  12.         tymed           dd ?
  13. ends
  14.  
  15. struct DROPFILES
  16.         pFiles dd ?
  17.         pt     POINT
  18.         fNC    dd ?
  19.         fWide  dd ?
  20. ends
  21.  
  22. ; GUID {0000010E-0000-0000-C000-000000000046}
  23. IID_IDataObject dd 00000010Eh
  24.                 dw 00000h
  25.                 dw 00000h
  26.                 db 0C0h, 000h, 000h, 000h, 000h, 000h, 000h, 046h
  27.  
  28. ; IID_IDataObject Interface
  29. struct IDataObject
  30.     ; IUnknown
  31.     QueryInterface        dd ?   ; 000h
  32.     AddRef                dd ?   ; 004h
  33.     Release               dd ?   ; 008h
  34.     ; IDataObject
  35.     GetData               dd ?   ; 00Ch
  36.     GetDataHere           dd ?   ; 010h
  37.     QueryGetData          dd ?   ; 014h
  38.     GetCanonicalFormatEtc dd ?   ; 018h
  39.     SetData               dd ?   ; 01Ch
  40.     EnumFormatEtc         dd ?   ; 020h
  41.     DAdvise               dd ?   ; 024h
  42.     DUnadvise             dd ?   ; 028h
  43.     EnumDAdvise           dd ?   ; 02Ch
  44. ends
  45.  
  46. DVASPECT_CONTENT = 1
  47. TYMED_HGLOBAL    = 1
Это интересная техника, освоившись с которой, вы сможете передавать через буфер обмена не только строки, но и файлы, графику, виртуальные данные. Так что будем разбирать примеры по нарастанию сложности.

Начнем с получения текстового значения из буфера обмена и запись в буфер произвольной строки. Для получения объекта данных, связанного с буфером обмена, используется функция OleGetClipboard. После этого при помощи метода GetData получаем сами данные, предварительно настроив в структуре FORMATETC их формат. Если формат данных известен заранее и ваша программа ожидает именно его, то указываем это значение, иначе придется проверить доступность нужного формата при помощи функций IsClipboardFormatAvailable или EnumClipboardFormats. В случае успешного вызова, поле hGlobal структуры STGMEDIUM будет содержать хэндл блока памяти с данными буфера обмена.
  1.         ; Инициализация
  2.         invoke  OleInitialize,NULL
  3.  
  4.         ; Прочитать содержимое буфера обмена
  5.         invoke  OleGetClipboard,pDataObject
  6.  
  7.         ; Настроить формат данных
  8.         mov     [fmte.cfFormat],CF_UNICODETEXT
  9.         mov     [fmte.lptd],NULL
  10.         mov     [fmte.dwAspect],DVASPECT_CONTENT
  11.         mov     [fmte.lindex],-1
  12.         mov     [fmte.tymed],TYMED_HGLOBAL
  13.  
  14.         ; Прочитать данные из объекта
  15.         mov     eax,[pDataObject]
  16.         mov     eax,[eax]
  17.         stdcall dword [eax+IDataObject.GetData],[pDataObject],fmte,medium
  18.  
  19.         ; Получить адрес содержимого
  20.         invoke  GlobalLock,[medium.hGlobal]
  21.  
  22.         ...
  23.         ; EAX -> данные
  24.         ...
  25.  
  26.         ; Освободить память
  27.         invoke  GlobalUnlock,[medium.hGlobal]
  28.         invoke  ReleaseStgMedium,medium
  29.  
  30.         ; Вызвать метод интерфейса IDataObject->Release()
  31.         mov     eax,[pDataObject]
  32.         mov     eax,[eax]
  33.         stdcall dword [eax+IDataObject.Release],[pDataObject]
  34.  
  35.         ; Освободить ресурсы
  36.         invoke  OleUninitialize
Чтобы записать информацию в буфер обмена, используется функция OleSetClipboard. Она также работает с объектом данных, который нам надо предварительно подготовить. Экземпляр объекта удобнее всего создать функцией SHCreateDataObject, затем выделить память нужного объема для хранения копируемого текста, скопировать в нее текст и настроить структуры FORMATETC (формат копируемых данных) и STGMEDIUM (указатель на данные). Подготовленные данные связываются с объектом при помощи метода SetData, а объект данных назначается буферу обмена.
  1.         ; Инициализация
  2.         invoke  OleInitialize,NULL
  3.  
  4.         ; Создать объект данных
  5.         invoke  SHCreateDataObject,NULL,0,NULL,NULL,IID_IDataObject,pDataObject
  6.  
  7.         ...
  8.         ; szTxt - строка для записи в буфер
  9.         ; EBX - длина строки в байтах
  10.         ...
  11.  
  12.         ; Выделить память и записать в нее строку
  13.         invoke  GlobalAlloc,GMEM_FIXED+GMEM_ZEROINIT,ebx
  14.         mov     [hGlobal],eax
  15.  
  16.         invoke  GlobalLock,[hGlobal]
  17.         mov     edi,eax
  18.         mov     esi,szTxt
  19.         mov     ecx,ebx
  20.         rep     movsb
  21.  
  22.         ; Настроить формат данных
  23.         mov     [fmte.cfFormat],CF_UNICODETEXT
  24.         mov     [fmte.lptd],NULL
  25.         mov     [fmte.dwAspect],DVASPECT_CONTENT
  26.         mov     [fmte.lindex],-1
  27.         mov     [fmte.tymed],TYMED_HGLOBAL
  28.  
  29.         mov     [medium.tymed],TYMED_HGLOBAL
  30.         mov     eax,[hGlobal]
  31.         mov     [medium.hGlobal],eax
  32.  
  33.         ; Связать данные с объектом
  34.         mov     eax,[pDataObject]
  35.         mov     eax,[eax]
  36.         stdcall dword [eax+IDataObject.SetData],[pDataObject],fmte,medium,TRUE
  37.  
  38.         ; Установить новое содержимое буфера обмена
  39.         invoke  OleSetClipboard,[pDataObject]
  40.         invoke  OleFlushClipboard
  41.  
  42.         ; Освободить память
  43.         invoke  GlobalUnlock,[hGlobal]
  44.  
  45.         ; Вызвать метод интерфейса IDataObject->Release()
  46.         mov     eax,[pDataObject]
  47.         mov     eax,[eax]
  48.         stdcall dword [eax+IDataObject.Release],[pDataObject]
  49.  
  50.         ; Освободить ресурсы
  51.         invoke  OleUninitialize
При копировании файлов в буфер обмена фактически копируется не их содержимое, а только список. Ориентируясь на этот список, программа уже сама решает, что с ними делать: запускает процесс копирования, переноса, архивирования или выполняет еще какие-то действия с выбранными файлами. Формат списка соответствует структуре DROPFILES, для получения информации о скопированных файлах можно распарсить его самостоятельно, но лучше воспользоваться штатными средствами системы. Код получится такой же, как и при обработке броска файлов на форму.
  1.         ; Инициализация
  2.         invoke  OleInitialize,NULL
  3.  
  4.         ; Прочитать содержимое буфера обмена
  5.         invoke  OleGetClipboard,pDataObject
  6.  
  7.         ; Настроить формат данных
  8.         mov     [fmte.cfFormat],CF_HDROP
  9.         mov     [fmte.lptd],NULL
  10.         mov     [fmte.dwAspect],DVASPECT_CONTENT
  11.         mov     [fmte.lindex],-1
  12.         mov     [fmte.tymed],TYMED_HGLOBAL
  13.  
  14.         ; Прочитать данные из объекта
  15.         mov     eax,[pDataObject]
  16.         mov     eax,[eax]
  17.         stdcall dword [eax+IDataObject.GetData],[pDataObject],fmte,medium
  18.  
  19.         ; Получить адрес содержимого
  20.         invoke  GlobalLock,[medium.hGlobal]
  21.  
  22.         ; Получить количество файлов
  23.         mov     esi,eax
  24.         invoke  DragQueryFile,esi,0FFFFFFFFh,NULL,NULL
  25.         xor     ecx,ecx
  26. @@:
  27.         push    ecx eax
  28.  
  29.         ; Получить скопированные файлы
  30.         invoke  DragQueryFile,esi,ecx,fname,MAX_PATH
  31.  
  32.         ...
  33.         ; fname - имя скопированного файла
  34.         ...
  35.  
  36.         pop     eax ecx
  37.         inc     ecx
  38.         cmp     ecx,eax
  39.         jne     @b
  40.  
  41.         ; Освободить память
  42.         invoke  GlobalUnlock,[medium.hGlobal]
  43.         invoke  ReleaseStgMedium,medium
  44.  
  45.         ; Вызвать метод интерфейса IDataObject->Release()
  46.         mov     eax,[pDataObject]
  47.         mov     eax,[eax]
  48.         stdcall dword [eax+IDataObject.Release],[pDataObject]
  49.  
  50.         ; Освободить ресурсы
  51.         invoke  OleUninitialize
Для копирования файлов в буфер обмена надо самостоятельно заполнить структуру DROPFILES, как мы это делали ранее при эмуляции броска файлов на форму. В этом случае код также получится практически без изменений. Что интересно, в списке могут быть несуществующие файлы, поэтому ответственность за корректность формирования списка лежит исключительно на вашем приложении. Например, Проводник Windows при попытке вставить несуществующий "скопированный" файл, блокирует вообще всю операцию вставки.
  1. file1   du 'D:\Data\test.txt',0
  2. file2   du 'C:\video.avi',0
  3. ...
  4.         ; Инициализация
  5.         invoke  OleInitialize,NULL
  6.  
  7.         ; Создать объект данных
  8.         invoke  SHCreateDataObject,NULL,0,NULL,NULL,IID_IDataObject,pDataObject
  9.  
  10.         ; Размер структуры DROPFILES
  11.         mov     ebx,sizeof.DROPFILES
  12.         inc     ebx
  13.         inc     ebx
  14.  
  15.         ; Длина имени первого файла
  16.         invoke  lstrlen,file1
  17.         inc     eax
  18.         shl     eax,1
  19.         add     ebx,eax
  20.  
  21.         ; Длина имени второго файла
  22.         invoke  lstrlen,file2
  23.         inc     eax
  24.         shl     eax,1
  25.         add     ebx,eax
  26.  
  27.         ; Выделить память и записать в нее строку
  28.         invoke  GlobalAlloc,GMEM_FIXED+GMEM_ZEROINIT,ebx
  29.         mov     [hGlobal],eax
  30.  
  31.         invoke  GlobalLock,[hGlobal]
  32.         mov     edi,eax
  33.  
  34.         ; Заполнить структуру DROPFILES
  35.         mov     [edi+DROPFILES.pFiles],sizeof.DROPFILES
  36.         mov     [edi+DROPFILES.pt.x],0
  37.         mov     [edi+DROPFILES.pt.y],0
  38.         mov     [edi+DROPFILES.fNC],FALSE
  39.         mov     [edi+DROPFILES.fWide],TRUE
  40.         add     edi,sizeof.DROPFILES
  41.  
  42.         ; Дописать к ней передаваемые файлы
  43.         mov     esi,file1
  44. @@:
  45.         lodsw
  46.         stosw
  47.         or      ax,ax
  48.         jnz     @b
  49.  
  50.         mov     esi,file2
  51. @@:
  52.         lodsw
  53.         stosw
  54.         or      ax,ax
  55.         jnz     @b
  56.  
  57.         ; Завершающий нулевой WORD
  58.         xor     eax,eax
  59.         stosw
  60.  
  61.         ; Настроить формат данных
  62.         mov     [fmte.cfFormat],CF_HDROP
  63.         mov     [fmte.lptd],NULL
  64.         mov     [fmte.dwAspect],DVASPECT_CONTENT
  65.         mov     [fmte.lindex],-1
  66.         mov     [fmte.tymed],TYMED_HGLOBAL
  67.  
  68.         mov     [medium.tymed],TYMED_HGLOBAL
  69.         mov     eax,[hGlobal]
  70.         mov     [medium.hGlobal],eax
  71.  
  72.         ; Связать данные с объектом
  73.         mov     eax,[pDataObject]
  74.         mov     eax,[eax]
  75.         stdcall dword [eax+IDataObject.SetData],[pDataObject],fmte,medium,TRUE
  76.  
  77.         ; Установить новое содержимое буфера обмена
  78.         invoke  OleSetClipboard,[pDataObject]
  79.         invoke  OleFlushClipboard
  80.  
  81.         ; Освободить память
  82.         invoke  GlobalUnlock,[hGlobal]
  83.  
  84.         ; Вызвать метод интерфейса IDataObject->Release()
  85.         mov     eax,[pDataObject]
  86.         mov     eax,[eax]
  87.         stdcall dword [eax+IDataObject.Release],[pDataObject]
  88.  
  89.         ; Освободить ресурсы
  90.         invoke  OleUninitialize
По аналогии в буфер обмена можно поместить данные других форматов, например, вот так копируется скриншот рабочего стола.
  1.         ; Инициализация
  2.         invoke  OleInitialize,NULL
  3.  
  4.         ; Создать объект данных
  5.         invoke  SHCreateDataObject,NULL,0,NULL,NULL,IID_IDataObject,pDataObject
  6.  
  7.         ; Сделать скриншот экрана
  8.         invoke  GetDesktopWindow
  9.         mov     [hwnd],eax
  10.         invoke  GetWindowDC,eax
  11.         mov     [hDC],eax
  12.  
  13.         invoke  CreateCompatibleDC,[hDC]
  14.         mov     [memDC],eax
  15.  
  16.         invoke  GetWindowRect,[hwnd],rc
  17.         mov     eax,[rc.right]
  18.         sub     eax,[rc.left]
  19.         mov     ebx,[rc.bottom]
  20.         sub     ebx,[rc.top]
  21.         invoke  CreateCompatibleBitmap,[hDC],eax,ebx
  22.         mov     [hBitmap],eax
  23.         invoke  SelectObject,[memDC],[hBitmap]
  24.         mov     [oldbm],eax
  25.  
  26.         mov     eax,[rc.right]
  27.         sub     eax,[rc.left]
  28.         mov     ebx,[rc.bottom]
  29.         sub     ebx,[rc.top]
  30.  
  31.         invoke  BitBlt,[memDC],0,0,eax,ebx,[hDC],0,0,SRCCOPY
  32.  
  33.         ; Настроить формат данных
  34.         mov     [fmte.cfFormat],CF_BITMAP
  35.         mov     [fmte.lptd],NULL
  36.         mov     [fmte.dwAspect],DVASPECT_CONTENT
  37.         mov     [fmte.lindex],-1
  38.         mov     [fmte.tymed],TYMED_GDI
  39.  
  40.         mov     [medium.tymed],TYMED_GDI
  41.         mov     eax,[hBitmap]
  42.         mov     [medium.hBitmap],eax
  43.         mov     [medium.pUnkForRelease],NULL
  44.  
  45.         ; Связать данные с объектом
  46.         mov     eax,[pDataObject]
  47.         mov     eax,[eax]
  48.         stdcall dword [eax+IDataObject.SetData],[pDataObject],fmte,medium,TRUE
  49.  
  50.         ; Установить новое содержимое буфера обмена
  51.         invoke  OleSetClipboard,[pDataObject]
  52.         invoke  OleFlushClipboard
  53.  
  54.         ; Вызвать метод интерфейса IDataObject->Release()
  55.         mov     eax,[pDataObject]
  56.         mov     eax,[eax]
  57.         stdcall dword [eax+IDataObject.Release],[pDataObject]
  58.  
  59.         ; Освободить ресурсы
  60.         invoke  SelectObject,[memDC],[oldbm]
  61.         invoke  ReleaseDC,[hwnd],[hDC]
  62.  
  63.         ; Освободить ресурсы
  64.         invoke  OleUninitialize
Получив из буфера обмена хэндл картинки, можно, например, вывести ее на форму, сохранить в файл или выполнить другие нужные действия.

Для очистки содержимого буфера обмена вместо функции EmptyClipboard можно использовать следующую конструкцию. Просто так ее применять, конечно, не надо, но если вся работа с буфером обмена в вашей программе уже выполнена на OLE, то нормально.
  1.         ; Инициализация
  2.         invoke  OleInitialize,NULL
  3.         ; Очистить буфера обмена
  4.         invoke  OleSetClipboard,NULL
  5.         ; Освободить ресурсы
  6.         invoke  OleUninitialize
В приложении примеры программ с исходными текстами, которые помещают и извлекают из буфера обмена данные различных форматов.

Примеры программ с исходными текстами (FASM)Примеры программ с исходными текстами (FASM)

Advanced.Clipboard.Demo.zip (14,673 bytes)


Поделиться ссылкой ВКонтакте
Просмотров: 1683 | Комментариев: 0

Внимание! Статья опубликована больше года назад, информация могла устареть!

Комментарии

Отзывы посетителей сайта о статье
Комментариeв нет

Добавить комментарий

Заполните форму для добавления комментария
Имя*:
Текст комментария (не более 2000 символов)*:

*Все поля обязательны для заполнения.
Комментарии, содержащие рекламу, ненормативную лексику, оскорбления и т.п., а также флуд и сообщения не по теме, будут удаляться. Нарушителям может быть заблокирован доступ к сайту.
Наверх
Powered by PCL's Speckled Band Engine 0.2 RC3
© ManHunter / PCL, 2008-2024
При использовании материалов ссылка на сайт обязательна
Время генерации: 0.08 сек. / MySQL: 2 (0.0059 сек.) / Память: 4.5 Mb
Наверх