Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Роббинс Д. - Отладка приложений для Microsoft .NET и Microsoft Windows - 2004

.pdf
Скачиваний:
353
Добавлен:
13.08.2013
Размер:
3.3 Mб
Скачать

ГЛАВА 16

Автоматизированное тестирование

593

 

 

 

 

 

 

 

 

 

 

 

 

case ANTS_ANYLOCMATCH

:

 

 

if ( NULL !=

_tcsstr ( szTitle

,

 

 

 

g_shared_NotifyData[i].szTitle ))

 

{

 

 

 

 

bMatch =

TRUE ;

 

 

 

}

 

 

 

 

break ;

 

 

 

 

default

 

:

 

 

TRACE ( _T (

"CheckTableMatch invalid " ) \

 

 

_T (

"search type!!!\n" ) ) ;

 

 

ReleaseMutex

( g_hMutex ) ;

 

 

return ;

 

 

 

 

break ;

 

 

 

 

}

// Ну, и каковы результаты? if ( TRUE == bMatch )

{

//Если это уведомление об уничтожении окна,

//соответствующее поле получает значение "1". if ( ANTN_DESTROYWINDOW == iNotifyType )

{

g_shared_NotifyData[ i ].bDestroy = TRUE ;

}

else

{

// Иначе устанавливается значение HWND. g_shared_NotifyData[ i ].hWndCreate = hWnd ;

}

}

}

}

ReleaseMutex ( g_hMutex ) ;

}

 

 

LRESULT CALLBACK CallWndRetProcHook ( int

nCode

,

WPARAM

wParam ,

LPARAM

lParam

)

{

 

 

//Буфер для хранения заголовка окна TCHAR szBuff[ MAX_PATH ] ;

//Прежде чем что либо сделать, я всегда передаю сообщение

//следующей ловушке, чтобы не забыть сделать это потом.

//Передав сообщение, я могу спокойно заняться своими делами. LRESULT lRet = CallNextHookEx ( g_hHook , nCode , wParam , lParam );

//В документации запрещается обрабатывать сообщение при

//отрицательных значениях параметра nCode. Не будем спорить.

if ( nCode < 0 )

{

см. след. стр.

594 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

return ( lRet ) ;

}

//Получение структуры сообщения. Интересно, зачем нужны три

//(или больше) различных структуры сообщений? Почему нельзя

//было использовать структуру MSG для всех сообщений/ловушек? PCWPRETSTRUCT pMsg = (PCWPRETSTRUCT)lParam ;

//Нет заголовка — нет работы.

LONG lStyle = GetWindowLong ( pMsg >hwnd , GWL_STYLE ) ; if ( WS_CAPTION != ( lStyle & WS_CAPTION ) )

{

return ( lRet ) ;

}

//Сообщения WM_DESTROY используются и диалоговыми, и обычными

//окнами. Нужно просто получить заголовок окна и проверить

//наличие соответствующего элемента в таблице уведомлений.

if ( WM_DESTROY == pMsg >message )

{

if ( 0 != GetWindowText ( pMsg >hwnd , szBuff , MAX_PATH ) )

{

CheckTableMatch ( ANTN_DESTROYWINDOW , pMsg >hwnd , szBuff ) ;

}

return ( lRet ) ;

}

//С созданием окна все не так просто, как с его уничтожением.

//Получение класса окна. Если это подлинное диалоговое

//окно, мне нужно только сообщение WM_INITDIALOG.

if ( 0 ==

GetClassName ( pMsg >hwnd , szBuff , MAX_PATH ) )

 

{

 

 

#ifdef _DEBUG

 

 

TCHAR

szBuff[ 50 ] ;

 

wsprintf ( szBuff

,

 

_T ( "GetClassName failed for HWND : 0x%08X\n" ) ,

 

pMsg >hwnd

) ;

TRACE

( szBuff ) ;

 

#endif

 

 

// Продолжение не имеет смысла. return ( lRet ) ;

}

if ( 0 == lstrcmpi ( szBuff , _T ( "#32770" ) ) )

{

// Мне нужно проверять только сообщение WM_INITDIALOG. if ( WM_INITDIALOG == pMsg >message )

{

// Получение заголовка диалогового окна.

if ( 0 != GetWindowText ( pMsg >hwnd , szBuff , MAX_PATH ) )

ГЛАВА 16 Автоматизированное тестирование

595

 

 

 

 

 

 

 

 

 

{

 

 

CheckTableMatch ( ANTN_CREATEWINDOW

,

 

pMsg >hwnd

,

 

szBuff

) ;

 

}

 

 

}

 

 

return ( lRet ) ;

 

 

}

 

 

// Я разобрался с диалоговыми окнами. Теперь

 

 

// мне нужно позаботиться о других окнах.

 

 

if ( WM_CREATE == pMsg >message )

 

 

{

 

 

//Очень немногие окна устанавливают заголовок

//при обработке сообщения WM_CREATE. Однако некоторые

//поступают именно так, и они не используют WM_SETTEXT,

//поэтому я должен выполнить соответствующую проверку.

if ( 0 != GetWindowText ( pMsg >hwnd , szBuff , MAX_PATH ) )

{

CheckTableMatch ( ANTN_CREATEWINDOW

,

pMsg >hwnd

,

szBuff

) ;

}

}

else if ( WM_SETTEXT == pMsg >message )

{

//Я всегда обрабатываю WM_SETTEXT, поскольку именно так

//программы устанавливают заголовки. К сожалению, похоже,

//некоторые приложения, такие как Internet Explorer, вызывают

//WM_SETTEXT несколько раз с одним заголовком. Чтобы не усложнять

//эту функцию, я просто сообщаю WM_SETTEXT вместо поддержки

//странных, тяжелых в отладке структур данных, необходимых

//для слежения за окнами, которые уже вызывали WM_SETTEXT раньше. if ( NULL != pMsg >lParam )

{

CheckTableMatch ( ANTN_CREATEWINDOW

,

pMsg >hwnd

,

(LPCTSTR)pMsg >lParam

) ;

}

}

return ( lRet ) ;

}

Некоторые аспекты реализации TNotify казались довольно сложными, поэто! му я был приятно удивлен тем, как мало проблем я испытал на самом деле. Если вы хотите усовершенствовать код функции!ловушки, знайте, что отлаживать сис! темные ловушки очень непросто. Для этого лучше всего использовать удаленную отладку (см. главу 5). Еще один способ отладки системных ловушек заключается в отладке в стиле printf. Программа DebugView, которую можно загрузить с сайта

596 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

www.sysinternals.com, позволит вам видеть все вызовы OutputDebugString, указыва! ющие на состояние вашей ловушки.

Реализация TESTREC.EXE

После создания Tester DLL мне нужно было разработать программу TESTREC.EXE, которая записывала бы события клавиатуры и мыши. Если дело касается записи вводимой информации, в ОС Windows, есть только один чистый способ сделать это: использовать ловушку записи журнала. В ловушках записи журнала нет ни! чего увлекательного, кроме проблемы правильной обработки сообщения WM_CAN

 

 

 

 

Начальное

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

состояние

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Клавиша

 

Любой ввод

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

отпущена

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Нажата

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Menu

 

 

 

 

 

 

Нажата

 

 

 

 

 

 

 

 

 

 

 

 

Тип ввода

 

VK_MENU

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

или Control

 

 

 

 

 

VK_CONTROL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

или VK_CONTROL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Клавиша

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

отпущена

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Нажата

 

 

 

Клавиша

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

VK_MENU

 

 

отпущена

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Состояние

 

 

 

 

 

 

 

 

 

 

Состояние

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

IsTabKey

 

 

 

 

 

 

 

 

 

 

CheckBreak

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Любой ввод

 

 

 

 

 

 

 

 

 

 

 

Любой ввод

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Тип ввода

 

 

 

 

 

 

 

 

 

 

Тип ввода

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Клавиша нажата

 

 

 

 

 

 

 

Клавиша нажата

 

 

 

 

 

 

 

 

 

 

 

 

Нажата

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

обычная клавиша

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Клавиша

 

 

 

 

 

 

 

 

 

Клавиша

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

VK_TAB

 

 

 

 

 

 

 

 

 

 

VK_CANCEL

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Состояние

 

 

Любая

 

 

 

 

Прервать

 

 

Любая

 

 

 

 

 

 

 

 

 

 

 

 

другая клавиша

 

 

 

другая клавиша

 

 

 

 

 

 

 

 

 

 

 

табуляции

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

запись

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Состояние

обычной

клавиши

Рис. 16 2. Конечные автоматы начала записи событий клавиатуры,

обработки клавиши Tab и комбинации Ctrl+Break

ГЛАВА 16 Автоматизированное тестирование

597

 

 

CELJOURNAL. Когда пользователь нажимает Ctrl+Alt+Delete, ОС завершает все актив! ные ловушки записи журнала. Это очень грамотное решение, так как возможность записи нажатий клавиш при вводе пароля открывала бы огромную брешь в сис! теме безопасности. Чтобы скрыть детали реализации обработки WM_CANCELJOURNAL, я написал фильтр, отслеживающий это сообщение. Все подробности работы фун! кции!ловушки вы можете увидеть в файле HOOKCODE.CPP, находящемся в ката! логе Tester\TestRec.

Обработка ввода с клавиатуры

Запись событий клавиатуры сводится главным образом к правильной обработке нажатий клавиш Shift, Ctrl и Alt. Прежде чем я опишу некоторые аспекты борьбы с нажатиями отдельных клавиш, изучите рисунки 16!2 — 16!4, на которых пред! ставлен упрощенный граф всех состояний клавиатуры, обрабатываемых кодом записи сценария.

 

 

НормальноеNormal

 

 

 

 

состояниеState

 

 

Клавиша

 

Введенная клавиша

 

Клавиша

 

 

 

отпущена

 

 

 

отпущена

Тип

VK_MENU

Тип ввода

VK_CANCEL

Тип

сообщения

сообщения

Клавиша нажата

 

 

 

Клавиша нажата

Состояние

 

Любая другая клавиша

 

Состояние

IsTabKey

 

 

 

CheckBreak

 

 

 

 

Запись

Обработка

 

Тип ввода

VK_SHIFT

Shift + клавиша

 

 

 

 

нажата/отпущена

Рис. 16 3. Конечный автомат нормальной обработки событий клавиатуры

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

Последний раз я работал с клавиатурой на этом уровне во времена MS!DOS (похоже, я выдал свой возраст!). Поэтому я внес в проблему некоторые дополни! тельные недоразумения. Например, когда я впервые ввел восклицательный знак, я ожидал увидеть, что именно этот символ и поступит в функцию!ловушку. Одна! ко вместо этого я получил символ Shift, за которым следовала единица. Именно так восклицательный знак вводится с клавиатур US English. Однако я хотел, что! бы все воспроизводимые мной последовательности нажатых клавиш были пре!

598

ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

дельно понятны. Последовательность SendKeys «+1» с технической точки зрения

верна, но при этом нужно проделать некоторую умственную гимнастику, чтобы

понять, что на самом деле это символ «!».

 

 

Состояние

 

 

 

Alt Tab

 

 

 

Любое входное сообщение

VK_TAB or VK_SHIFT

Любая

 

 

 

другая

Тип ввода

WM_SYSKEYDOWN

Клавиша

клавиша

 

 

 

 

WM_SYSKEYUP

 

WM_SYSKEYDOWN

 

 

 

 

Клавиша

 

Игнорируем

 

 

Alt+Tab

 

 

 

VK_MENU

Игнорируем

Да

Alt+Tab?

 

Нет

 

Определение

Начальное

состояние

состояния фокуса

 

Прервать

Да

В фокусе

Нет

запись

при TestRec?

 

 

Рис. 16 4. Конечный автомат обработки комбинации Alt+Tab

Чтобы программа TESTREC.EXE была максимально полезна, я должен был реа! лизовать некоторый специальный механизм обработки, который позволил бы сделать выводимые строки понятными. Проще говоря, я должен был проверить состояние клавиатуры, узнать, нажата ли клавиша Shift, и, если да, преобразовать символ в понятную форму. К счастью, для получения действительного символа у нас есть API!функции GetKeyboardState и ToUnicode. Чтобы понять суть обработки нажатий клавиш, изучите функцию CRecordingEngine::NormalKeyState из листинга 16!4.

Листинг 16-4. CRecordingEngine::NormalKeyState

void CRecordingEngine :: NormalKeyState ( PEVENTMSG pMsg )

{

// Состояние, в которое будет выполнен переход

ГЛАВА 16 Автоматизированное тестирование

599

 

 

// после обработки поступившего сообщения. eKeyStates eShiftToState = eNormalKey ;

UINT vkCode = LOBYTE ( pMsg >paramL ) ;

 

 

#ifdef _DEBUG

 

 

{

 

 

STATETRACE (_T("RECSTATE: Normal : ")) ;

 

if ( ( WM_KEYDOWN == pMsg >message

) ||

 

( WM_SYSKEYDOWN == pMsg >message )

)

{

 

 

STATETRACE ( _T( "KEYDOWN " ) ) ;

 

 

}

 

 

else

 

 

{

 

 

STATETRACE ( _T ( "KEYUP " ) ) ;

 

 

}

 

 

TCHAR szName [ 100 ] ;

 

 

GetKeyNameText ( pMsg >paramH << 16 , szName , 100 ) ;

CharUpper ( szName ) ;

STATETRACE ( _T ( "%c %d %04X (%s)\n" ) ,

vkCode

,

vkCode

,

vkCode

,

szName

) ;

}

 

#endif

 

//Проверка того, что нажатая клавиша

//не вызовет прекращения записи сообщений. switch ( vkCode )

{

case VK_CONTROL :

//Нажатие CTRL может произойти при нажатой клавише ALT.

if ( (

WM_KEYDOWN

==

pMsg >message

)

||

(

WM_SYSKEYDOWN ==

pMsg >message

)

)

{

eShiftToState = eCheckBreak ;

STATETRACE ( _T ( "RECSTATE: Looking for BREAK key\n"));

}

else

{

m_cKeyBuff += _T( "{CTRL UP}" ) ; m_iKeyBuffKeys++ ;

}

 

 

m_iKeyBuffKeys++ ;

 

break ;

 

 

case VK_MENU

:

 

if ( ( WM_KEYDOWN

== pMsg >message ) ||

см. след. стр.

600

ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

 

 

 

 

 

 

 

 

 

 

( WM_SYSKEYDOWN == pMsg >message )

)

 

{

 

 

 

eShiftToState = eIsTabKey ;

 

 

STATETRACE (_T("RECSTATE: Looking for TAB key\n")) ;

 

}

 

 

 

else

 

 

 

{

 

 

 

m_cKeyBuff += _T( "{ALT UP}" ) ;

 

 

m_iKeyBuffKeys++ ;

 

 

}

 

 

 

m_iKeyBuffKeys++ ;

 

 

 

break ;

 

 

 

case VK_SHIFT :

 

 

 

if ( ( WM_KEYDOWN

== pMsg >message ) ||

 

 

( WM_SYSKEYDOWN == pMsg >message )

)

 

{

 

 

// При нажатии SHIFT этот блок выполняется только один раз! if ( FALSE == m_bShiftDown )

{

// Нажата клавиша SHIFT, выполняется установка флагов. m_bShiftDown = TRUE ;

m_bShiftDownInString = FALSE ;

}

}

else

{

//Если раньше я записал {SHIFT DOWN},

//мне нужно сопоставить его с {SHIFT UP}. if ( TRUE == m_bShiftDownInString )

{

m_cKeyBuff += _T ( "{SHIFT UP}" ) ; m_iKeyBuffKeys++ ;

m_bShiftDownInString = FALSE ;

}

// Клавиша SHIFT отпущена.

m_bShiftDown = FALSE ;

}

break ; default :

//Это обычная клавиша.

//Если это сообщение не о нажатии, я ничего не делаю.

if ( ( WM_KEYDOWN

==

pMsg >message

)

||

 

( WM_SYSKEYDOWN ==

pMsg >message

)

 

)

{

 

 

 

 

 

//TRACE ( "vkCode = %04X\n" , vkCode ) ;

// Есть ли строка, соответствующая этому виртуальному коду?

ГЛАВА 16 Автоматизированное тестирование

601

 

 

 

 

 

 

 

 

 

if ( NULL != g_KeyStrings[ vkCode ].szString

)

 

{

 

 

 

 

// Нажата ли

клавиша SHIFT?

Если да,

 

 

// перед записью обрабатываемой клавиши

 

 

// мне нужно

записать {SHIFT DOWN}.

 

 

if ( ( TRUE

== m_bShiftDown

) &&

 

 

( FALSE

== m_bShiftDownInString )

)

 

{

 

 

 

 

m_cKeyBuff += _T ( "{SHIFT DOWN}" ) ; m_iKeyBuffKeys++ ; m_bShiftDownInString = TRUE ;

}

// Добавление клавиши в список.

m_cKeyBuff += g_KeyStrings[ vkCode ].szString ;

}

else

{

//Я должен преобразовать нажатую клавишу в ее

//символьный эквивалент. Для правильного

//преобразования таких последовательностей, как

//"{SHIFT DOWN}1{SHIFT UP}" в "!", мне нужно

//получить состояние клавиатуры и вызвать

//функцию ToAscii.

//Сначала нужно получить состояние клавиатуры. BYTE byState [ 256 ] ;

GetKeyboardState ( byState ) ;

// А теперь выполнить преобразование. TCHAR cConv[3] = { _T ( '\0' ) } ; TCHAR cChar ;

#ifdef _UNICODE

 

 

 

int iRet = ToUnicode

( vkCode

 

,

 

pMsg7>paramH

 

,

 

byState

 

,

 

(LPWORD)&cConv

,

 

sizeof ( cConv ) /

 

 

sizeof ( TCHAR )

,

 

0

 

) ;

#else

 

 

 

int iRet = ToAscii (

vkCode

,

 

 

pMsg7>paramH

,

 

 

byState

,

 

 

(LPWORD)&cConv ,

 

 

0

) ;

 

#endif

 

 

 

if ( 2 == iRet )

{

см. след. стр.

602 ЧАСТЬ IV Мощные средства и методы отладки неуправляемого кода

// Это национальный символ.

ASSERT ( !"I gotta handle this!" ) ;

}

//Если символ не был преобразован,

//cChar не используется!

if ( 0 == iRet )

{

cChar = (TCHAR)vkCode ;

}

else

{

//Прежде чем записать символ, мне нужно узнать,

//нажата ли клавиша CTRL. Если да, то функции

//ToAscii/ToUnicode возвращают управляющий

//код ASCII. Так как мне нужен символ, я должен

//выполнить некоторую дополнительную работу. SHORT sCtrlDown =

GetAsyncKeyState ( VK_CONTROL ) ; if ( 0xFFFF8000 == ( 0xFFFF8000 & sCtrlDown ))

{

//Клавиша CTRL нажата, поэтому мне нужно

//узнать состояние клавиш CAPSLOCK и SHIFT. BOOL bCapsLock =

( 0xFFFF8000 == ( 0xFFFF8000 & GetAsyncKeyState ( VK_CAPITAL)));

if ( TRUE == bCapsLock )

{

//Если нажаты клавиши CAPSLOCK и SHIFT,

//используем символ нижнего регистра. if ( TRUE == m_bShiftDown )

{

//Запрещение предупреждения 'variable' : conversion from 'type' to 'type'

//of greater size (преобразование к типу, имеющему больший размер). #pragma warning ( disable : 4312 )

cChar = (TCHAR)

CharLower ( (LPTSTR)vkCode );

#pragma warning ( default : 4312 )

}

else

{

// Символ верхнего регистра. cChar = (TCHAR)vkCode ;

}

}

else

{

//Клавиша CAPSLOCK не нажата,

//поэтому проверяется только

//клавиша SHIFT.

Соседние файлы в предмете Программирование на C++