Часто в приложениях нужно определить, какая кнопка мыши была нажата, где была нажата, как долго удерживалась, когда и где была отпущена.
Visual C++, отслеживать события мыши и регировать на нихОперационная система Windows генерирует достаточное количество сообщений о событиях, связанных с мышью, чтобы строить гибкие приложения, позволяющие решать практически любые задачи. Если выделить диалоговую форму и во вкладке Properties (Свойства) щелкнуть по кнопке Messages (Сообщения), то в прокручивающемся списке можно обнаружить следующие сообщения для мыши
| Сообщения о событиях мыши | |
|---|---|
| Сообщение | Описание |
WM_LBUTTONDOWN |
Левая кнопка мыши нажата |
WM_LBUTTONUP |
Левая кнопка мыши отпущена |
WM_LBUTTONDBLCLK |
Двойной щелчок левой кнопкой мыши |
WM_RBUTTONDOWN |
Правая кнопка мыши нажата |
WM_RBUTTONUP |
Правая кнопка мыши отпущена |
WM_RBUTTONDBLCLK |
Двойной щелчок правой кнопкой мыши |
WM_MBUTTONDOWN |
Средняя кнопка мыши нажата (в случае трехкнопочной мыши) |
WM_MBUTTONUP |
Средняя кнопка мыши отпущена (в случае трехкнопочной мыши) |
WM_MBUTTONDBLCLK |
Двойной щелчок средней кнопкой мыши (в случае трехкнопочной мыши) |
WM_MOUSEMOVE |
Перемещение указателя мыши в окне приложения |
WM_MOUSEWHEEL |
Колесо мыши прокручивается |
Создайте новый проект с именем
Application Type Application TypeDialog BasedResource language:Project style:MFC standard Use of MFC:Use MFC in a shared DLLAdvanced Features User Interface Features Main frame styles:System menu About boxDialog title:Mouse and Keyboard
Русифицируйте ресурсы узлов Dialog и String Table через панель Resource View
Раскройте ресурс IDD_MOUSE_DIALOG. Через меню Edit/Select All выделите и удалите все элементы управления из диалогового окна приложения
Тем самым вся поверхность окна будет отведена для рисования. Если этого на сделать, то любому из оставшихся элементов управления, получившему фокус ввода, диалоговое окно будет передавать события клавиатуры как своему дочернему окну.
Выделите диалоговое окно приложения и во вкладке Properties щелкните по кнопке Messages. В открывшейся вкладке сообщений найдите сообщение WM_MOUSEMOVE и обработайте ее следующим кодомОбработчик сообщения WM_MOUSEMOVE файла MouseDlg.cpp
void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Что сделать: Добавьте свой код обработчика сообщения
// и/или вызовите заданный по умолчанию
// Выяснить, нажата ли левая кнопка мыши
if((nFlags MK_LBUTTON) == MK_LBUTTON){
// Создать объект контекста устройства
CClientDC dc(this);
// Нарисовать точку в текущей позиции курсора
dc.SetPixel(point.x,
point.y,
RGB(0, 0, 0));
}
CDialog::OnMouseMove(nFlags, point);
}
Контекст устройства - это некий логический экран, обеспечивающий согласованною связь с драйверами конкретных устройств вывода: физического экрана или принтера. Контекст устройства как класс имеет необходимые методы и свойства для рисования. Конструктору этого класса при создании объекта контекста устройства передается указатель this на само диалоговое окно и областью рисования будет считаться клиентская область этого диалогового окна.
В коде применяются маски выделения флага о состоянии кнопок мыши и
| Маски выделения состояний кнопок и клавиш | |
|---|---|
| Маска | Описание |
MK_CONTROL |
Нажата клавиша CTRL |
MK_LBUTTON |
Нажата левая кнопка мыши |
MK_MBUTTON |
Нажата средняя кнопка мыши (в случае трехкнопочной мыши) |
MK_RBUTTON |
Нажата правая кнопка мыши |
MK_SHIFT |
Нажата клавиша SHIFT |
Постройте и выполните проектОбратите внимание, что при быстром движении мыши линия рисования имеет разрывы. Дело в том, что при выполнении функции OnMouseMove() параметр point показывает координаты курсора мыши во время последней проверки местоположения, а сама функция выполняется не непрерывно, а по квантам времени. Между этими квантами процессор выполняет другие задачи.
Все графические редакторы из-за перегруженности процессора рисуют непрерывные кривые соединением отдельных точек местоположения мыши отрезками прямых. Реализуем это.
Перейдите на вкладку Class View
Выберите класс диалогового окна CMouseDlg, производный от MFC -класса CDialog
Щелкните правой кнопкой мыши и выберите из контекстного меню Add/Add Variables...
В диалоговом окне
То же самое повторите для создания переменной m_iPrevY
Мастер поместил объявления переменных в объявление класса CMouseDlg, создав код
Объявления переменных-членов класса CMouseDlg файла MouseDlg.h
// CMouseDlg dialog
class CMouseDlg : public CDialog
{
.......
private:
// Предыдущая координата курсора
int m_iPrevX;
int m_iPrevY;
};
Задайте инициализацию этих переменных в обработчике, выполняемом при каждом нажатии левой кнопки мыши. Для этого выделите диалоговое окно в визуальном редакторе диалоговых окон
Во вкладке Properties установите режим Messages и обработайте сообщение WM_LBUTTONDOWN кодом
Обработчик нажатия левой кнопки мыши в начале рисования каждой новой линии
void CMouseDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Установить текущую точку в качестве исходной
m_iPrevX = point.x;
m_iPrevY = point.y;
CDialog::OnLButtonDown(nFlags, point);
}
Измените код функции-обработчика OnMouseMove() так, чтобы рисовались не точки, а линии
Обработчик перемещения мыши при нажатой левой кнопке
void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Что сделать: Добавьте свой код обработчика сообщения
// и/или вызовите заданный по умолчанию
// Выяснить, нажата ли левая кнопка мыши
if((nFlags MK_LBUTTON) == MK_LBUTTON){
// Создать объект контекста устройства
CClientDC dc(this);
// Нарисовать точку в текущей позиции курсора
// dc.SetPixel(point.x,
// point.y,
// RGB(0, 0, 0));
// Провести линию от предыдущей точки до текущей
dc.MoveTo(m_iPrevX, m_iPrevY); // поместить графический курсор
dc.LineTo(point.x, point.y); // рисовать до текущей
// Обновить предыдущую точку для нового кванта времени
m_iPrevX = point.x;
m_iPrevY = point.y;
}
CDialog::OnMouseMove(nFlags, point);
}
Постройте проект и убедитесь, что получили то, что нужно
Измените код функции-обработчика OnMouseMove() так, чтобы линии рисовались толщиной 10 пикселов и красным цветом
Линия в 10 пикселов красного цвета
void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Что сделать: Добавьте свой код обработчика сообщения
// и/или вызовите заданный по умолчанию
// Выяснить, нажата ли левая кнопка мыши
if((nFlags MK_LBUTTON) == MK_LBUTTON){
// Создать объект контекста устройства
CClientDC dc(this);
// Создать новое перо (solid, 10 pixels, red)
CPen newPen(PS_SOLID,
10,
RGB(255, 0, 0));
// Установить новое перо текущим
dc.SelectObject(newPen);
// Нарисовать точку в текущей позиции курсора
// dc.SetPixel(point.x,
// point.y,
// RGB(0, 0, 0));
// Провести линию от предыдущей точки до текущей
dc.MoveTo(m_iPrevX, m_iPrevY); // поместить графический курсор
dc.LineTo(point.x, point.y); // рисовать до текущей
// Обновить предыдущую точку для нового кванта времени
m_iPrevX = point.x;
m_iPrevY = point.y;
}
CDialog::OnMouseMove(nFlags, point);
}
Постройте проект и убедитесь, что результат будет, примерно, таким
Событий для клавиатуры меньше, чем для мыши. Но существуют много действий, которые могут быть выполнены только при помощи клавиатуры.
| Сообщения о событиях клавиатуры | |
|---|---|
| Сообщение | Описание |
WM_KEYDOWN |
Клавиша нажата |
WM_KEYUP |
Клавиша отпущена |
WM_SYSKEYDOWN |
Нажата клавиша F10 или Alt в сочетании с другой клавишей |
WM_SYSKEYUP |
Отпущена клавиша F10 или Alt в сочетании с другой клавишей |
Сообщения о событиях клавиатуры доступны объекту самого диалогового окна только тогда, когда ни один его дочерний элемент управления не имеет фокуса ввода.
Сделаем так, чтобы при нажатии на определенные клавиши курсор менял свою форму по схеме
| Смена формы курсора | |
|---|---|
| Нажата клавиша | Действие |
| Символ "A" | Загрузить курсор в виде стрелки |
| Символ "B" | Загрузить курсор в виде текстового указателя |
| Символ "C" | Загрузить курсор в виде песочных часов |
| Символ "E" | Выйти из приложения |
Выделите диалоговое окно и в панели Properties обработайте сообщение WM_KEYDOWN следующим кодом
Перехват клавиш для изменения курсора
void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
char ch; // текущий нажимаемый символ
HCURSOR hCursor = 0; // дескриптор курсора, который будет отображен
HCURSOR hPrevCursor = 0; // дескриптор предыдущего курсора
// Преобразовать нажатую клавишу в символ
ch = char(nChar); // символ
bool endFlag = false; // Выход
switch(ch){
// Курсор в виде стрелки
case 'A': // латинская заглавная
hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
break;
// Курсор в виде текстового указателя
case 'B': // латинская заглавная
hCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);
break;
// Курсор в форме песочных часов
case 'C': // латинская заглавная
hCursor = AfxGetApp()->LoadStandardCursor(IDC_WAIT);
break;
// Восстановить курсор в виде стрелки
case 'E': // латинская заглавная
hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
endFlag = true;
}
hPrevCursor = hCursor ? SetCursor(hCursor) : 0;
if(hCursor){ // Уничтожить предыдущий курсор для освобождения ресурсов
DestroyCursor(hPrevCursor);
hCursor = 0;
}
if(endFlag)
OnOK();
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}
Функция AfxGetApp() является глобальной и возвращает указатель на экземпляр класса текущего приложения. В исходном коде приложения существует глобальная переменная класса приложения theApp, но явно ссылаться на нее нельзя, а нужно пользоваться функцией AfxGetApp() всякий раз, когда нужно вызвать объект приложения.
Постройте проект и убедитесь, что курсор меняет форму
Но если двинуть мышь - курсор восстанавливается. Это происходит потому, что каждый раз, когда нужно перерисовать указатель мыши, приложению направляется сообщение WM_SETCURSOR, по которому восстанавливается курсор по умолчанию. Если блокировать это сообщение, то будет перерисовываться установленный текущий курсор. Введем логическую переменную-флаг, информирующую о том, что курсор был изменен.
Для этого
Выделите класс CMouseDlg и вызовите мастер Add Member Variable Wizard
В диалоговом окне Add Member Variable Wizard заполните поля так, как указано на рисунке

Получится код
Объявление переменной-флага
// CMouseDlg dialog
class CMouseDlg : public CDialog
{
.....................
private:
// Флаг, что курсор был изменен
bool m_bMyCursor;
};
В функции начальной инициализации OnInitDialog() добавьте кодИнициализация переменной-флага
BOOL CMouseDlg::OnInitDialog()
{
.................................
// TODO: Add extra initialization here
// Инициализировать курсор по умолчанию
m_bMyCursor = false;
return TRUE; // return TRUE unless you set the focus to a control
}
Скорректируйте функцию-обработчик OnKeyDown()Поднимем переменную-флаг
void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
........................................
hPrevCursor = hCursor ? SetCursor(hCursor) : 0;
if(hCursor){// Уничтожить предыдущий курсор для освобождения ресурсов
DestroyCursor(hPrevCursor);
hCursor = 0;
m_bMyCursor = true; // Курсор изменили
}
if(endFlag)
OnOK();
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}
Обработайте сообщение WM_SETCURSOR такОбработчик сообщения WM_SETCURSOR
BOOL CMouseDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default
if(m_bMyCursor)
return TRUE; // Выйти без выполнения нижнего кода
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
Постройте приложение и проверьте его работоспособностьOnKeyDown, добавив в нее следующие стандартные формы курсораIDC_CROSS IDC_UPARROW IDC_SIZEALL IDC_SIZENWSE IDC_SIZENESW IDC_SIZEWE IDC_SIZENS IDC_NO IDC_APPSTARTING IDC_HELP 
Часто в приложениях нужно определить, какая кнопка мыши была нажата, где была нажата, как долго удерживалась, когда и где была отпущена.
Visual C++, отслеживать события мыши и регировать на нихОперационная система Windows генерирует достаточное количество сообщений о событиях, связанных с мышью, чтобы строить гибкие приложения, позволяющие решать практически любые задачи. Если выделить диалоговую форму и во вкладке Properties (Свойства) щелкнуть по кнопке Messages (Сообщения), то в прокручивающемся списке можно обнаружить следующие сообщения для мыши
| Сообщения о событиях мыши | |
|---|---|
| Сообщение | Описание |
WM_LBUTTONDOWN |
Левая кнопка мыши нажата |
WM_LBUTTONUP |
Левая кнопка мыши отпущена |
WM_LBUTTONDBLCLK |
Двойной щелчок левой кнопкой мыши |
WM_RBUTTONDOWN |
Правая кнопка мыши нажата |
WM_RBUTTONUP |
Правая кнопка мыши отпущена |
WM_RBUTTONDBLCLK |
Двойной щелчок правой кнопкой мыши |
WM_MBUTTONDOWN |
Средняя кнопка мыши нажата (в случае трехкнопочной мыши) |
WM_MBUTTONUP |
Средняя кнопка мыши отпущена (в случае трехкнопочной мыши) |
WM_MBUTTONDBLCLK |
Двойной щелчок средней кнопкой мыши (в случае трехкнопочной мыши) |
WM_MOUSEMOVE |
Перемещение указателя мыши в окне приложения |
WM_MOUSEWHEEL |
Колесо мыши прокручивается |
Создайте новый проект с именем
Application Type Application TypeDialog BasedResource language:Project style:MFC standard Use of MFC:Use MFC in a shared DLLAdvanced Features User Interface Features Main frame styles:System menu About boxDialog title:Mouse and Keyboard
Русифицируйте ресурсы узлов Dialog и String Table через панель Resource View
Раскройте ресурс IDD_MOUSE_DIALOG. Через меню Edit/Select All выделите и удалите все элементы управления из диалогового окна приложения
Тем самым вся поверхность окна будет отведена для рисования. Если этого на сделать, то любому из оставшихся элементов управления, получившему фокус ввода, диалоговое окно будет передавать события клавиатуры как своему дочернему окну.
Выделите диалоговое окно приложения и во вкладке Properties щелкните по кнопке Messages. В открывшейся вкладке сообщений найдите сообщение WM_MOUSEMOVE и обработайте ее следующим кодомОбработчик сообщения WM_MOUSEMOVE файла MouseDlg.cpp
void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Что сделать: Добавьте свой код обработчика сообщения
// и/или вызовите заданный по умолчанию
// Выяснить, нажата ли левая кнопка мыши
if((nFlags MK_LBUTTON) == MK_LBUTTON){
// Создать объект контекста устройства
CClientDC dc(this);
// Нарисовать точку в текущей позиции курсора
dc.SetPixel(point.x,
point.y,
RGB(0, 0, 0));
}
CDialog::OnMouseMove(nFlags, point);
}
Контекст устройства - это некий логический экран, обеспечивающий согласованною связь с драйверами конкретных устройств вывода: физического экрана или принтера. Контекст устройства как класс имеет необходимые методы и свойства для рисования. Конструктору этого класса при создании объекта контекста устройства передается указатель this на само диалоговое окно и областью рисования будет считаться клиентская область этого диалогового окна.
В коде применяются маски выделения флага о состоянии кнопок мыши и
| Маски выделения состояний кнопок и клавиш | |
|---|---|
| Маска | Описание |
MK_CONTROL |
Нажата клавиша CTRL |
MK_LBUTTON |
Нажата левая кнопка мыши |
MK_MBUTTON |
Нажата средняя кнопка мыши (в случае трехкнопочной мыши) |
MK_RBUTTON |
Нажата правая кнопка мыши |
MK_SHIFT |
Нажата клавиша SHIFT |
Постройте и выполните проектОбратите внимание, что при быстром движении мыши линия рисования имеет разрывы. Дело в том, что при выполнении функции OnMouseMove() параметр point показывает координаты курсора мыши во время последней проверки местоположения, а сама функция выполняется не непрерывно, а по квантам времени. Между этими квантами процессор выполняет другие задачи.
Все графические редакторы из-за перегруженности процессора рисуют непрерывные кривые соединением отдельных точек местоположения мыши отрезками прямых. Реализуем это.
Перейдите на вкладку Class View
Выберите класс диалогового окна CMouseDlg, производный от MFC -класса CDialog
Щелкните правой кнопкой мыши и выберите из контекстного меню Add/Add Variables...
В диалоговом окне
То же самое повторите для создания переменной m_iPrevY
Мастер поместил объявления переменных в объявление класса CMouseDlg, создав код
Объявления переменных-членов класса CMouseDlg файла MouseDlg.h
// CMouseDlg dialog
class CMouseDlg : public CDialog
{
.......
private:
// Предыдущая координата курсора
int m_iPrevX;
int m_iPrevY;
};
Задайте инициализацию этих переменных в обработчике, выполняемом при каждом нажатии левой кнопки мыши. Для этого выделите диалоговое окно в визуальном редакторе диалоговых окон
Во вкладке Properties установите режим Messages и обработайте сообщение WM_LBUTTONDOWN кодом
Обработчик нажатия левой кнопки мыши в начале рисования каждой новой линии
void CMouseDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Установить текущую точку в качестве исходной
m_iPrevX = point.x;
m_iPrevY = point.y;
CDialog::OnLButtonDown(nFlags, point);
}
Измените код функции-обработчика OnMouseMove() так, чтобы рисовались не точки, а линии
Обработчик перемещения мыши при нажатой левой кнопке
void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Что сделать: Добавьте свой код обработчика сообщения
// и/или вызовите заданный по умолчанию
// Выяснить, нажата ли левая кнопка мыши
if((nFlags MK_LBUTTON) == MK_LBUTTON){
// Создать объект контекста устройства
CClientDC dc(this);
// Нарисовать точку в текущей позиции курсора
// dc.SetPixel(point.x,
// point.y,
// RGB(0, 0, 0));
// Провести линию от предыдущей точки до текущей
dc.MoveTo(m_iPrevX, m_iPrevY); // поместить графический курсор
dc.LineTo(point.x, point.y); // рисовать до текущей
// Обновить предыдущую точку для нового кванта времени
m_iPrevX = point.x;
m_iPrevY = point.y;
}
CDialog::OnMouseMove(nFlags, point);
}
Постройте проект и убедитесь, что получили то, что нужно
Измените код функции-обработчика OnMouseMove() так, чтобы линии рисовались толщиной 10 пикселов и красным цветом
Линия в 10 пикселов красного цвета
void CMouseDlg::OnMouseMove(UINT nFlags, CPoint point)
{
// TODO: Add your message handler code here and/or call default
// Что сделать: Добавьте свой код обработчика сообщения
// и/или вызовите заданный по умолчанию
// Выяснить, нажата ли левая кнопка мыши
if((nFlags MK_LBUTTON) == MK_LBUTTON){
// Создать объект контекста устройства
CClientDC dc(this);
// Создать новое перо (solid, 10 pixels, red)
CPen newPen(PS_SOLID,
10,
RGB(255, 0, 0));
// Установить новое перо текущим
dc.SelectObject(newPen);
// Нарисовать точку в текущей позиции курсора
// dc.SetPixel(point.x,
// point.y,
// RGB(0, 0, 0));
// Провести линию от предыдущей точки до текущей
dc.MoveTo(m_iPrevX, m_iPrevY); // поместить графический курсор
dc.LineTo(point.x, point.y); // рисовать до текущей
// Обновить предыдущую точку для нового кванта времени
m_iPrevX = point.x;
m_iPrevY = point.y;
}
CDialog::OnMouseMove(nFlags, point);
}
Постройте проект и убедитесь, что результат будет, примерно, таким
Событий для клавиатуры меньше, чем для мыши. Но существуют много действий, которые могут быть выполнены только при помощи клавиатуры.
| Сообщения о событиях клавиатуры | |
|---|---|
| Сообщение | Описание |
WM_KEYDOWN |
Клавиша нажата |
WM_KEYUP |
Клавиша отпущена |
WM_SYSKEYDOWN |
Нажата клавиша F10 или Alt в сочетании с другой клавишей |
WM_SYSKEYUP |
Отпущена клавиша F10 или Alt в сочетании с другой клавишей |
Сообщения о событиях клавиатуры доступны объекту самого диалогового окна только тогда, когда ни один его дочерний элемент управления не имеет фокуса ввода.
Сделаем так, чтобы при нажатии на определенные клавиши курсор менял свою форму по схеме
| Смена формы курсора | |
|---|---|
| Нажата клавиша | Действие |
| Символ "A" | Загрузить курсор в виде стрелки |
| Символ "B" | Загрузить курсор в виде текстового указателя |
| Символ "C" | Загрузить курсор в виде песочных часов |
| Символ "E" | Выйти из приложения |
Выделите диалоговое окно и в панели Properties обработайте сообщение WM_KEYDOWN следующим кодом
Перехват клавиш для изменения курсора
void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
char ch; // текущий нажимаемый символ
HCURSOR hCursor = 0; // дескриптор курсора, который будет отображен
HCURSOR hPrevCursor = 0; // дескриптор предыдущего курсора
// Преобразовать нажатую клавишу в символ
ch = char(nChar); // символ
bool endFlag = false; // Выход
switch(ch){
// Курсор в виде стрелки
case 'A': // латинская заглавная
hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
break;
// Курсор в виде текстового указателя
case 'B': // латинская заглавная
hCursor = AfxGetApp()->LoadStandardCursor(IDC_IBEAM);
break;
// Курсор в форме песочных часов
case 'C': // латинская заглавная
hCursor = AfxGetApp()->LoadStandardCursor(IDC_WAIT);
break;
// Восстановить курсор в виде стрелки
case 'E': // латинская заглавная
hCursor = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
endFlag = true;
}
hPrevCursor = hCursor ? SetCursor(hCursor) : 0;
if(hCursor){ // Уничтожить предыдущий курсор для освобождения ресурсов
DestroyCursor(hPrevCursor);
hCursor = 0;
}
if(endFlag)
OnOK();
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}
Функция AfxGetApp() является глобальной и возвращает указатель на экземпляр класса текущего приложения. В исходном коде приложения существует глобальная переменная класса приложения theApp, но явно ссылаться на нее нельзя, а нужно пользоваться функцией AfxGetApp() всякий раз, когда нужно вызвать объект приложения.
Постройте проект и убедитесь, что курсор меняет форму
Но если двинуть мышь - курсор восстанавливается. Это происходит потому, что каждый раз, когда нужно перерисовать указатель мыши, приложению направляется сообщение WM_SETCURSOR, по которому восстанавливается курсор по умолчанию. Если блокировать это сообщение, то будет перерисовываться установленный текущий курсор. Введем логическую переменную-флаг, информирующую о том, что курсор был изменен.
Для этого
Выделите класс CMouseDlg и вызовите мастер Add Member Variable Wizard
В диалоговом окне Add Member Variable Wizard заполните поля так, как указано на рисунке

Получится код
Объявление переменной-флага
// CMouseDlg dialog
class CMouseDlg : public CDialog
{
.....................
private:
// Флаг, что курсор был изменен
bool m_bMyCursor;
};
В функции начальной инициализации OnInitDialog() добавьте кодИнициализация переменной-флага
BOOL CMouseDlg::OnInitDialog()
{
.................................
// TODO: Add extra initialization here
// Инициализировать курсор по умолчанию
m_bMyCursor = false;
return TRUE; // return TRUE unless you set the focus to a control
}
Скорректируйте функцию-обработчик OnKeyDown()Поднимем переменную-флаг
void CMouseDlg::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
........................................
hPrevCursor = hCursor ? SetCursor(hCursor) : 0;
if(hCursor){// Уничтожить предыдущий курсор для освобождения ресурсов
DestroyCursor(hPrevCursor);
hCursor = 0;
m_bMyCursor = true; // Курсор изменили
}
if(endFlag)
OnOK();
CDialog::OnKeyDown(nChar, nRepCnt, nFlags);
}
Обработайте сообщение WM_SETCURSOR такОбработчик сообщения WM_SETCURSOR
BOOL CMouseDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
// TODO: Add your message handler code here and/or call default
if(m_bMyCursor)
return TRUE; // Выйти без выполнения нижнего кода
return CDialog::OnSetCursor(pWnd, nHitTest, message);
}
Постройте приложение и проверьте его работоспособностьOnKeyDown, добавив в нее следующие стандартные формы курсораIDC_CROSS IDC_UPARROW IDC_SIZEALL IDC_SIZENWSE IDC_SIZENESW IDC_SIZEWE IDC_SIZENS IDC_NO IDC_APPSTARTING IDC_HELP 
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.