Работа с буфером обмена

  Как-то раз меня посетила мысль о создании глобального помощника в программировании. Что-то вроде автоматизированной вставки заранее подготовленных кусков кода в буфер обмена. Но этой идее не суждено было стать реальности в связи со смутным представлением о результате, посему проект был заморожен. До того момента я еще ни разу не работал с буфером обмена, поэтому решил разобраться, что тут к чему. Собственно, в данной статье я буду вести разговор не о неудавшихся попытках сделать Всея Искусственный Разум, а как раз о обычной работе с буфером обмена.
  Буфер обмена может хранить совершенно любые данные, в том числе и пользовательские, но в данной статье я рассмотрю лишь работу с обычным текстом и совсем чуть-чуть расскажу о формате изображений.
  Для начала рассмотрим процесс чтения обычного текста.
  Естественно, прежде чем что-то делать с буфером, его необходимо открыть, для этого существует соответствующая функция:


	bool	OpenClipboard	( 
		HWND	hwnd		//	Идентификатор окна
 	);
		

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


	bool	CloseClipboard		( void );
		

  Если буфер успешно закрыт, то функция возвращает true, иначе false.
  Итак, следующим шагом является проверка буфера на наличие какой-либо информации. Для этого нужно прощупать буфер специальной функцией, с желаемыми параметрами:


	HANDLE	GetClipboardData	(
		UINT	uFormat		// Формат запрашиваемых данных
	);
		

  В случае успеха функция возвращает идентификатор блока памяти, в котором содержатся сами данные, в противном случае возвращает 0. Иными словами, если данные запрашиваемого типа имеются в буфере, то функция завершается успешно. Параметр uFormat , при запросе обычного текста должен принимать значение CF_TEXT. На самом деле текстовых форматов несколько, например тексту в OEM кодировке соответствует константа CF_OEMTEXT, но рассматривать их в данной статье мы не будем.
  Теперь, если идентификатор не равен нулю, то есть все нормально, нужно узнать размер блока памяти, для этого существует функция:


	UINT	GlobalSize	(
		HANDLE	handle		// Идентификатор блока памяти
	);
		

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


	LPVOID	GlobalLock	(	// Тип LPVOID эквивалентен void *
		HANDLE	handle		// Идентификатор блока памяти
	);
		

  И теперь, остается только скопировать данные куда-нибудь, с помощью например memcpy, где в качестве второго параметра использовать указатель возвращенный функцией GlobalLock, а в качестве третьего - размер блока, полученный с помощью функции GlobalSize. Думаю, о первом параметре говорить не нужно.
  После этого нужно разблокировать блок памяти функцией:


	BOOL	GlobalUnLock	(	
		HANDLE	handle		// Идентификатор блока памяти
	);
		

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


	BOOL	EmptyClipboard	( void );
		

  Функция возвращает true если буфер успешно очищен и else в случае ошибки.
  Для того чтобы записать данные в буфер нужно сначала выделить блок памяти, а потом передать идентификатор блока в качестве параметра соответствующей функции. Выделить блок памяти можно функцией GlobalAlloc.


	HANDLE	GlobalAlloc	( 
		UINT		uFlags		// Атрибуты
		DWORD	dwSize		// Размер выделяемого блока в байтах
 	);
		

  Атрибуты указывают способ выделения памяти, например, если нужно заполнить ее нулями, то следует использовать флаг GMEM_ZEROINIT. Но достаточно просто выделить фиксированный блок памяти, с помощью флага GMEM_FIXED. Следует отметить, что константа GMEM_FIXED равна 0. Этим я воспользовался при написании примера к данной статье, где в качестве параметра передавал значение NULL. Вообще же, при фиксированном блоке памяти, вызов функции GlobalLock нужен скорее в эстетических целях, для получения указателя на блок.
  Далее нужно скопировать данные с помощью все той же memcpy.
  Логично предположить, что раз есть функция GetClipboardData, то есть и функция SetClipboardData, такое предположение, в данном случае, верно.


	HANDLE	SetClipboardData	(
		UINT	uFormat		// Формат запрашиваемых данных
		HANDLE	handle		// Идентификатор блока памяти
	);
		

  Формат нужно указать текстовый - CF_TEXT, а в качестве handle передать идентификатор блока выделенной памяти.
  Это был простейший случай работы с буфером обмена, на примере обычного текста. Но этой информации должно хватить для понятия основных принципов.
  Я не буду вдаваться в подробности считывания и записи изображений, алгоритм тот же что и с текстом, скажу лишь, что формат изображений CF_DIB. По сути это обычный bmp файл, но без самого первого заголовка. У bmp файла есть два заголовка BmpHeader и BmpInfoHeader, подробнее можно посмотреть в файле Image.h, в прилагаемом к статье архиву. Так вот, данные в формате CF_DIB начинаются с BmpInfoHeader и идут до последнего байта картинки, поэтому первый заголовок придется писать вручную. Вообще данная информация является неполной и может быть даже ошибочна, ибо получена методом тыка, но это работает.
  А теперь немного о прилагающейся программе и исходниках. При нажатии комбинации Ctrl + 1 на экран выводится текстовая информация, из буфера, если таковая имеется. При нажатии комбинации Ctrl + 2 буфер заполняется текстовой строкой «Hello Clipboard!». При нажатии комбинации Ctrl + 3 в файл, с именем «Image.bmp», сохраняется картинка из буфера обмена, если она там есть. И, наконец, при нажатии комбинации Ctrl + 4 в буфер записывается картинка из файла «Grey.bmp», который поставляется с архивом.
  На этом все. Скачать программу и исходный код к статье можно З Д Е С Ь

Статью написал faceH0r 22.01.2008
Используются технологии uCoz