7. Еще несколько новых органов

В этой главе мы расскажем о группе новых органов управления, которые называются общими (generic) органами управления. Это Trackbar , Progressbar , анимационный орган управления Animation , орган циклического просмотра Up-Down и, наконец, орган управления Hot Key .

Все перечисленные выше органы управления просты и удобны.

С помощью органа управления Trackbar пользователь может регулировать какие-либо аналоговые или дискретные параметры, такие как, например, громкость или яркость.

Орган управления Progressbar удобен для отображения процента завершения какого-либо процесса, например, процесса установки программного обеспечения или копирования данных.

Анимационный орган управления Animation предназначен для просмотра видеоклипов (без звукового сопровождения), "оживляющих" приложение.

Ниже в этой главе мы приведем полные исходные тексты приложения Compact Disk Player, демонстрирующих практическое использование трех перечисленных выше органов управления в приложении, предназначенном для проигрывания звуковых компакт-дисков.

Кроме того, в некоторых случаях могут оказаться весьма полезными органы управления Up-Down и Hot Key. Первый из них позволяет организовать циклический просмотр и выбор чисел или текстовых строк, а второй является удобным средством для ввода комбинаций клавиш ускоренного доступа.

В этой главе вы научитесь создавать органы управления Up-Down и Hot Key на примере приложения UpDown, исходные тексты которого приведены в конце книги.

7.1. Орган управления Trackbar

Внешний вид органа управления Trackbar показан на рис. 7.1.

Рис. 7.1. Орган управления Trackbar

По принципу действия и назначению Trackbar напоминает орган управления Scrollbar (полоса просмотра), который мы подробно рассмотрели в 12 томе "Библиотеки системного программиста".

В окне Trackbar имеется движок, который можно перемещать мышью, клавишами перемещения курсора, а также клавишами <Home>, <End>, <PgUp> и <PgDn>. При перемещении движка родительское окно, создавшее орган управления Trackbar, получает извещения. Помимо движка, в окне органа управления Trackbar есть риски (tick marks), разделяющие движок на равные части. Кроме того, можно выделить любой диапазон в окне Trackbar.

Вы можете выбрать горизонтальное или вертикальное расположение органа управления Trackbar. Риски могут находиться с любой стороны, с обеих сторон или их может не быть совсем.

При создании Trackbar приложение должно определить диапазон значений, соответствующих положению движка, шаг расположения рисок, а также шаг изменения этих значений.

Создание органа управления Trackbar

Для создания органа управления Trackbar можно воспользоваться функцией CreateWindowEx , указав ей предопределенный класс окна TRACKBAR_CLASS . При этом следует использовать следующие стили, определяющие расположение и внешний вид органа управления Trackbar:

Стиль Описание
TBS_HORZ Горизонтальное расположение окна Trackbar
TBS_VERT Вертикальное расположение окна Trackbar
TBS_TOP Риски будут расположены сверху (используется вместе со стилем TBS_HORZ)
TBS_BOTTOM Риски будут расположены снизу (используется вместе со стилем TBS_HORZ)
TBS_LEFT Риски будут расположены слева (используется вместе со стилем TBS_VERT)
TBS_RIGHT Риски будут расположены справа (используется вместе со стилем TBS_VERT)
TBS_BOTH Риски будут расположены с обеих сторон
TBS_AUTOTICKS Если указан этот стиль, риски создаются для каждого возможного значения из диапазона значений, определенных для Trackbar
TBS_NOTICKS Сразу после создания окна Trackbar риски не отображаются, однако позже вы их сможете определить при помощи сообщений TBM_SETTIC и TBM_SETTICFREQ, которые будут рассмотрены ниже
TBS_ENABLESELRANGE Возможно выделение диапазона значений

Не забудьте также инициализировать библиотеку стандартных органов управления, вызвав функцию InitCommonControls :

InitCommonControls();

Чаще всего, однако, орган управления Trackbar создается не функцией CreateWindowEx, а редактором диалоговых панелей. Именно так мы и поступили в приложении Compact Disk Player, просто переместив изображение Trackbar из палитры органов управления в диалоговую панель.

Если для работы вы используете систему Microsoft Visual C++ версии 2.0 , предназначенную для Microsoft Windows NT, то вам потребуется отредактировать регистрационную базу данных (иначе в указанной палитре не будет новых органов управления). Процесс редактирования несложен и описан в SDK.

Прежде всего вам нужно запустить приложение REGEDIT , что можно сделать, например, с помощью всемогущей кнопки Start. На экране появится содержимое регистрационной базы данных в виде дерева просмотра (рис. 7.2).

Рис. 7.2. Просмотр содержимого регистрационной базы данных

Теперь вам нужно раскрыть ветвь этого дерева, которая ведет к набору параметров редактора диалогов. Эта ветвь обозначается как HKEY_CURRENT_USER\Software\Microsoft\Visual C++ 2.0 Dialog Editor.

Далее из меню Edit выберите строки New и Binary Value. Задайте имя для нового элемента данных как ChicagoControls. После этого из меню Edit выберите строку Modify и введите значение 01 00 00 00. Результат показан на рис. 7.3.

Рис. 7.3. Внесенные изменения в регистрационную базу данных, позволяющие использовать новые органы управления в системе разработки Microsoft Visual C++ версии 2.0

Завершите работу редактора регистрационной базы данных. Теперь после перезапуска Microsoft Visual C++ в соответствующей палитре вам будут доступны новые органы управления.

После перемещения изображения органа управления Trackbar в проектируемую диалоговую панель вы можете сделать по этому изображению двойной щелчок левой клавишей мыши и определить стили в блокноте Trackbar Properties (рис. 7.4).

Рис. 7.4. Определение стилей органа управления Trackbar

Обработка извещений

Когда пользователь перемещает движок органа управления Trackbar, родительское окно получает извещения в форме сообщения WM_HSCROLL (или WM_VSCROLL для горизонтального расположения). Эти извещения аналогичны извещениям, поступающим от полосы просмотра Scrollbar.

Младшее слово параметра wParam сообщения WM_HSCROLL может содержать один из следующих кодов извещения:

Код извещения Описание
TB_PAGEUP Извещение поступает, когда пользователь сделал щелчок мышью по полосе Trackbar слева или сверху от движка, либо нажал клавишу с кодом VK_PRIOR (клавишу <PgUp>)
TB_PAGEDOWN Пользователь сделал щелчок справа или снизу от движка, либо нажал клавишу с кодом VK_NEXT (клавишу <PgDn>)
TB_LINEUP Была нажата клавиша с кодом VK_LEFT или VK_UP (клавиша перемещения курсора влево или вверх)
TB_LINEDOWN Была нажата клавиша с кодом VK_RIGHT или VK_DOWN (клавиша перемещения курсора вправо или вниз)
TB_TOP Пользователь нажал клавишу с кодом VK_HOME (клавишу <Home>)
TB_BOTTOM Пользователь нажал клавишу с кодом VK_END (клавишу <End>)
TB_THUMBPOSITION Это извещение приходит, когда пользователь переместил мышью движок в новое положение
TB_THUMBTRACK Извещение TB_THUMBTRACK приходит в процессе перемещения движка
TB_ENDTRACK Извещение приходит после завершения перемещения движка мышью или отпускания клавиши, с помощью которой перемещался движок

Ниже мы привели функцию DlgProc_OnHScroll из приложения Compact Disk Player, обрабатывающую извещения от органа управления Trackbar:

void DlgProc_OnHScroll(HWND hdlg, HWND hwndCtl, 
  UINT code, int pos)
{
  switch(code)
  {
    case TB_LINEDOWN:
    case TB_PAGEDOWN:
      CdPlayNext(hdlg);         break;
    case TB_LINEUP:
    case TB_PAGEUP:
      CdPlayPrev(hdlg);         break;
    case TB_BOTTOM:
      CdPlay(hdlg, nTrackCnt);  break;
    case TB_TOP:
      CdPlay(hdlg, 1);          break;
    case TB_THUMBPOSITION:
      CdPlay(hdlg, pos);        break;
    default:
      break;
  }
}

Как видите, здесь все очень просто.

Функции DlgProc_OnHScroll передаются преобразованные параметры сообщения WM_HSCROLL. Код извещения находится в параметре code, а новое значение позиции (которое нужно для обработки извещения TB_THUMBPOSITION) - в параметре pos.

Сообщения для органа управления Trackbar

Ниже мы перечислили сообщения, которые приложение может посылать органу управления Trackbar при помощи функции SendMessage :

Сообщение Описание
TBM_CLEARSEL Отмена выделения диапазона значений в окне Trackbar
TBM_CLEARTICS Удаление рисок
TBM_GETCHANNELRECT Определение координат и размеров воображаемого прямоугольника, ограничивающего область перемещения движка
TBM_GETLINESIZE Определение величины (в рисках) на которую перемещается движок, если пользователь нажимает клавиши с кодами VK_LEFT, VK_RIGHT, VK_UP или VK_DOWN
TBM_GETNUMTICS Определение количества рисок в окне Trackbar
TBM_GETPAGESIZE Определение величины (в рисках) на которую перемещается движок, если пользователь нажимает клавиши с кодами VK_PRIOR или VK_NEXT
TBM_GETPOS Определение текущего положения движка
TBM_GETPTICS Функция SendMessage, посылающая это сообщение, возвращает указатель на массив позиций рисок
TBM_GETRANGEMAX Определение максимальной позиции движка
TBM_GETRANGEMIN Определение минимальной позиции движка
TBM_GETSELEND Определение конечной позиции выделенной области
TBM_GETSELSTART Определение начальной позиции выделенной области
TBM_GETTHUMBLENGTH Определение длины движка в пикселах
TBM_GETTHUMBRECT Определение расположения и размеров движка
TBM_GETTIC Определение позиции риски с заданным номером
TBM_GETTICPOS Определение физического расположения указанной риски в системе координат внутренней области окна
TBM_SETLINESIZE Установка величины (в рисках) на которую перемещается движок, если пользователь нажимает клавиши с кодами VK_LEFT, VK_RIGHT, VK_UP или VK_DOWN
TBM_SETPAGESIZE Установка величины (в рисках) на которую перемещается движок, если пользователь нажимает клавиши с кодами VK_PRIOR или VK_NEXT
TBM_SETPOS Установка текущего положения движка
TBM_SETRANGE Установка минимальной и максимальной позиции движка в окне Trackbar
TBM_SETRANGEMAX Установка максимальной позиции движка в окне Trackbar
TBM_SETRANGEMIN Установка минимальной позиции движка в окне Trackbar
TBM_SETSEL Установка начальной и конечной позиции выделенной области
TBM_SETSELEND Установка конечной позиции выделенной области
TBM_SETSELSTART Установка начальной позиции выделенной области
TBM_SETTHUMBLENGTH Установка длины движка в пикселах
TBM_SETTIC Установка позиции риски с заданным номером. Не используется для первой и последней риски, которые создаются автоматически
TBM_SETTICFREQ Установка интервала, с которым располагаются риски

Детальное описание параметров этих сообщений вы найдете в SDK. Из-за недостатка места мы подробно рассмотрим только самые важные из них.

Прежде всего, если оран управления Trackbar расположен в диалоговой панели, вы должны определить его идентификатор. Это можно сделать с помощью функции GetDlgItem (описанной в 12 томе "Библиотеки системного программиста"):

hTrackBar = GetDlgItem(hdlg, IDC_TRACKBAR);

В этом примере в переменную hTrackBar записывается идентификатор окна органа управления Trackbar, который определен в диалоговой панели с идентификатором IDC_TRACKBAR (рис. 7.4).

При инициализации органа управления Trackbar вы должны определить диапазон изменения значений, т. е. минимальную и максимальную позицию движка в окне Trackbar. Это можно сделать с помощью сообщения TBM_SETRANGE :

SendMessage(hTrackBar, TBM_SETRANGE, TRUE, 
  MAKELPARAM(1, nTrackCnt));

Через параметр wParam вместе с этим сообщением передается флаг перерисовки. Если он равен TRUE, сразу после установки диапазона окно органа управления будет перерисовано, если FALSE - нет.

Значение MAKELPARAM(min, max), записанное в lParam, определяет минимальное min и максимальное max значения позиции движка.

Затем нужно установить расстояние, на которое перемещается движок при управлении им с помощью мыши и клавиатуры. Для этого окну органа управления Trackbar нужно послать сообщения TBM_SETPAGESIZE и TBM_SETLINESIZE , например:

SendMessage(hTrackBar, TBM_SETPAGESIZE, 0, 1);
SendMessage(hTrackBar, TBM_SETLINESIZE, 0, 1);

Для этих сообщений параметр wParam должен быть равен нулю. В параметр lParam следует записать значение шага.

В данном случае мы устанавливаем одинаковый шаг, однако вы можете использовать и разные шаги. Например, для грубого позиционирования вы можете указать в сообщении TBM_SETPAGESIZE шаг, равный 10, а для тонкого - указать в сообщении TBM_SETLINESIZE шаг, равный 1.

Для принудительной установки движка Trackbar в новую позицию используйте сообщение TBM_SETPOS :

SendMessage(hTrackBar, TBM_SETPOS, TRUE, nCurTrack);

Параметр wParam этого сообщения содержит флаг позиционирования. Если он равен TRUE (как в приведенном выше примере), движок устанавливается в позицию, указанную параметром lParam. Если же этот флаг равен FALSE, движок не перемещается.

Заметьте, что есть одно отличие между полосой просмотра Scrollbar и органом управления Trackbar. Когда пользователь делает щелчок мышью по полосе просмотра, приложение должно в ответ на это установить новое положение движка с помощью функции SetScrollPos . В противоположность этому, в указанной ситуации орган управления Trackbar перемещает движок автоматически.

7.2. Орган управления Progressbar

Орган управления Progressbar ничем не управляет, а служит только для отображения как линейный индикатор (рис. 7.5).

Рис. 7.5. Орган управления Progressbar

Он очень прост в использовании, так как не посылает извещений и воспринимает небольшое количество сообщений.

Создание органа управления Progressbar

Так же как и Trackbar, вы можете создать орган управления Progressbar при помощи функции CreateWindowEx , указав ей класс окна PROGRESS_CLASS .

Кроме того, вы можете использовать Progressbar в диалоговых панелях аналогично органу управления Trackbar. Для того чтобы в палитре органов управления редактора диалоговых панелей системы разработки Microsoft Visual C++ версии 2.0 появилась пиктограмма Progressbar, вы должны внести описанные ранее изменения в регистрационную базу данных.

Для органа управления Progressbar при инициализации необходимо задать диапазон изменения значений и начальное значение (в пределах от 0 до 65535).

Сообщения для органа управления Progressbar

Для органа управления Progressbar определены пять сообщений:

Сообщение Описание
PBM_DELTAPOS Продвижение заполняющей полосы в окне индикатора на заданное расстояние, определенное сообщением PBM_SETSTEP
PBM_SETPOS Установка текущего положения заполняющей полосы и перерисовка индикатора
PBM_SETRANGE Установка минимальной и максимальной позиции для органа управления Progressbar
PBM_SETSTEP Установка шага продвижения при поступлении сообщения PBM_STEPIT (по умолчанию шаг равен 10)
PBM_STEPIT Продвижение заполняющей полосы в окне индикатора на величину, определенную сообщением PBM_SETSTEP

При инициализации необходимо установить диапазон изменений значений и шаг продвижения.

Первая задача решается с помощью сообщения PBM_SETRANGE:

SendMessage(hProgressBar, PBM_SETRANGE, 0, 
  MAKELPARAM(0, nTrackCnt));

Параметр wParam этого сообщения должен быть равен нулю. Значение MAKELPARAM(min, max), записанное в lParam, определяет минимальное min и максимальное max значения позиции для органа управления Progressbar.

Шаг продвижения устанавливается в параметре wParam сообщения PBM_SETSTEP (параметр lParam должен быть равен нулю):

SendMessage(hProgressBar, PBM_SETSTEP, 1, 0);

Если вам нужно установить новое текущее положение заполняющей полосы, это можно сделать при помощи сообщения PBM_SETPOS, указав положение в параметре wParam:

SendMessage(hProgressBar, PBM_SETPOS, nCurTrack, 0);

При этом параметр lParam должен быть равен нулю.

Сообщение PBM_STEPIT не имеет параметров и может быть использовано для продвижения полосы следующим образом:

SendMessage(hProgressBar, PBM_STEPIT, 0, 0);

Если необходимо продвинуть полосу на заданное расстояние, используйте сообщение PBM_DELTAPOS:

SendMessage(hProgressBar, PBM_DELTAPOS, nDelta, 0);

Расстояние должно быть указано в параметре wParam. Параметр lParam должен быть равен нулю.

7.3. Орган управления Animation

Орган управления Animation очень прост в использовании и может увеличить привлекательность приложения. На рис. 1.5 в первой главе показано, как с помощью органа управления Animation можно "оживить" процедуру копирования файла. В нашем приложении Compact Disk Player, к которому мы скоро перейдем, этот орган управления используется для изображения вращающегося компакт диска. Диск вращается только тогда, когда выполняется проигрывание дорожки.

Принцип действия органа управления Animation заключается в проигрывании AVI -файла, содержащего видеоизображение. Вы можете создать небольшой мультфильм из отдельных BMP-файлов с помощью приложения VidEdit, которое входит в состав Microsoft Video for Windows 3.1 версии 1.1. Соответствующая процедура описана в 15 томе "Библиотеки системного программиста", который называется "Мультимедиа для Windows".

Учтите, что для сжатия вы можете использовать только алгоритм RLE (можно также использовать несжатые файлы), причем AVI-файл не должен содержать звуковой информации. Если же вам нужно проигрывать видеоклип с звуковым сопровождением, да и еще сжатый более эффективно, лучше всего использовать орган управления MCIWnd , который был подробно описан в 15 томе нашей библиотеки.

Создание органа управления Animation

Если вы желаете создать орган управления Animation в обычном окне, то это можно сделать с помощью макрокоманды Animate_Create :

HWND Animate_Create(
  HWND  hwndP,          // идентификатор родительского окна
  UINT  id,             // идентификатор окна органа Animation
  DWORD  dwStyle,       // стиль окна органа Animation
  HINSTANCE  hInstance; // идентификатор приложения
);

Эта макрокоманда создает орган управления Animation, вызывая функцию CreateWindow и указывая ей предопределенный класс окна ANIMATE_CLASS .

В параметре dwStyle вы можете указать один из следующих стилей:

Стиль Описание
ACS_AUTOPLAY Проигрывание видеоизображения начинается автоматически сразу после открытия соответствующего AVI-файла
ACS_CENTER Видеоизображение будет центрировано в окне органа управления
ACS_TRANSPARENT Изображение рисуется в прозрачном режиме

В том случае когда орган управления Animation должен быть расположен в диалоговой панели, его необходимо переместить туда из палитры редактора диалоговых панелей. Если же вы пользуетесь системой разработки Microsoft Visual C++ версии 2.0, вам придется отредактировать файл ресурсов самостоятельно. Для этого откройте его в текстовом режиме и вставьте следующие строки:

CONTROL  "Animate",IDC_ANIMATE, "SysAnimate32", 
  WS_BORDER | WS_TABSTOP | 0x1, 219, 34, 33, 33

Последние четыре числа определяют, соответственно расположение и размеры окна органа управления Animation.

Дополнительно к стилям WS_BORDER и WS_TABSTOP могут быть добавлены описанные выше специфические стили органа управления Animation, определенные следующим образом:

#define ACS_CENTER        0x0001
#define ACS_TRANSPARENT   0x0002
#define ACS_AUTOPLAY      0x0004

Сообщения для органа управления Animation

Для органа управления Animation определены макрокоманды, посылающие сообщения ACM_OPEN, ACM_PLAY и ACM_STOP. Все они возвращают значение TRUE в случае успешного завершения и FALSE при ошибке.

Открытие видеоролика

BOOL Animate_Open (hwnd, lpszName);

Через параметр hwnd макрокоманде передается идентификатор органа управления Animation.

Параметр lpszName должен содержать указатель на текстовый буфер, в который записан путь к AVI-файлу или идентификатор ресурса AVI, созданный с помощью макрокоманды MAKEINTRESOURCE.

Закрытие видеоролика

BOOL Animate_Close (hwnd);

Параметр hwnd должен содержать идентификатор органа управления Animation.

Запуск проигрывания

BOOL Animate_Play (hwnd, wFrom, wTo, cRepeat);

Эта макрокоманда запускает проигрывание открытого видеоролика для органа управления Animation с идентификатором hwnd.

Параметры wFrom и wTo указывают, соответственно, номера начального и конечного кадра в видеоролике. Эти номера не должны превышать значения 65536, причем нулевое число означает начало видеоролика.

Параметр cRepeat определяет количество повторных проигрываний, которые необходимо выполнить. Если в этом параметре указано значение -1, проигрывание выполняется бесконечное число раз.

Останов проигрывания

BOOL Animate_Stop (hwnd);

Макрокоманда Animate_Stop выполняет останов запущенного ранее проигрывания видеоролика.

Позиционирование

BOOL Animate_Seek (hwnd, wFrame);

С помощью макрокоманды Animate_Seek вы можете отобразить кадр открытого видеоролика с номером wFrame.

Извещения от органа управления Animation

Орган управления может посылать в родительское окно извещения в форме сообщения WM_COMMAND . Это извещения ACN_START и ACN_STOP .

Первое из них сообщает о том, что процесс проигрывания видеоролика начался, а второй - что этот процесс завершился.

7.4. Приложение Compact Disk Player

В качестве примера практического использования органов управления Trackbar, Progressbar и Animation мы предлагаем вам приложение Compact Disk Player, предназначенное для проигрывания дорожек звукового компакт-диска.

Попутно мы продемонстрируем использование MCI-интерфейса в операционной системе Microsoft Windows 95. Напомним, что MCI-интерфейс используется для работы с устройствами мультимедиа. Более подробно вы можете об этом узнать из 15 тома "Библиотеки системного программиста".

Вы также узнаете, как создать приложение Win32, не имеющее главное окно, а представляющее из себя обычную диалоговую панель.

Внешний вид приложения Compact Disk Player показан на рис. 7.6.

Рис. 7.6. Приложение Compact Disk Player

В верхней части окна слева находятся органы управления Trackbar и Progressbar. Первый из них используется для перемещения по дорожкам звукового компакт-диска, второй показывает текущую дорожку. По мере проигрывания дорожек полоса Progressbar и движок Trackbar сдвигаются вправо.

Вы можете переключаться на произвольную дорожку компакт-диска, просто передвигая движок органа Trackbar в нужное место или делая щелчки мышью слева или справа от него в окне Trackbar.

Для перехода к следующей дорожке можно использовать клавишу <PgUp>, клавиши перемещения курсора вверх и вправо, а для перехода к предыдущей - клавишу <PgDn> и клавиши перемещения курсора вниз и влево.

Если нажать на клавишу <Home>, приложение начнет проигрывание с первой дорожки, а если <End> - с последней.

Справа вверху в окне нашего приложения расположен орган управления Animation. Когда проигрывание остановлено, в окне этого органа управления изображается неподвижный компакт-диск. Когда же начинается проигрывание, этот диск начинает вращаться.

В нижней части окна находятся кнопки, с помощью которых можно управлять процессом проигрывания:

Кнопка Назначение
>> Переход к проигрыванию следующей дорожки
<< Переход к проигрыванию предыдущей дорожки
Play Запуск проигрывания
Stop Останов проигрывания
Pause Временный останов проигрывания
Resume Продолжение проигрывания после временного останова
Eject Выталкивание компакт-диска (работает не на всех типах устройств чтения компакт-диска)
Exit Завершение работы приложения

При смене компакт-диска количество рисок в окне органа управления Trackbar автоматически устанавливается равным количеству звуковых дорожек.

Еще одно замечание.

Когда вы вставляете музыкальный компакт-диск, операционная система Microsoft Windows 95 автоматически запускает приложение и начинает проигрывание вставленного диска. Для работы с нашим приложением просто остановите проигрывание и завершите приложение CD Player.

Исходные тексты приложения Compact Disk Player

Приложение Compact Disk Player сделано на базе приложения MCICDPL, которое было описано в 15 томе "Библиотеки системного программиста".

Функции приложения Compact Disk Player определены в файлах cdplay.c (листинг 7.1) и cdproc.c (листинг 7.2).

Листинг 7.1. Файл cdplay\cdplay.c

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "resource.h"
#include "afxres.h"
#include "cdplay.h"

HINSTANCE hInst;
char szAppName[]  = "CdPlayApp";
char szAppTitle[] = "CD Player";
UINT nTimerID;
UINT nCurTrack = 0;
UINT nTrackCnt = 0;
HWND hwndCurTrack;
HWND hProgressBar;
HWND hTrackBar;

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  HWND hWnd;
  hInst = hInstance;

  // Преверяем, не было ли это приложение запущено ранее
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    if(IsIconic(hWnd))
      ShowWindow(hWnd, SW_RESTORE);
    SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Инициализируем библиотеку стандартных органов управления
  InitCommonControls();

  // Отображаем диалоговую панель, которая служит
  // главным окном приложения
  DialogBox(hInstance, 
    MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);

  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc
// -----------------------------------------------------
BOOL APIENTRY
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc_OnInitDialog);
    HANDLE_MSG(hdlg, WM_COMMAND,    DlgProc_OnCommand);
    HANDLE_MSG(hdlg, WM_HSCROLL,    DlgProc_OnHScroll);
    HANDLE_MSG(hdlg, WM_TIMER,      DlgProc_OnTimer);
    default:
      break;
  }
  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc_OnInitDialog
// -----------------------------------------------------
BOOL DlgProc_OnInitDialog(HWND hdlg, 
  HWND hwndFocus, LPARAM lParam)
{
  // Определяем идентификатор поля, которое используется
  // для отображения номера текущей дорожки
  hwndCurTrack = GetDlgItem(hdlg, IDT_CURTRACK);

  // Получаем идентификатор органа Progressbar
  hProgressBar = GetDlgItem(hdlg, IDC_PROGRESSBAR);
  
  // Устанавливаем диапазон изменения значений
  // и шаг для органа Progressbar
  SendMessage(hProgressBar, PBM_SETRANGE, 0, 
    MAKELPARAM(0, nTrackCnt));
  SendMessage(hProgressBar, PBM_SETSTEP, 1, 0);

  // Получаем идентификатор органа Trackbar
  hTrackBar = GetDlgItem(hdlg, IDC_TRACKBAR);
  
  // Устанавливаем диапазон изменения значений
  // для органа Trackbar
  SendMessage(hTrackBar, TBM_SETRANGE, TRUE, 
    MAKELPARAM(1, nTrackCnt));

  // Устанавливаем шаг изменений
  SendMessage(hTrackBar, TBM_SETPAGESIZE, 0, 1);
  SendMessage(hTrackBar, TBM_SETLINESIZE, 0, 1);

  // Инициализируем устройство проигрывания
  // звуковых компакт-дисков
  CdInit();  

  // Создаем таймер для периодического определения
  // состояния устройства проигрывания
  nTimerID = SetTimer(hdlg, 1, 1000, NULL);

  // Открываем AVI-файл с видеоклипом (вращающийся
  // компакт-диск)
  Animate_Open(GetDlgItem(hdlg, IDC_ANIMATE), "CD.AVI");

  return TRUE;
}

// -----------------------------------------------------
// Функция DlgProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void DlgProc_OnCommand(HWND hdlg, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  switch (id)
  {
    case IDCANCEL:
    case IDB_EXIT:
    {
      KillTimer(hdlg, nTimerID); // удаляем таймер

      CdClose(); // закрываем устройство чтения компакт-диска

      EndDialog(hdlg, TRUE); // завершаем работу приложения
      break;
    }
      
    // Выполнение команд проигрывания, останова,
    // паузы, возобновления проигрывания после паузы и т. д.
    case IDB_PLAY:
      CdPlay(hdlg, 1);  break;
    case IDB_STOP:
      CdStop();         break;
    case IDB_PAUSE:
      CdPause();        break;
    case IDB_RESUME:
      CdResume(hdlg);   break;
    case IDB_NEXT:
      CdPlayNext(hdlg); break;
    case IDB_PREV:
      CdPlayPrev(hdlg); break;
    case IDB_EJECT: 
      CdEject();        break;

      default:
      return FALSE;
  }
  return TRUE;
}

// -----------------------------------------------------
// Функция DlgProc_OnTimer
// -----------------------------------------------------
void DlgProc_OnTimer(HWND hwnd, UINT id)
{
  BYTE szBuf[20];

  // Если окно свернуто в пиктограмму, ничего не делаем,
  // чтобы не снижать производительность системы
  if(IsIconic(hwnd))
    return;

  // Если состояние устройства проигрывания изменилось,
  // отображаем изменения в диалоговой панели
  if(CdUpdateState(hwnd))
  {
    // Отображаем номер текущей дорожки
    if(nCurTrack != 0)
    {
      itoa(nCurTrack, szBuf, 10);
      SetWindowText(hwndCurTrack, szBuf);
    }
    else
      SetWindowText(hwndCurTrack, "");

    // Изменяем положение движка Trackbar
    SendMessage(hTrackBar, TBM_SETPOS, TRUE, nCurTrack);

    // Изменяем диапазон значений и положение 
    // полосы Progressbar
    SendMessage(hProgressBar, PBM_SETRANGE, 0, 
      MAKELPARAM(0, nTrackCnt));
    SendMessage(hProgressBar, PBM_SETPOS, nCurTrack, 0);
  }
}

// -----------------------------------------------------
// Функция DlgProc_OnHScroll
// -----------------------------------------------------
void DlgProc_OnHScroll(HWND hdlg, HWND hwndCtl, 
  UINT code, int pos)
{
  switch(code)
  {
    // Отрабатываем команды, поступающие от органа
    // управления Trackbar
    case TB_LINEDOWN:
    case TB_PAGEDOWN:
      CdPlayNext(hdlg);         break;
    
    case TB_LINEUP:
    case TB_PAGEUP:
      CdPlayPrev(hdlg);         break;

    case TB_BOTTOM:
      CdPlay(hdlg, nTrackCnt);  break;

    case TB_TOP:
      CdPlay(hdlg, 1);          break;

    case TB_THUMBPOSITION:
      CdPlay(hdlg, pos);        break;
    default:
      break;
  }
}

В файле cdproc.c (листинг 7.2) собраны функции управления устройством чтения компакт-диска.

Листинг 7.2. Файл cdplay\cdproc.c

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "resource.h"
#include "afxres.h"
#include "cdplay.h"
#include <mmsystem.h>

DWORD dwrc;
MCI_OPEN_PARMS   MCIOpen;
MCI_SET_PARMS    MCISet;
MCI_STATUS_PARMS MCIStatus;
MCI_PLAY_PARMS   MCIPlay;
BOOL bMediaPresent = FALSE;
BOOL bPaused       = FALSE;
UINT nMode = 0;
HWND hwndCurTrack = NULL;
extern UINT nCurTrack;
extern UINT nTrackCnt;
extern HWND hTrackBar;

// -----------------------------------------------------
// Функция CdInit
// -----------------------------------------------------
BOOL CdInit(void)
{
  // Открываем устройство чтения компакт-дисков
  MCIOpen.lpstrDeviceType = (LPSTR)MCI_DEVTYPE_CD_AUDIO;
  dwrc = mciSendCommand(0, MCI_OPEN,
    MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD)&MCIOpen);
  if(dwrc)
  {
    mciwioError(dwrc);
    return FALSE;
  }

  // Устанавливаем формат времени
  MCISet.dwTimeFormat = MCI_FORMAT_TMSF;
  dwrc = mciSendCommand(MCIOpen.wDeviceID, MCI_SET,
    MCI_SET_TIME_FORMAT, (DWORD)&MCISet);
  if(dwrc)
  {
    mciwioError(dwrc);
    return FALSE;
  }
  return TRUE;
}

// -----------------------------------------------------
// Функция CdClose
// -----------------------------------------------------
void CdClose(void)
{
  // Закрываем устройство чтения компакт-дисков
  dwrc = mciSendCommand(MCIOpen.wDeviceID, MCI_CLOSE, 0, 0);
  if(dwrc)
    mciwioError(dwrc);
}

//-----------------------------------------------------
// mciwioError
// Обработка ошибок
//-----------------------------------------------------
void mciwioError(DWORD dwrc)
{
  BYTE szBuf[MAXERRORLENGTH];

  if(mciGetErrorString(dwrc, (LPSTR)szBuf, MAXERRORLENGTH))
    MessageBox(NULL, szBuf,
      "MCIWAVE Error", MB_ICONEXCLAMATION);
  else
    MessageBox(NULL, "Неизвестная ошибка",
      "MCIWAVE Error", MB_ICONEXCLAMATION);
}

// -----------------------------------------------------
// Функция CdUpdateState
// -----------------------------------------------------
BOOL CdUpdateState(HWND hdlg)
{
  BOOL fNeedUpdate = FALSE;
  UINT nCurMode;

  // Определяем текущее состояние проигрывателя CD
  MCIStatus.dwItem = MCI_STATUS_MODE;
  mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS,
    MCI_STATUS_ITEM | MCI_WAIT, (DWORD)&MCIStatus);

  // Проверяем, готово ли устройство чтения к работе 
  if((MCIStatus.dwReturn == MCI_MODE_NOT_READY) ||
     (MCIStatus.dwReturn == MCI_MODE_OPEN))
  {
    // Устройство не готово
    nCurMode = CD_EMPTY;
  }
  else if((MCIStatus.dwReturn == MCI_MODE_STOP) &&
    bPaused)
  {
    // Устройство остановлено 
    nCurMode = CD_PAUSED;
  }
  else if(MCIStatus.dwReturn == MCI_MODE_PLAY)
  {  
    // Устройство находится в режиме проигрывания
    nCurMode = CD_PLAYING;
  }
  else
  {
    // Устройство готово
    nCurMode = CD_READY;
  }

  // Если с момента последней проверки произошло
  // изменение режима, записываем код нового режима
  if(nMode != nCurMode)
  {
    fNeedUpdate = TRUE;
    nMode = nCurMode;
    
    // Если устройство находится в режиме проигрывания,
    // запускаем видеоклип. В противном случае
    // останавливаем видеоклип в его текущей позиции
    if(nCurMode == CD_PLAYING)
      Animate_Play(GetDlgItem(hdlg, IDC_ANIMATE), 0, -1, -1);
    else
      Animate_Stop(GetDlgItem(hdlg, IDC_ANIMATE));
  }

  // Проверяем, вставлен ли компакт-диск
  MCIStatus.dwItem = MCI_STATUS_MEDIA_PRESENT;
    mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS,
    MCI_STATUS_ITEM | MCI_WAIT, (DWORD)&MCIStatus);

  // Если компакт-диск вставлен, определяем
  // количество звуковых дорожек
  if((!bMediaPresent) && MCIStatus.dwReturn)
  {
    bMediaPresent = TRUE;
    bPaused = FALSE;
    nCurTrack = 0;

    MCIStatus.dwItem = MCI_STATUS_NUMBER_OF_TRACKS;
    mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS,
      MCI_STATUS_ITEM | MCI_WAIT, (DWORD)&MCIStatus);

    nTrackCnt = MCIStatus.dwReturn;

    // Устанавливаем диапазон изменения значений
    // для органа управления Trackbar
    SendMessage(hTrackBar, TBM_SETRANGE, TRUE, 
      MAKELPARAM(1, nTrackCnt));
  }

  // Если компакт-диск не вставлен, сбрасываем
  // номер текущей дорожки в поле диалоговой панели
  else if((bMediaPresent) && !MCIStatus.dwReturn)
  {
    bMediaPresent = FALSE;
    bPaused = FALSE;
  }

  // Если приложение находится в режиме проигрывания,
  // определяем номер текущей дорожки
  if(nCurMode == CD_PLAYING)
  {
    // Определяем текущую позицию
    MCIStatus.dwItem = MCI_STATUS_POSITION;
    mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS,
      MCI_STATUS_ITEM | MCI_WAIT, (DWORD)&MCIStatus);

    // Если номер дорожки изменился, отображаем новое
    // значение в соответствующем поле диалоговой панели 
    if(nCurTrack != (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn))
    {
      nCurTrack = (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn);
      fNeedUpdate = TRUE;
    }
  }
  return fNeedUpdate;
}

//-----------------------------------------------------
// CdPlay
// Запуск проигрывания дорожки
//-----------------------------------------------------
void CdPlay(HWND hwnd, UINT nTrack)
{
  if(bMediaPresent)
  {
    bPaused = FALSE;

    MCIPlay.dwCallback = (DWORD)hwnd;
    MCIPlay.dwFrom = MCI_MAKE_TMSF(nTrack, 0, 0, 0);

    dwrc = mciSendCommand(MCIOpen.wDeviceID, MCI_PLAY,
      MCI_FROM | MCI_NOTIFY, (DWORD)&MCIPlay);
    if(dwrc)
      mciwioError(dwrc);
  }
}

//-----------------------------------------------------
// CdStop
// Останов проигрывания дорожки
//-----------------------------------------------------
void CdStop(void)
{
  if(bMediaPresent)
  {
    bPaused = FALSE;
    nCurTrack = 0;
    mciSendCommand(MCIOpen.wDeviceID, MCI_STOP, 0, 0);
  }
}

//-----------------------------------------------------
// CdPause
// Временный останов проигрывания дорожки
//-----------------------------------------------------
void CdPause(void)
{
  if(bMediaPresent)
  {
    if(!bPaused)
    {
      bPaused = TRUE;
      mciSendCommand(MCIOpen.wDeviceID, MCI_PAUSE, 0, 0);
    }
  }
}

//-----------------------------------------------------
// CdResume
// Возобновление проигрывания после временного останова
//-----------------------------------------------------
void CdResume(HWND hwnd)
{
  if(bMediaPresent)
  {
    if(bPaused)
    {
      bPaused = FALSE;
      MCIPlay.dwCallback = (DWORD)hwnd;
      mciSendCommand(MCIOpen.wDeviceID, MCI_PLAY,
        MCI_NOTIFY, (DWORD)&MCIPlay);
    }
  }
}

//-----------------------------------------------------
// CdPlayNext
// Проигрывание следующей дорожки
//-----------------------------------------------------
void CdPlayNext(HWND hwnd)
{
  UINT nNewTrack;
  
  if(bMediaPresent)
  {
    // Если текущая дорожка - последняя,
    // начинаем проигрывание с первой дорожки.
    // Если нет - проигрываем следующую дорожку
    if(nCurTrack == nTrackCnt)
      nNewTrack = 1;
    else
      nNewTrack = nCurTrack + 1;

    CdPlay(hwnd, nNewTrack);
  }
}

//-----------------------------------------------------
// CdPlayPrev
// Проигрывание предыдущей дорожки
//-----------------------------------------------------
void CdPlayPrev(HWND hwnd)
{
  UINT nNewTrack;
  if(bMediaPresent)
  {
    // Если текущая дорожка - первая,
    // проигрываем последнюю дорожку
    if(nCurTrack <= 1)
      nNewTrack = nTrackCnt;
    else
      nNewTrack = nCurTrack - 1;

    CdPlay(hwnd, nNewTrack);
  }
}

//-----------------------------------------------------
// CdEject 
// Выталкивание компакт-диска
//-----------------------------------------------------
void CdEject(void)
{
  mciSendCommand(MCIOpen.wDeviceID, MCI_SET,
    MCI_SET_DOOR_OPEN, 0);
}

В файле cdplay.h (листинг 7.3) находятся описания функций, определенных в приложении Compact Disk Player.

Листинг 7.3. Файл cdplay\cdplay.h

BOOL APIENTRY
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL DlgProc_OnInitDialog(HWND hdlg, 
  HWND hwndFocus, LPARAM lParam);
void DlgProc_OnCommand(HWND hdlg, int id, 
  HWND hwndCtl, UINT codeNotify);
void DlgProc_OnTimer(HWND hwnd, UINT id);
void DlgProc_OnHScroll(HWND hwnd,HWND hwndCtl,UINT code,
  int pos);
void UpdateDlgControls(void);
BOOL CdInit(void);
void CdClose(void);
void mciwioError(DWORD dwrc);
BOOL CdUpdateState(HWND hdlg);
void CdPlay(HWND hwnd, UINT nTrack);
void CdStop(void);
void CdPause(void);
void CdResume(HWND hwnd);
void CdPlayNext(HWND hwnd);
void CdPlayPrev(HWND hwnd);
void CdEject(void);

#define CD_EMPTY   0
#define CD_READY   1
#define CD_PLAYING 2
#define CD_PAUSED  3

В файле resource.h (создается автоматически системой Microsoft Visual C++) находятся определения констант для работы с ресурсами приложения Compact Disk Player. Этот файл представлен в листинге 7.4.

Листинг 7.4. Файл cdplay\resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by cdplay.rc
//
#define IDD_DIALOG1                     101
#define IDB_NEXT                        1000
#define IDB_PREV                        1001
#define IDB_PLAY                        1002
#define IDB_STOP                        1003
#define IDB_PAUSE                       1004
#define IDB_RESUME                      1005
#define IDB_EJECT                       1006
#define IDT_CURTRACK                    1008
#define IDB_EXIT                        1009
#define IDC_PROGRESSBAR                 1012
#define IDC_TRACKBAR                    1014
#define IDC_ANIMATE                     1015
// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_3D_CONTROLS                     1
#define _APS_NEXT_RESOURCE_VALUE        102
#define _APS_NEXT_COMMAND_VALUE         40001
#define _APS_NEXT_CONTROL_VALUE         1016
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

Файл cdplay.rc (листинг 7.5) содержит определение ресурсов приложения Compact Disk Player.

Листинг 7.5. Файл cdplay\cdplay.rc

//Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////
// Dialog
//

IDD_DIALOG1 DIALOG DISCARDABLE  0, 0, 278, 138
STYLE DS_MODALFRAME | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | 
    WS_SYSMENU
CAPTION "Compact disk player, (C) A. Frolov, 1995"
FONT 8, "MS Sans Serif"
BEGIN
    PUSHBUTTON      ">>",IDB_NEXT,22,89,50,14
    PUSHBUTTON      "<<",IDB_PREV,76,89,50,14
    PUSHBUTTON      "Play",IDB_PLAY,129,89,50,14
    PUSHBUTTON      "Stop",IDB_STOP,22,106,50,14
    PUSHBUTTON      "Pause",IDB_PAUSE,76,106,50,14
    PUSHBUTTON      "Resume",IDB_RESUME,129,106,50,14
    PUSHBUTTON      "Eject",IDB_EJECT,203,89,50,14
    PUSHBUTTON      "Exit",IDB_EXIT,203,106,50,14
    LTEXT           "",IDT_CURTRACK,79,20,92,10
    GROUPBOX        "",IDC_STATIC,11,6,256,75
    CONTROL         "Generic1",IDC_PROGRESSBAR,
     "msctls_progress32", 0x3,25,39,184,9
    CONTROL         "Generic3",IDC_TRACKBAR,
     "msctls_trackbar32", WS_TABSTOP | 0x21,19,50,195,25
    LTEXT           "Track number:",IDC_STATIC,25,20,47,8
    GROUPBOX        "",IDC_STATIC,11,76,181,53
    CONTROL         "Animate",IDC_ANIMATE,"SysAnimate32",
                    WS_BORDER | WS_TABSTOP | 0x1,219,34,33,33
    GROUPBOX        "",IDC_STATIC,191,76,76,53
END

#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

//////////////////////////////////////////////////////////////
#endif    // APSTUDIO_INVOKED

#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 3 resource.
//////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

Описание функций

Кратко опишем функции приложения Compact Disk Player, имеющие отношение к органам управления Trackbar, Progressbar и Animation. Особенности использования интерфейса MCI были описаны в 15 томе "Библиотеки системного программиста".

WinMain

После сохранения идентификатора приложения и проверки на повторный запуск функция WinMain вызывает функцию InitCommonControls, выполняющую инициализацию библиотеки стандартных органов управления.

Далее вызывается функция DialogBox, создающая диалоговую панель. Функция этой диалоговой панели DlgProc выполняет всю полезную работу.

DlgProc

Функция DlgProc обрабатывает сообщения WM_INITDIALOG, WM_COMMAND, WM_HSCROLL и WM_TIMER, вызывая для них, соответственно, функции DlgProc_OnInitDialog, DlgProc_OnCommand, DlgProc_OnHScroll и DlgProc_OnTimer.

DlgProc1_OnInitDialog

Функция DlgProc_OnInitDialog определяет идентификаторы органов управления, расположенных в диалоговой панели, и сохраняет их для дальнейшего использования.

После этого она устанавливает диапазон изменения значений и шаг для органа Progressbar, посылая ему сообщения PBM_SETRANGE и PBM_SETSTEP. В качестве максимального используется значение из переменной nTrackCnt. Как только в устройство чтения компакт-дисков будет вставлен звуковой компакт-диск, обработчик сообщения WM_TIMER запишет в эту переменную количество дорожек.

Шаг изменения значений для органа управления Progressbar устанавливается равным единице.

Затем выполняется инициализация органа управления Trackbar. Максимальное значение устанавливается равным содержимому переменной nTrackCnt (как и для органа Progressbar), а шаг изменения - равным единице. Благодаря этому пользователь сможет перемещаться по всем дорожкам компакт-диска.

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

На последнем шаге инициализации открывается файл с видеоклипом CD.AVI, который должен находится в текущем каталоге. Этот файл открывается макрокомандой Animate_Open.

DlgProc_OnCommand

Функция DlgProc1_OnCommand обрабатывает сообщение WM_COMMAND, поступающее от органов управления диалоговой панели.

Когда пользователь нажимает кнопку Exit или клавишу <Esc> обработчик уничтожает таймер, закрывает устройство чтения компакт-диска при помощи функции CdClose и затем закрывает диалоговую панель, вызывая функцию EndDialog. В результате приложение завершает свою работу.

Обработка остальных кнопок выполняется соответствующими функциями, определенными в файле cdproc.c (листинг 7.2). Действия, которые выполняют эти функции, были описаны нами в 15 томе "Библиотеки системного программиста", поэтому для экономии места мы не будем рассказывать о них еще раз.

DlgProc_OnTimer

Функция DlgProc_OnTimer обрабатывает сообщение WM_TIMER , поступающее примерно один раз в секунду. Если диалоговая панель находится в минимизированном состоянии (что определяется с помощью функции IsIconic), обработчик сразу возвращает управление.

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

Если в результате вызова этой функции выяснилось, что состояние изменилось, функция DlgProc_OnTimer отображает номер текущей дорожки в статическом органе управления с идентификатором hwndCurTrack. После этого она изменяет положение движка Trackbar, чтобы он указывал на текущую дорожку, а также устанавливает диапазон значений и новое положение полосы Progressbar.

DlgProc_OnHScroll

Функция DlgProc_OnHScroll обрабатывает сообщение WM_HSCROLL , которое вырабатывает орган управления Trackbar.

В ответ на извещения TB_LINEDOWN и TB_PAGEDOWN обработчик вызывает функцию CdPlayNext, которая проигрывает следующую дорожку. При обработке извещений TB_LINEUP и TB_PAGEUP вызывается функция CdPlayPrev, которая проигрывает предыдущую дорожку.

Когда пользователь нажимает клавиши <Home> или <End>, обработчики извещений TB_TOP и TB_BOTTOM запускают проигрывание, соответственно, первой и последней дорожки звукового компакт-диска.

Если пользователь перемещает движок органа управления Trackbar в новое положение, обработчик извещения TB_THUMBPOSITION запускает проигрывание той дорожки, которая была выбрана и номер которой был передан вместе с этим извещением.

7.5. Орган управления Up-Down

В этом разделе мы расскажем вам об очень простом органе управления Up-Down , который мы будем называть органом циклического просмотра.

Внешне этот орган управления похож на вырожденную полосу просмотра Scrollbar, у которой нет движка, а верхняя и нижняя кнопка расположены рядом.

Вы можете создать вертикальный или горизонтальный орган циклического просмотра, просто указав соответствующий стиль окна органа Up-Down.

Обычно орган управления Up-Down используется для изменения значения какого-либо параметра (например, количества копий при печати). При этом он комбинируется с однострочным текстовым редактором, как это показано на рис. 7.7.

Рис. 7.7. Комбинирование органа управления Up-Down и однострочного текстового редактора

В этом случае можно сделать так, что когда пользователь нажимает кнопки органа управления Up-Down, в окне текстового редактора появляются значения, которые находятся в заранее заданном диапазоне. Пользователь может также ввести нужное значение непосредственно в окне текстового редактора.

Вы также можете объединить орган циклического просмотра с другим органом управления, например, с органом управления Tab или Trackbar. Способ такого объединения органов управления будет описан ниже.

Создание органа управления Up-Down

Орган управления Up-Down создается функцией CreateWindowEx на базе предопределенного класса окна UPDOWN_CLASS . При этом следует сохранить полученный от функции CreateWindowEx идентификатор для посылки окну органа Up-Down управляющих сообщений.

Вы можете также переместить изображение пиктограммы органа Up-Down из палитры редактора диалогов в проектируемую диалоговую панель. Если вы работаете с системой Microsoft Visual C++ версии 2.0 , не забудьте внести изменения в регистрационную базу данных, описанные в этой главе.

Для определения идентификатора окна органа управления Up-Down, расположенного в диалоговой панели, вы можете воспользоваться функцией GetDlgItem .

Есть еще один способ, который удобен при объединении органа Up-Down с каким-либо другим органом управления. Этот способ основан на использовании функции CreateUpDownControl :

HWND CreateUpDownControl(
  DWORD dwStyle,    // стиль окна органа Up-Down
  int   x, 	      // расположение окна
  int   y, 
  int   cx,         // размеры окна
  int   cy, 
  HWND  hParent,    // иднтификатор родительского окна
  int   nID,        // идентификатор органа Up-Down
  HINSTANCE hInst,  // идентифкатор приложения	
  HWND  hBuddy,     // идентификатор сцепленного органа
  int   nUpper,     // верхняя граница значений
  int   nLower,     // нижняя граница значений
  int   nPos);      // начальное значение

Функция CreateUpDownControl выполняет несколько действий.

Прежде всего, она создает орган управления Up-Down с помощью функции CreateWindowEx на базе предопределенного класса окна UPDOWN_CLASS . Затем она устанавливает диапазон возможных значений и начальное значение, посылая окну органа управляющие сообщения. Затем функция CreateUpDownControl подключает орган Up-Down к органу управления с идентификатором hBuddy.

Остановимся подробнее на объединении органов управления .

Окно органа управления, с которым сцепляется орган Up-Down, называется сцепленным окном (buddy window ). Так как при сцеплении органы действуют как единое целое и находятся внутри одной рамки (рис. 7.7), пользователю кажется, что он работает с одним органом управления.

Вы можете сцепить окна органов управления двумя различными способами.

Во-первых, для органа управления Up-Down можно задать стиль окна UDS_AUTOBUDDY . При этом последний сцепляется с органом управления, расположенным под ним (с предыдущим по порядку в шаблоне диалоговой панели).

Во-вторых, вы можете сцепить органы управления, послав окну органа Up-Down сообщение UDM_SETBUDDY . Именно этот способ и используется функцией CreateUpDownControl.

При создании органа Up-Down вы должны указывать обычные стили окна, такие как WS_CHILD , WS_BORDER и WS_VISIBLE , а также дополнительные, определяющие поведение этого органа управления:

Стиль Описание
UDS_ALIGNLEFT Орган Up-Down будет выровнен по левой границе окна сцепленного с ним органа управления
UDS_ALIGNRIGHT То же, но по правой границе
UDS_SETBUDDYINT В заголовок окна органа управления, сцепленного с органом Up-Down, будет автоматически записываться текущее значение, установленное для органа Up-Down. Если же орган Up-Down сцеплен с органом Listbox, заголовок изменяться не будет. Вместо этого будет изменяться номер выделенного элемента списка
UDS_NOTHOUSANDS Используется вместе со стилем UDS_SETBUDDYINT. Указывает, что при отображении текущего значения не нужно разделять разряды тысяч десятичной точкой
UDS_ARROWKEYS Если указан этот стиль, для изменения текущего значения можно использовать клавиши перемещения курсора по вертикали
UDS_HORZ Размещение окна органа Up-Down по горизонтали
UDS_WRAP При достижении в процессе перебора значений верхней или нижней границы будет происходить переход, соответственно, к нижнему или верхнему значению. Таким образом, возможна организация циклического перебора возможных значений

Вот пример создания органа Up-Down, сцепленного с однострочным текстовым редактором hwndEdit, который расположен в диалоговой панели:

hwndUpDown = CreateUpDownControl (
  WS_CHILD | WS_BORDER | WS_VISIBLE | 
  UDS_WRAP | UDS_ARROWKEYS | UDS_ALIGNRIGHT |
  UDS_SETBUDDYINT, 0, 0, 0, 0, hdlg, IDC_UPDOWN, hInst,
  hwndEdit,10, 1, 1);

Обратите внимание, что мы не указали размеры и расположение окна органа Up-Down, так как при сцеплении они устанавливаются автоматически.

Сообщения для органа управления Up-Down

Для органа управления Up-Down определены следующие сообщения:

Сообщение Описание
UDM_GETACCEL Получение параметров режима ускорения работы органа управления Up-Down
UDM_GETBASE С помощью этого сообщения приложение может определить, какая система счисления (десятичная или шестнадцатиричная) используется для органа Up-Down
UDM_GETBUDDY Определение идентификатора окна органа управления, сцепленного с органом Up-Down
UDM_GETPOS Определение текущего значения
UDM_GETRANGE Определение интервала значений
UDM_SETACCEL Установка параметров режима ускорения работы органа управления Up-Down
UDM_SETBASE Установка десятичной или шестнадцатиричной системы счисления
UDM_SETBUDDY Подключение сцепленного органа управления
UDM_SETPOS Установка текущего значения
UDM_SETRANGE Установка интервала значений

Сообщение UDM_SETACCEL позволяет установить режим ускорения. В этот режим орган управления Up-Down переходит в том случае, если пользователь держит одну из кнопок органа нажатой в течении определенного интервала времени. После этого текущее значение, связанное с органом Up-Down, начинает изменяться автоматически. Подробности вы можете найти в справочной системе SDK.

Для подключения органа Up-Down к окну другого органа управления вам может пригодиться сообщение UDM_SETBUDDY. Параметр wParam этого сообщения должен содержать идентификатор окна органа управления, к которому будет выполняться подключение.

С помощью сообщения UDM_SETRANGE вы можете установить диапазон значений (такая операция может вам пригодиться при инициализации органа Up-Down, созданного функцией CreateWindowEx). Параметр wParam этого сообщения должен быть равен нулю, а параметр lParam необходимо указать следующим образом:

lParam = (LPARAM) MAKELONG((short)nMax, (short)nMin);

Здесь nMin и nMax задают, соответственно, нижний и верхний предел изменения значения. Параметр nMin не должен быть меньше константы UD_MINVAL, а параметр nMax - больше константы UD_MAXVAL. Кроме того, минимальное и максимальное значение не должны отличаться друг от друга больше чем на значение константы UD_MAXVAL.

С помощью сообщения UDM_SETPOS можно задать текущее значение. При этом в параметр wParam нужно записать ноль, а параметр lParam подготовить так:

lParam = (LPARAM) MAKELONG((short)nPos, 0);	

Параметр nPos должен содержать новое значение.

При установке системы счисления вы должны в параметр wParam сообщения UDM_SETBASE записать значение 10 (для десятичной системы счисления) или 16 (для шестнадцатиричной). В параметр lParam нужно записать нулевое значение.

Извещение от органа управления Up-Down

Когда позиция органа управления Up-Down (т. е. текущее значение, связанное с органом) изменяется, родительское окно получает извещение UDN_DELTAPOS , которое поступает в форме сообщения WM_NOTIFY .

При этом в параметре lParam передается адрес структуры NM_UPDOWN , содержащей информацию об изменении:

typedef struct _NM_UPDOWN 
{
  NMHDR hdr;     // заголовок извещения
  int   iPos;    // текущая позиция
  int   iDelta;  // предполагаемая величина изменения позиции
} NM_UPDOWNW;

Обработчик этого извещения может разрешить или запретить изменение позиции, возвратив, соответственно, значение TRUE или FALSE. Кроме того, обработчик извещения может при необходимости изменить содержимое поля iDelta.

7.6. Орган управления Hot Key

Последний орган управления, который мы рассмотрим в этой книге, предназначен для ввода комбинации клавиш активизации приложения. Однако прежде мы должны немного рассказать о том, что это такое.

Пользователь может определить комбинацию клавиш для быстрого доступа к какой-либо часто выполняемой операции. Такую комбинацию клавиш мы будем называть комбинацией клавиш активизации или просто клавишами активизации .

В операционной системе Microsoft Windows 95 вы можете установить так называемую глобальную комбинацию клавиш для активизации при помощи сообщения WM_SETHOTKEY , послав его главному окну приложения. Заметим, что сообщение WM_SETHOTKEY не имеет никакого отношения к органу управления Hot Key .

Параметр lParam этого сообщения должен быть равен нулю. Клавиши активизации задаются параметром lParam следующим образом:

wParam = (WPARAM)MAKEWORD (vkey, modifiers);

Здесь vkey определяет виртуальный код клавиши. Список виртуальных кодов для различных клавиш вы найдете в пятой главе 12 тома "Библиотеки системного программиста" (стр. 158).

Параметр modifiers определяет клавиши модификации, такие как <Ctrl> и <Alt>, которые нужно нажимать вместе с клавишей, заданной параметром vkey для активизации. Параметр modifiers нужно задавать как комбинацию следующих значений:

Значение Клавиша модификации
HOTKEYF_ALT <Alt>
HOTKEYF_CONTROL <Ctrl>
HOTKEYF_EXT Дополнительные клавиши, такие как правая клавиша <Ctrl> или правая клавиша <Alt>. Дополнительные клавиши есть не на всех клавиатурах
HOTKEYF_SHIFT <Shift>

Ниже мы показали, как задать комбинацию <Ctrl+Alt+V> в качестве клавиш активизации:

SendMessage(hWnd, WM_SETHOTKEY, 
 (WPARAM)MAKEWORD('V', HOTKEYF_ALT | HOTKEYF_CONTROL), 0); 

Что же произойдет, когда пользователь нажмет клавиши активизации, заданные таким образом?

Главное окно приложение будет выдвинуто на первый план, а его функция получит сообщение WM_SYSCOMMAND с кодом команды SC_HOTKEY . При этом обработчик сообщения WM_SYSCOMMAND может выполнить какие-либо действия, например, послать главному окну приложения другое сообщение.

Ниже мы привели исходный текст обработчика сообщения WM_SYSCOMMAND из приложения UpDown:

void WndProc_OnSysCommand(HWND hwnd, UINT cmd, int x, int y)
{
  if(cmd == SC_HOTKEY)
  {
    SendMessage(hwndMainWindow, WM_COMMAND,
     (WPARAM)MAKELONG(ID_FILE_OPTIONS, 0), 0L);
    return 0;
  }
  return FORWARD_WM_SYSCOMMAND (hwnd,cmd,x,y,DefWindowProc);
}

Здесь обработчик посылает главному окну сообщение WM_COMMAND, имитируя выбор из меню File строки Options.

С помощью сообщения WM_SETHOTKEY пользователь может назначить для приложения только одну комбинацию клавиш активизации.

Приложение может определить, была ли назначена окну комбинация клавиш активизации и если была, то какая именно. Для этого достаточно послать окну сообщение WM_GETHOTKEY , например, так:

wHotKey = (WORD)SendMessage(hwndMainWindow,WM_GETHOTKEY,0,0); 

Если комбинация клавиш активизации не была назначена, возвращается значение NULL, а если была, то возвращается значение, которое можно использовать в качестве параметра wParam сообщения WM_SETHOTKEY.

Итак, для того чтобы определить комбинацию клавиш активизации, можно воспользоваться сообщением WM_SETHOTKEY. Что же касается подготовки значения wParam для этого сообщения, то это можно сделать с помощью органа управления Hot Key, к описанию которого мы и перейдем.

Внешне орган управления Hot Key похож на однострочный текстовый редактор (рис. 7.8).

Рис. 7.8. Окно органа управления Hot Key

Когда это окно получает фокус ввода, пользователь может нажимать комбинацию клавиш активизации, которая тотчас же будет в нем отображаться. Таким образом, пользователь сразу видит, какая комбинация клавиш выбрана.

Послав окну органа управления Hot Key сообщение HKM_GETHOTKEY , приложение может определить выбранную комбинацию клавиш и передать значение, возвращенное функцией SendMessage как параметр wParam сообщению WM_SETHOTKEY.

Этот нехитрый механизм позволяет легко организовать процедуру определения пользователем комбинации клавиш активизации.

Создание органа управления Hot Key

Вы можете создать орган управления Hot Key функцией CreateWindowEx на базе предопределенного класса окна HOTKEY_CLASS , либо добавить его к диалоговой панели, выбрав соответствующую пиктограмму из палитры редактора диалога. Например:

hwndHotKey = CreateWindowEx( 
 0, HOTKEY_CLASS, "", WS_CHILD | WS_VISIBLE,
 30, 50, 100, 32, hWnd, NULL, hInst, NULL); 

В приложении UpDown мы выбрали второй способ.

Сообщения для органа управления Hot Key

Инициализацию органа управления Hot Key выполняют с помощью сообщения HKM_SETHOTKEY . Параметр lParam должен быть равен нулю, а параметр wParam следует определить следующим образом:

wParam = MAKEWORD (vkey, modifiers);

Параметры vkey и modifiers имеют то же назначение, что и в сообщении WM_SETHOTKEY.

Кроме этого, при инициализации с помощью сообщения HKM_SETRULES можно указать запрещенные комбинации клавиш и клавиши, которые используются по умолчанию.

Параметры сообщения HKM_SETRULES приведены ниже:

wParam = (WPARAM) fwInvalid; 
lParam = MAKELPARAM (modifiers, 0);

Параметр fwInvalid указывает запрещенные клавиши и может быть комбинацией следующих значений:

Значение Запрещенные клавиши
HKCOMB_A <Alt>
HKCOMB_C <Ctrl>
HKCOMB_CA <Ctrl + Alt>
HKCOMB_NONE Запрещается использовать клавиши без модификаторов, т. е. без клавиш <Alt>, <Ctrl> или <Shift>
HKCOMB_S <Shift>
HKCOMB_SA <Shift + Alt>
HKCOMB_SC <Shift + Ctrl>
HKCOMB_SCA <Shift + Ctrl + Alt>

Когда пользователь пытается определить запрещенную комбинацию клавиш, вместо нее будет выбрана та, что указана параметром modifiers.

7.7. Приложение UpDown

Несложное приложение UpDown демонстрирует, как это нетрудно догадаться из его названия, работу органа управления Up-Down. Дополнительно в этом приложении мы создаем комбинацию клавиш активизации, работаем с сообщением WM_SETHOTKEY и органом управления Hot Key.

Если выбрать строку Options из главного меню приложения File, на экране появится диалоговая панель Value Selection, показанная на рис. 7.9.

Рис. 7.9. Диалоговая панель Value Selection

В этой панели вы можете задать с помощью органа управления Up-Down некоторое значение, и посмотреть результат, нажав кнопку OK.

В процессе инициализации своего главного окна приложение определяет комбинацию клавиш активизации <Ctrl + Alt + V>. Если нажать эту комбинацию клавиш, появится все та же диалоговая панель.

Для изменения комбинации клавиш активизации вы можете передать фокус ввода органу управления Hot Key, нажать новую комбинацию клавиш и затем нажать кнопку Set HotKey. После этого новая комбинация клавиш может быть использована для активизации диалоговой панели Value Selection.

Исходные тексты приложения UpDown

Исходные тексты всех функций приложения UpDown собраны в файле updown.c (листинг 7.6).

Листинг 7.6. Файл updown\updown.c

#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commctrl.h>
#include "resource.h"
#include "afxres.h"
#include "updown.h"

HINSTANCE hInst;
char szAppName[]  = "UpDownApp";
char szAppTitle[] = "Up-Down and HotKey Demo";

HWND hwndEdit;
HWND hwndUpDown;
HWND hwndMainWindow;

HWND hwndHotKey;
WORD wHotKey;

// -----------------------------------------------------
// Функция WinMain
// -----------------------------------------------------
int APIENTRY 
WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
        LPSTR lpCmdLine, int nCmdShow)
{
  WNDCLASSEX wc;
  HWND hWnd;
  MSG msg;
  
  hInst = hInstance;

  // Преверяем, не было ли это приложение запущено ранее
  hWnd = FindWindow(szAppName, NULL);
  if(hWnd)
  {
    if(IsIconic(hWnd))
      ShowWindow(hWnd, SW_RESTORE);
    SetForegroundWindow(hWnd);
    return FALSE;
  }

  // Регистрируем класс окна
  memset(&wc, 0, sizeof(wc));
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.hIconSm = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0);
  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC)WndProc;
  wc.cbClsExtra  = 0;
  wc.cbWndExtra  = 0;
  wc.hInstance = hInst;
  wc.hIcon = LoadImage(hInst,
    MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1);
  wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU);
  wc.lpszClassName = szAppName;
  if(!RegisterClassEx(&wc))
    if(!RegisterClass((LPWNDCLASS)&wc.style))
      return FALSE;
    
  // Создаем главное окно приложения
  hWnd = CreateWindow(szAppName, szAppTitle, 
    WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0,
    NULL, NULL, hInst, NULL);
  if(!hWnd) return(FALSE);

  // Сохраняем идентификатор главного окна
  hwndMainWindow = hWnd;

  // Отображаем окно и запускаем цикл обработки сообщений
  ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);
  while(GetMessage (&msg, NULL, 0, 0))
  {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// -----------------------------------------------------
// Функция WndProc
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hWnd, WM_CREATE,     WndProc_OnCreate);
    HANDLE_MSG(hWnd, WM_DESTROY,    WndProc_OnDestroy);
    HANDLE_MSG(hWnd, WM_COMMAND,    WndProc_OnCommand);
    HANDLE_MSG(hWnd, WM_SYSCOMMAND, WndProc_OnSysCommand);

    default:
      return(DefWindowProc(hWnd, msg, wParam, lParam));
  }
}

// -----------------------------------------------------
// Функция WndProc_OnCreate
// -----------------------------------------------------
BOOL WndProc_OnCreate(HWND hWnd, LPCREATESTRUCT lpCreateStruct)
{
  // Инициализируем библиотеку стандартных органов управления
  InitCommonControls();

  // Устанавливаем комбинацию клавиш активизации
  // диалоговой панели <Alt+Ctrl+V>
  SendMessage(hWnd, WM_SETHOTKEY, 
    (WPARAM)MAKEWORD('V', HOTKEYF_ALT | HOTKEYF_CONTROL), 0); 

  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnDestroy
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnDestroy(HWND hWnd)
{
  PostQuitMessage(0);
  return 0L;
}

// -----------------------------------------------------
// Функция WndProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  switch (id)
  {
    case ID_FILE_EXIT:  
      PostQuitMessage(0); // завершаем работу приложения
      return 0L;
      break;
      
      case ID_HELP_ABOUT:
      MessageBox(hWnd, 
        "Up-Down and HotKey Demo Application, v.1.0\n"
        "(C) Alexandr Frolov, 1995\n"
        "Email: frolov@glas.apc.org",
        szAppTitle, MB_OK | MB_ICONINFORMATION);
      return 0L;
      break;

    case ID_FILE_OPTIONS:  
    {
      DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG1), 
        NULL, DlgProc);
      return 0L;
      break;
    }
    default:
      break;
  }
  return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, 
    codeNotify, DefWindowProc);
}

// -----------------------------------------------------
// Функция DlgProc
// -----------------------------------------------------
BOOL APIENTRY
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch(msg)
  {
    HANDLE_MSG(hdlg, WM_INITDIALOG, DlgProc_OnInitDialog);
    HANDLE_MSG(hdlg, WM_COMMAND,    DlgProc_OnCommand);
    default:
      break;
  }
  return FALSE;
}

// -----------------------------------------------------
// Функция DlgProc_OnInitDialog
// Вызывается при инициализации первой страницы
// -----------------------------------------------------
BOOL DlgProc_OnInitDialog(HWND hdlg, 
  HWND hwndFocus, LPARAM lParam)
{
  // Получаем идентификатор редактора текста
  hwndEdit   = GetDlgItem(hdlg, IDC_EDIT1);
  
  // Создаем орган управления Up-Down и подключаем
  // его к редактору текста. Задаем начальное
  // и конечное значение, инициализируем 
  // редактор текста
  hwndUpDown = CreateUpDownControl(
    WS_CHILD | WS_BORDER | WS_VISIBLE | 
    UDS_WRAP | UDS_ARROWKEYS | UDS_ALIGNRIGHT |
    UDS_SETBUDDYINT,     
    0, 0, 0, 0, hdlg, IDC_UPDOWN, hInst,
    hwndEdit,10, 1, 1);

  // Получаем идентификатор органа Hot Key
  hwndHotKey = GetDlgItem(hdlg, IDC_HOTKEY);

  // Получаем код комбинации клавиш, которая
  // используется для активизации диалоговой панели
  wHotKey = 
    (WORD)SendMessage(hwndMainWindow, WM_GETHOTKEY, 0, 0); 
  
  // Устанавливаем начальное состояние органа Hot Key
  // в соответствии с полученным кодом
  if(wHotKey != 0)
    SendMessage(hwndHotKey, HKM_SETHOTKEY, wHotKey, 0); 

  return TRUE;
}

// -----------------------------------------------------
// Функция DlgProc_OnCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void DlgProc_OnCommand(HWND hdlg, int id, 
  HWND hwndCtl, UINT codeNotify)
{
  int nValue;
  char szBuf[80];
  BOOL fError;

  switch (id)
  {
    case IDOK:
    {
      // Определяем и отображаем выбранное значение
      nValue = (int)GetDlgItemInt(hdlg,
        IDC_EDIT1, &fError, FALSE);
      
      wsprintf(szBuf, "Value:\t%d", nValue);
      MessageBox(NULL, szBuf,
        szAppTitle, MB_OK | MB_ICONINFORMATION);
       
      EndDialog(hdlg, TRUE); 
      break;
    }

    case IDC_SETHOTKEYBUTTON:
    {
      // Получаем код комбинации клавиш, которая
      // используется для активизации диалоговой панели
      wHotKey = 
        (WORD)SendMessage(hwndHotKey, HKM_GETHOTKEY, 0, 0); 
      
      // Устанавливаем новую комбинацию клавиш для 
      // активизации диалоговой панели 
      SendMessage(hwndMainWindow, WM_SETHOTKEY, wHotKey, 0); 
        break;
    }

    case IDCANCEL:
    {
      EndDialog(hdlg, TRUE); 
      break;
    }
      
    default:
      return FALSE;
  }
  return TRUE;
}

// -----------------------------------------------------
// Функция WndProc_OnSysCommand
// -----------------------------------------------------
#pragma warning(disable: 4098)
void WndProc_OnSysCommand(HWND hwnd, UINT cmd, int x, int y)
{
  // Если пришло сообщение от клавиши активизации,
  // посылаем функции главного окна сообщение WM_COMMAND
  // с кодом, соответствующем строке Options меню File
  // для активизации диалоговой панели
  if(cmd == SC_HOTKEY)
  {
    SendMessage(hwndMainWindow, WM_COMMAND,
     (WPARAM)MAKELONG(ID_FILE_OPTIONS, 0), 0L);
    return 0;
  }
  return FORWARD_WM_SYSCOMMAND(hwnd, cmd, x, y, DefWindowProc);
}

Описания функция и идентификатор IDC_UPDOWN органа управления Up-Down находятся в файле updown.h (листинг 7.7).

Листинг 7.7 Файл updown\updown.h

// -----------------------------------------------------
// Описание функций
// -----------------------------------------------------
LRESULT WINAPI
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL WndProc_OnCreate(HWND hWnd, 
  LPCREATESTRUCT lpCreateStruct);
void WndProc_OnDestroy(HWND hWnd);
void WndProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify);
BOOL APIENTRY
DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL DlgProc_OnInitDialog(HWND hwnd, 
  HWND hwndFocus, LPARAM lParam);
void DlgProc_OnCommand(HWND hWnd, int id, 
  HWND hwndCtl, UINT codeNotify);
void WndProc_OnSysCommand(HWND hwnd, UINT cmd, int x, int y);
#define IDC_UPDOWN 1224

В файле resource.h (листинг 7.8), созданном автоматически, определены константы идентификаторов ресурсов приложения.

Листинг 7.8 Файл updown\resource.h

//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by UPDOWN.RC
//
#define IDR_APPMENU                     102
#define IDI_APPICON                     103
#define IDI_APPICONSM                   104
#define IDD_DIALOG1                     121
#define IDC_EDIT1                       1009
#define IDC_HOTKEY                      1010
#define IDC_SETHOTKEYBUTTON             1011
#define ID_FILE_EXIT                    40001
#define ID_HELP_ABOUT                   40003
#define ID_FILE_OPTIONS                 40029

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        126
#define _APS_NEXT_COMMAND_VALUE         40030
#define _APS_NEXT_CONTROL_VALUE         1012
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif

Файл updown.rc (листинг 7.9) содрежит определения ресурсов приложения.

Листинг 7.9 Файл updown\updown.rc

//Microsoft Visual C++ generated resource script.
//
#include "resource.h"

#define APSTUDIO_READONLY_SYMBOLS
//////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 2 resource.
//
#include "afxres.h"

//////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS

//////////////////////////////////////////////////////////////
// Menu
//

IDR_APPMENU MENU DISCARDABLE 
BEGIN
    POPUP "&File"
    BEGIN
        MENUITEM "&Options...",                 ID_FILE_OPTIONS
        MENUITEM SEPARATOR
        MENUITEM "E&xit",                       ID_FILE_EXIT
    END
    POPUP "&Help"
    BEGIN
        MENUITEM "&About...",                   ID_HELP_ABOUT
    END
END

#ifdef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
// TEXTINCLUDE
//

1 TEXTINCLUDE DISCARDABLE 
BEGIN
    "resource.h\0"
END

2 TEXTINCLUDE DISCARDABLE 
BEGIN
    "#include ""afxres.h""\r\n"
    "\0"
END

3 TEXTINCLUDE DISCARDABLE 
BEGIN
    "\r\n"
    "\0"
END

//////////////////////////////////////////////////////////////
#endif    // APSTUDIO_INVOKED

//////////////////////////////////////////////////////////////
// Icon
//

IDI_APPICON             ICON    DISCARDABLE     "updown.ico"
IDI_APPICONSM           ICON    DISCARDABLE     "updownsm.ico"

//////////////////////////////////////////////////////////////
// Dialog
//

IDD_DIALOG1 DIALOG DISCARDABLE  0, 0, 194, 95
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
CAPTION "Value selection"
FONT 8, "MS Sans Serif"
BEGIN
    EDITTEXT        IDC_EDIT1,15,9,40,13,ES_AUTOHSCROLL
    DEFPUSHBUTTON   "OK",IDOK,130,6,50,14
    PUSHBUTTON      "Cancel",IDCANCEL,130,23,50,14
    CONTROL         "Generic1",IDC_HOTKEY,"msctls_hotkey32",WS_TABSTOP,13,66,
                    80,12
    PUSHBUTTON      "Set HotKey",IDC_SETHOTKEYBUTTON,130,65,50,14
    GROUPBOX        "Hot Key",IDC_STATIC,8,49,178,40
END

//////////////////////////////////////////////////////////////
// String Table
//

STRINGTABLE DISCARDABLE 
BEGIN
    ID_FILE_EXIT            "Quits the application"
END

#ifndef APSTUDIO_INVOKED
//////////////////////////////////////////////////////////////
// Generated from the TEXTINCLUDE 3 resource.
//////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

Описание функций

Расскажем о наиболее важных функциях, определенных в приложении Up-Down.

WndProc_OnCreate

Функция WndProc_OnCreate обрабатывает сообщение WM_CREATE , поступающее в функцию главного окна приложения при его инициализации.

После инициализации библиотеки стандартных органов управления функция WndProc_OnCreate определяет комбинацию клавиш <Alt+Ctrl+V> как комбинацию клавиш активизации диалоговой панели Value Selection (рис. 7.9). Для этого главному окну приложения посылается сообщение WM_SETHOTKEY .

WndProc_OnCommand

Функция WndProc_OnCommand содержит обработчик сообщения WM_COMMAND , поступающего от главного меню приложения, а также в том случае, если пользователь нажимает комбинацию клавиш активизации диалоговой панели Value Selection.

Если вместе с этим сообщением приходит код команды ID_FILE_OPTIONS, с помощью функции DialogBox создается указанная выше диалоговая панель.

DlgProc_OnInitDialog

Функция DlgProc_OnInitDialog вызывается при инициализации диалоговой панели Value Selection, когда в окно этой диалоговой панели приходит сообщение WM_INITDIALOG .

При этом вначале соответствующий обработчик получает идентификатор окна редактора текста, к которому будет привязан орган управления Up-Down, и сохраняет его в переменной hwndEdit.

Затем с помощью функции CreateUpDownControl создается орган управления Up-Down. Функция CreateUpDownControl привязывает созданный орган управления к редактору текста hwndEdit и выполняет все необходимые инициализирующие действия.

Далее функции главного окна приложения посылается сообщение WM_GETHOTKEY . Если для приложения определена комбинация клавиш активизации, то эта комбинация используется для инициализации органа управления Hot Key, расположенного в диалоговой панели Value Selection.

DlgProc_OnCommand

Эта функция обрабатывает сообщение WM_COMMAND , поступающее в функцию окна диалоговой панели Value Selection от органов управления, расположенных в этой диалоговой панели.

Если пользователь нажимает кнопку OK, обработчик получает целое значение, соответствующее текстовой строке из редактора IDC_EDIT1. Так как последний был привязан к органу управления Up-Down, полученное значение соответствует текущему для органа Up-Down.

В том случае, когда пользователь нажимает клавишу Set HotKey, обработчик считывает новую комбинацию клавиш активизации из органа управления Hot Key, посылая последнему сообщение HKM_GETHOTKEY , и записывает ее в переменную wHotKey.

Далее полученная комбинация клавиш устанавливается в качестве комбинации клавиш активизации при помощи сообщения WM_SETHOTKEY .

WndProc_OnSysCommand

Функция WndProc_OnSysCommand обрабатывает сообщение WM_SYSCOMMAND , поступающее в главное окно приложения в том случае, если пользователь нажмет комбинацию клавиш активизации.

Этот обработчик посылает главному окну приложения сообщение WM_COMMAND с кодом команды ID_FILE_OPTIONS. В результате на экране появится диалоговая панель Value Selection.