Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
Рассмотрим процесс работы программы-наблюдателя более подробно. Первое, что ей необходимо сделать – это зарегистрировать своё окно при помощи функции SetClipboardViewer, которая возвращает хэндл текущего наблюдателя и делает текущим наше окно. Как уже говорилось, переданный нам хэндл окна следует сохранить в переменной для дальнейшего использования. Например:
BOOL CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
…
static HWND hNextViewer;
…
hNextViewer = SetClipboardViewer(hDlg);
…
}
Следующий шаг – научить программу реагировать на сообщение WM_DRAWCLIPBOARD. Это очень простое сообщение, никак не использующее параметры wParam и lParam. Как я уже говорил, программа обязана передать это сообщение дальше по цепочке наблюдателей. Выглядит это так.
case WM_DRAWCLIPBOARD:
// Работаем с буфером обмена
if(IsWindow(hNextViewer)) PostMessage(hNextViewer, msg, wParam, lParam);
ПРИМЕЧАНИЕ
В общем случае весьма опасно использовать SendMessage для отправки сообщений чужим окнам. Если приложение, которому принадлежит окно, занято выполнением длительной операции или же просто "зависло" в бесконечном цикле, то в ожидании возврата из SendMessage наше приложение зависнет тоже. Вот почему лучше использовать PostMessage, как это сделано в примере выше, или воспользоваться функциями типа SendMessageTimeout.
Не менее важно правильно удалить себя из очереди наблюдателей за буфером обмена. Для этого используется функция ChangeClipboardChain, которая получает в качестве параметров хэндлы нашего окна и следующего наблюдателя в цепочке, которому мы передаём уведомления. Например:
ChangeClipboardChain(hDlg, hNextViewer);
Функция ChangeClipboardChain посылает текущему наблюдателю сообщение WM_CHANGECBCHAIN, передавая полученные хэндлы через параметры wParam и lParam. Текущий наблюдатель сравнивает wParam с хэндлом следующего наблюдателя, который он хранит в переменной. Если обнаружено совпадение, то он просто сохраняет в качестве HWND следующего наблюдателя значение lParam, тем самым удаляя окно нашего приложения из списка. В противном случае он должен передать WM_CHANGECBCHAIN дальше по цепочке. Вот как выглядит типичный обработчик сообщения WM_CHANGECBCHAIN:
case WM_CHANGECBCHAIN:
if (hNextViewer == (HWND)wParam) hNextViewer = (HWND)lParam;
else if(IsWindow(hNextViewer)) PostMessage(hNextViewer, msg, wParam, lParam);
Пример CbView иллюстрирует все принципы, которые мы только что рассмотрели. Программа CbView добавляет своё окно в цепочку наблюдателей за буфером обмена, когда пользователь устанавливает галочку "Spy clipboard". В ответ на WM_DRAWCLIPBOARD она проверяет содержимое буфера обмена, и если это текст (формат CF_TEXT), загружает его в RichEdit.
В MFC наблюдение за буфером обмена осуществляется по тому же самому принципу. В ней предусмотрены макросы ON_WM_DRAWCLIPBOARD и ON_WM_CHANGECBCHAIN для добавления соответствующих обработчиков в карту сообщений, а также функции SetClipboardViewer и ChangeClipboardChain класса CWnd, соответствующие одноимённым функциям из Win32 API. Программа-пример MfcCbView демонстрирует создание наблюдателя за буфером обмена с использованием MFC.
Все на сегодня. До следующих встреч!
Алекс Jenter [email protected] Красноярск, 2001. Рассылка является частью проекта RSDN.Программирование на Visual C++
Выпуск №44 от 13 мая 2001 г.
Добрый день!
ОБРАТНАЯ СВЯЗЬВ статье "Свойства в C++" в №43 был приведен пример "свойства", который не мог оставить меня равнодушным, как программиста, использующего язык C++ не один год.
Приведенный в статье пример, является забавной комбинацией COM и непреодолимым желанием автора сделать "как в бейсике". Кстати COM, берет начало с OLE и ActiveX, который создавался так, чтобы программистам на VB было как можно проще его использовать. А что хорошо для VB-программистов, то одна головная боль для программистов на C++. Отсюда и возникли добавляемые автоматически префиксы get_ и put_. Первый вариант класса CValue (с использованием declspec) верен, и его, с небольшими изменениями, можно использовать в качестве COM интерфейса. Но для внутреннего использования (т.е. для использования только в C++) он и все последующие мало пригодны (минусы уже были перечислены).
Поэтому, в качестве опровержения некоторых утверждений, приведенных в той статье, предлагаю вашему вниманию свою статью под названием "Эффективное использование C++. Создание классов-оберток для стандартных типов данных".
Я не буду против опубликовании вами этой статьи.
С уважением, Илья Жарков. СТАТЬЯ Эффективное использование C++ Создание классов-оберток для стандартных типов данных Автор: Илья ЖарковБольшое распространение технологии COM и повальное увлечение всех начинающих программистов языками программирования высокого уровня (я имею ввиду Visual Basic & Delphi), приводит к тому, что в массовом сознании закрепляется твердое убеждение, что те средства, которые используются в данных технологиях и языках, является единственно верными и правильными. А что происходит, когда программист "взрослеет"? Он, в погоне за новыми возможностями, устремляется к другим языкам, например к C++. Но тут оказывается, что в этом языке нет привычных ему средств или их реализация не лежит на поверхности. Как всегда в программировании нет времени на детальное изучение возможностей языка (печально, если оно так и не появляется) – программу надо сдать завтра в 8 утра и не часом позже. Вот так и начинается повторное изобретение велосипеда.
Данная статья, я надеюсь, будет полезна программистам, начинающим изучать язык программирования C++, а также тем, кто хочет научиться использовать его возможности наиболее эффективным образом. Тут вы сможете прочитать о создании специальных классов, упрощающих использование стандартных типов данных и называемых "классами-обертками". Такие "классы-обертки" работают подобно нетипизированным переменным языка Visual Basic – производят нужные преобразования из одного типа в другой, а также хранят в себе несколько переменных разного типа.
У каждого поколения программистов возникали проблемы с передачей функциям в качестве параметров большого количества переменных. На языке C эта проблема решалась созданием структуры, содержащей в себе все необходимые параметры. Реализовывалось это так:
struct Value {
int nVal;
char *str;
};
А использовалось следующим образом:
void init(Value* val) {
val.nVal=10;
val.str=(char*)malloc(50);
}
То же самое, конечно, можно написать и на C++, но он предоставляет гораздо более мощное средство под названием класс. Этот пример можно переписать так (забегая немного вперед, скажу, что на практике чаще всего используются классы, подобные этому):
class CValue {
int nVal;
char* str;
public:
CValue() { nVal=0; str=NULL; }
~CValue() { delete[] str; }
CValue& Val(int val) { nVal=val; return *this; }
CValue& Str(const char* string) {
delete[] str;
str=new char[strlen(string)+1];
strcpy(str, string);
return *this;
}
int Val() const { return nVal; }
char* Str() const { return str; }
};
Вы спросите, что нам дает такое, казалось бы, громоздкое повторение предыдущей маленькой структуры. В первую очередь, контроль за значениями, хранящимися в переменных. Мы можем, например, ограничить диапазон переменной nVal значениями от 3 до 11, включив соответствующую проверку в функцию CValue& Val(int val). Благодаря спецификаторам доступа public и private (используемый неявно в начале класса) исключается несанкционированный доступ к переменным класса. Но не менее важно и то, что их значения не примут случайное значение (благодаря конструктору) и не произойдет утечки памяти (благодаря деструктору). Кроме этого очень сильно упрощается использование такой структуры.
CValue value;
// так происходит инициализация
value.Val(10).Str("string");
// так значения используются
int n=value.Val();
char *str=value.Str();
Каскадный вызов функций при инициализации возможен благодаря тому, что они возвращают ссылку на свой экземпляр класса. Это, в случае частого использования, может приводить к значительному ускорению работы программы и к получению более оптимального кода, генерируемого компилятором.
Теперь воспользуемся таким средством C++ как перегрузка операторов и добавим в наш класс следующие функции:
class CValue {
...
public:
CValue& operator=(int val) { return Val(val); }
CValue& operator=(const char* string) { return Str(string); }
operator int() const { return nVal; }
operator char*() const { return str; }
};
Это дало нам еще один способ использования объектов этого типа:
CValue value;