Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Мультимедиа для Windows

© Александр Фролов, Григорий Фролов
Том 15, М.: Диалог-МИФИ, 1994, 284 стр.

[Назад] [Содеожание] [Дальше]

3.2. Интерфейс управляющих сообщений MCI

Как правило, большинство приложений, составленных на языках программирования C и C++, управляют устройством чтения CD ROM с помощью интерфейса управляющих сообщений MCI. Напомним, что приложения, использующие этот интерфейс, посылают устройствам мультимедиа управляющие сообщения с помощью функции mciSendCommand .

Рассмотрим особенности команд MCI, предназначенных для устройства чтения компакт-дисков.

MCI_OPEN

Команда MCI_OPEN не имеет никаких дополнительных возможностей и используется как обычно. Приложение должно подготовить структуру MCI_OPEN_PARMS и передать ее адрес через четвертый параметр функции mciSendCommand.

Поле lpstrDeviceType структуры MCI_OPEN_PARMS содержит указатель на строку имени устройства, или константный идентификатор устройства. Для устройства чтения CD ROM вы можете указать имя "cdaudio " или константу MCI_DEVTYPE_CD_AUDIO .

Параметр lpstrElementName не используется, так как устройство чтения компакт-дисков не работает с файлами.

Приведенный ниже фрагмент кода открывает устройство чтения компакт-дисков:

MCIOpen.lpstrDeviceType = (LPSTR)MCI_DEVTYPE_CD_AUDIO;
dwrc = mciSendCommand(NULL, MCI_OPEN,
   MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID, (DWORD)(LPVOID)&MCIOpen);

После выполнения этого фрагмента в переменную dwrc будет записан код результата завершения. При успешном завершении в поле wDeviceID структуры mciOpen будет находиться идентификатор открытого устройства.

MCI_CLOSE

Эта команда закрывает устройство. Она также не имеет никаких особенностей.

MCI_PLAY

Команда MCI_PLAY не имеет расширений для устройства чтения CD ROM.

MCI_PAUSE

Команда MCI_PAUSE останавливает выполнение операции проигрывания звукового компакт-диска и действует точно так же, как и команда MCI_STOP.

MCI_STOP

Команда MCI_STOP останавливает выполнение проигрывания компакт-диска.

MCI_SEEK

Команда MCI_SEEK позволяет выполнять позиционирование. Она не имеет расширений, специально предназначенных для устройства чтения CD ROM.

MCI_BREAK

С помощью команды MCI_BREAK указывается виртуальный код клавиши, с помощью которой можно прервать выполнение операции. По умолчанию используется комбинация клавиш <Control+Break>.

MCI_GETDEVCAPS

С помощью команды MCI_GETDEVCAPS можно определить возможности устройства чтения компакт-дисков. Для нее используется блок параметров в формате структуры MCI_GETDEVCAPS_PARMS , определенной в файле mmsystem.h следующим образом:

typedef struct tagMCI_GETDEVCAPS_PARMS {
    DWORD   dwCallback;
    DWORD   dwReturn;
    DWORD   dwItem;
} MCI_GETDEVCAPS_PARMS;
typedef MCI_GETDEVCAPS_PARMS FAR * LPMCI_GETDEVCAPS_PARMS;

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

Приведем возможные значения параметра dwItem:

Значение параметра dwItem Описание
MCI_GETDEVCAPS_CAN_EJECT Если устройство может выталкивать компакт-диск, после возврата из функции mciSendCommand в поле dwReturn будет ненулевое значение TRUE
MCI_GETDEVCAPS_CAN_PLAY Устройство может проигрывать
MCI_GETDEVCAPS_CAN_RECORD Устройство может записывать
MCI_GETDEVCAPS_CAN_SAVE Устройство может сохранять записанные данные в файле
MCI_GETDEVCAPS_COMPOUND_DEVICE Устройство может работать с файлами
MCI_GETDEVCAPS_DEVICE_TYPE Требуется определить тип устройства
MCI_GETDEVCAPS_HAS_AUDIO Устройство имеет звуковой выход
MCI_GETDEVCAPS_HAS_VIDEO Устройство имеет видеовыход
MCI_GETDEVCAPS_USES_FILES При открытии устройства требуется указывать имя файла

MCI_INFO

С помощью этой команды можно получить информацию об устройстве чтения CD ROM в виде текстовой строки.

Используется блок параметров в формате структуры MCI_INFO_PARMS :

typedef struct tagMCI_INFO_PARMS {
    DWORD   dwCallback;
    LPSTR   lpstrReturn;
    DWORD   dwRetSize;
} MCI_INFO_PARMS;
typedef MCI_INFO_PARMS FAR * LPMCI_INFO_PARMS;

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

Приведем набор флагов для команды MCI_INFO, допустимых к использованию при работе с устройством чтения компакт-дисков:

Флаг Описание
MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса
MCI_INFO_PRODUCT Требуется получить описание аппаратуры устройства

MCI_SYSINFO

С помощью этой команды можно получить системную информацию об устройстве в виде текстовой строки. Команда MCI_SYSINFO не имеет расширений для устройства чтения компакт-дисков.

MCI_STATUS

Команда MCI_STATUS используется для определения текущего состояния устройства.

Формат соответствующего блока параметров описывается структурой MCI_STATUS_PARMS :

typedef struct tagMCI_STATUS_PARMS {
    DWORD   dwCallback;
    DWORD   dwReturn;
    DWORD   dwItem;
    DWORD   dwTrack;
} MCI_STATUS_PARMS;
typedef MCI_STATUS_PARMS FAR * LPMCI_STATUS_PARMS;

Через поле dwReturn передается возвращаемая информация. Вид запрашиваемой информации определяется содержимым поля dwItem. Для устройства чтения компакт-дисков в поле dwTrack можно указать размер или номер дорожки.

Приведем возможные значения параметра dwItem:

Значение параметра dwItem Описание получаемой информации
MCI_STATUS_CURRENT_TRACK Номер текущей дорожки
MCI_STATUS_LENGTH Общий размер всех дорожек компакт-диска
MCI_STATUS_MODE Текущий режим устройства. Может иметь следующие значения:MCI_MODE_NOT_READY не готово;MCI_MODE_PAUSE пауза;MCI_MODE_PLAY проигрывание;MCI_MODE_STOP останов;MCI_MODE_OPEN открывание;MCI_MODE_RECORD запись;MCI_MODE_SEEK позиционирование
MCI_STATUS_NUMBER_OF_TRACKS Общее количество дорожек, которые можно проиграть
MCI_STATUS_POSITION Текущая позиция
MCI_STATUS_READY Если устройство готово, возвращается значение TRUE, в противном случае - FALSE
MCI_STATUS_TIME_FORMAT Текущий формат времени. Может иметь следующие значения:MCI_FORMAT_MILLISECONDS MCI_FORMAT_MSF MCI_FORMAT_TMSF
MCI_STATUS_START Начальная позиция
MCI_STATUS_TRACK В поле dwTrack записывается либо начальная позиция заданной дорожки (если дополнительно используется MCI_STATUS_POSITION), либо размер дорожки (если дополнительно используется MCI_STATUS_LENGTH)
MCI_STATUS_MEDIA_PRESENT Возвращается TRUE, если компакт-диск вставлен в устройство

MCI_SET

Команда MCI_SET предназначена для установки режима работы устройства. Вместе с этой командой используется блок параметров в формате структуры MCI_SET_PARMS :

typedef struct tagMCI_SET_PARMS {
    DWORD   dwCallback;
    DWORD   dwTimeFormat;
    DWORD   dwAudio;
} MCI_SET_PARMS;
typedef MCI_SET_PARMS FAR *LPMCI_SET_PARMS;

Поле dwTimeFormat определяет формат времени для устройства, поле dwAudio определяет выходной канал.

Приведем список флагов, которые используются вместе с командой MCI_SET:

Флаг Описание
MCI_NOTIFY Если установлен этот флаг, после завершения команды функции окна, адрес которой передан через поле dwCallback, будет послано сообщение MM_MCINOTIFY
MCI_WAIT Функция mciSendCommand вернет управление только после завершения процесса
MCI_SET_AUDIO Включение или выключение каналов, используется вместе с флагами MCI_SET_ON и MCI_SET_OFF. Поле dwAudio содержит номера канала. Дополнительно можно указать следующие константы:MCI_SET_AUDIO_ALL все каналыMCI_SET_AUDIO_LEFT левый каналMCI_SET_AUDIO_RIGHT правый канал
MCI_SET_DOOR_CLOSED По этой команде устройство защелкивает компакт-диск
MCI_SET_DOOR_OPEN Освобождение носителя данных
MCI_SET_VIDEO Включение или выключение видеосигнала, используется вместе с флагами MCI_SET_ON и MCI_SET_OFF
MCI_SET_TIME_FORMAT Установить формат времени. Используется вместе со следующими константами:MCI_FORMAT_MSF минуты, секунды, фреймы;MCI_FORMAT_MILLISECONDS в миллисекундах;MCI_FORMAT_TMSF треки, минуты, секунды, фреймы
MCI_SET_ON Включение заданного канала
MCI_SET_OFF Выключение заданного канала

При использовании формата времени MCI_FORMAT_MSF старший байт старшего слова поля dwTimeFormat не используется, младший содержит номер фрейма. Старший байт младшего слова содержит секунды, младший - минуты. Формат времени MCI_FORMAT_TMSF аналогичный, за исключением того, что старший байт старшего слова содержит номер дорожки.

Приложение MCICDPL

Если вы будете разрабатывать проигрыватель звуковых компакт-дисков, то можете взять за основу приложение MCICDPL (рис. 3.1), которое работает с устройством чтения CD-ROM при помощи управляющих сообщений MCI.

Рис. 3.1. Главное окно приложения MCICDPL

Исходный текст приложения представлен в листинге 3.1.


Листинг 3.1. Файл mcicdpl/mcicdpl.cpp


// ----------------------------------------
// Проигрыватель звуковых компакт-дисков
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <mmsystem.h>
#include <mem.h>
#include <stdlib.h>

#include "mcicdpl.hpp"

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

// Прототипы функций
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);
void mciwioError(DWORD dwrc);
void Play(HWND hwnd, UINT nTrack);

// Имя класса окна
char const szClassName[]   = "MCICDP";

// Заголовок окна
char const szWindowTitle[] = "MCI CD Player";

HINSTANCE hInst;

DWORD dwrc;
UINT nTimerID;

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;
UINT nCurTrack = 0;
UINT nTrackCnt = 0;

HWND hwndCurTrack = NULL;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine, 
        int       nCmdShow)   
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения

  if(hPrevInstance)
    return FALSE;

  // Инициализируем приложение
  if(!InitApp(hInstance))
    return FALSE;

  hInst = hInstance;

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

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

  // Создаем диалоговую панель вместо главного окна
  hwnd = CreateDialog(hInstance, szClassName, 0, NULL);

  // Если создать окно не удалось, завершаем приложение
  if(!hwnd)
    return FALSE;

  // Определяем идентификатор поля, которое используется
  // для отображения номера текущей дорожки
  hwndCurTrack = GetDlgItem(hwnd, IDT_CURTRACK);

  // Рисуем главное окно
  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  // Запускаем цикл обработки сообщений
  while(GetMessage(&msg, NULL, 0, 0))
  {
    if((hwnd == 0) || (!IsDialogMessage(hwnd, &msg)))
      DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// Выполняет регистрацию класса окна
// =====================================

BOOL InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));
  wc.style = 0;
  wc.lpfnWndProc    = (WNDPROC) WndProc;
  wc.cbClsExtra     = 0;
  wc.cbWndExtra     = DLGWINDOWEXTRA;
  wc.hInstance      = hInstance;
  wc.hIcon          = LoadIcon(hInstance, "APPICON");
  wc.hCursor        = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground  = NULL;
  wc.lpszMenuName   = (LPSTR)NULL;
  wc.lpszClassName  = (LPSTR)szClassName;

  // Регистрация класса
  aWndClass = RegisterClass(&wc);

  return (aWndClass != 0);
}

// =====================================
// Функция WndProc
// =====================================

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  switch (msg)
  {
    // ---------------------------------------
    // Обработчик сообщения WM_CREATE
    // ---------------------------------------
    case WM_CREATE:
    {
      // Создаем таймер, который нужен для периодического
      // определения состояния устройства чтения CD
      nTimerID = SetTimer(hwnd, 1, 1000, NULL);

      return 0;
    }

    // ---------------------------------------
    // Обработчик сообщения WM_COMMAND
    // ---------------------------------------
    case WM_COMMAND:
    {
      switch(wParam)
      {
        // Запуск режима проигрывания 
        case IDB_PLAY:
        {
          // Если в проигрывателе есть компакт-диск,
          // запускаем проигрывание
          if(bMediaPresent)
            Play(hwnd, 1);
          return 0;
        }

        // Останов проигрывания
        case IDB_STOP:
        {
          if(bMediaPresent)
          {
            bPaused = FALSE;
            nCurTrack = 0;
            mciSendCommand(MCIOpen.wDeviceID, MCI_STOP,
              NULL, NULL);
          }
          return 0;
        }

        // Временный останов проигрывания
        case IDB_PAUSE:
        {
          if(bMediaPresent)
          {
            if(!bPaused)
            {
              bPaused = TRUE;
              mciSendCommand(MCIOpen.wDeviceID, MCI_PAUSE,
                NULL, NULL);
            }
          }
          return 0;
        }

        // Продолжение проигрывания после
        // временного останова
        case IDB_RESUME:
        {
          if(bMediaPresent)
          {
            if(bPaused)
            {
              bPaused = FALSE;
              MCIPlay.dwCallback = (DWORD)hwnd;
              mciSendCommand(MCIOpen.wDeviceID, MCI_PLAY,
                MCI_NOTIFY, (DWORD)(LPVOID)&MCIPlay);
            }
          }
          return 0;
        }

        // Позиционирование на следующую дорожку
        case IDB_NEXT:
        {
          if(bMediaPresent)
          {
            UINT nNewTrack;

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

            Play(hwnd, nNewTrack);
          }
          return 0;
        }

        // Позиционирование на предыдущую дорожку 
        case IDB_PREV:
        {
          if(bMediaPresent)
          {
            UINT nNewTrack;

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

            Play(hwnd, nNewTrack);
          }
          return 0;
        }

        // Завершаем работу приложения
        case IDOK:
        case IDCANCEL:
        {
          SendMessage(hwnd, WM_CLOSE, 0, 0L);
          return 0;
        }

        // Выполняем команду извлечения диска из
        // устройства чтения
        case IDB_EJECT:
        {
          mciSendCommand(MCIOpen.wDeviceID, MCI_SET,
            MCI_SET_DOOR_OPEN, NULL);
          return 0;
        }
      }
    }

    // ---------------------------------------
    // Обработчик сообщения WM_TIMER
    // ---------------------------------------
    case WM_TIMER:
    {
      UINT nCurMode;

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

      // Определяем текущее состояние проигрывателя CD
      MCIStatus.dwItem = MCI_STATUS_MODE;
      mciSendCommand(MCIOpen.wDeviceID, MCI_STATUS,
        MCI_STATUS_ITEM | MCI_WAIT,
        (DWORD)(LPVOID)&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)
      {
        nMode = nCurMode;
      }

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

        nTrackCnt = MCIStatus.dwReturn;
      }

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

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

        // Если номер дорожки изменился, отображаем новое
        // значение в соответствующем поле диалоговой панели 
        if(nCurTrack != (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn))
        {
          BYTE szBuf[20];
          nCurTrack = (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn);
          SetWindowText(hwndCurTrack, itoa(nCurTrack, szBuf, 10));
        }
      }
      return 0;
    }

    // ---------------------------------------
    // Обработчик сообщения MM_MCINOTIFY
    // ---------------------------------------
    case MM_MCINOTIFY:
    {
      if(wParam == MCI_NOTIFY_SUCCESSFUL)
      {
        if(bMediaPresent)
           Play(hwnd, 1);
      }
      return 0;
    }

    // ---------------------------------------
    // Обработчик сообщения WM_CLOSE
    // ---------------------------------------
    case WM_CLOSE:
    {
      DestroyWindow(hwnd);
      return 0;
    }

    // ---------------------------------------
    // Обработчик сообщения WM_DESTROY
    // ---------------------------------------
    case WM_DESTROY:
    {
      // Закрываем устройство чтения компакт-дисков
      dwrc = mciSendCommand(MCIOpen.wDeviceID, MCI_CLOSE,
        NULL, NULL);
      if(dwrc)
        mciwioError(dwrc);

      // Уничтожаем таймер
      KillTimer(hwnd, nTimerID);

      PostQuitMessage(0);
      return 0;
    }
  }
  return DefDlgProc(hwnd, msg, wParam, lParam);
}

//-----------------------------------------------------
// 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);
}

//-----------------------------------------------------
// Play
// Запуск проигрывания дорожки
//-----------------------------------------------------
void Play(HWND hwnd, UINT nTrack)
{
  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)(LPVOID)&MCIPlay);
  if(dwrc)
  {
    mciwioError(dwrc);
    return;
  }
}

Особенностью данного приложения является отсутствие главного окна - его роль выполняет диалоговая панель.

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

Далее устанавливается формат времени MCI_FORMAT_TMSF, так как приложение будет выполнять позиционирование по дорожкам компакт-диска.

Далее с помощью функции CreateDialog создается диалоговая панель, при этом указывается зарегистрированный приложением класс окна szClassName (строка "MCICDP"):

hwnd = CreateDialog(hInstance, szClassName, 0, NULL);

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

CLASS "MCICDP"

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

hwndCurTrack = GetDlgItem(hwnd, IDT_CURTRACK);

Затем диалоговая панель отображается на экране и запускается цикл обработки сообщений, в котором вызывается функция IsDialogMessage:

while(GetMessage(&msg, NULL, 0, 0))
{
  if((hwnd == 0) || (!IsDialogMessage(hwnd, &msg)))
    DispatchMessage(&msg);
}

Во время обработки сообщения WM_CREATE создается таймер с периодом 1 секунда. Этот таймер будет использоваться для определения текущего состояния устройства чтения компакт-дисков.

Если нажать на кнопку "Play", функция окна получит сообщение WM_COMMAND с параметром IDB_PLAY. При этом приложение проверит состояние флага bMediaPresent (наличие компакт-диска в устройстве) и, если этот флаг установлен, запустит проигрывание первой дорожки. Содержимое флага bMediaPresent периодически обновляется в соответствии с действительным состоянием устройства обработчиком сообщений таймера.

Кнопка "Stop" позволяет остановить процесс проигрывания. При этом устройству посылается команда MCI_STOP. Алогично, кнопка "Pause" выполняет временный останов, соответствующий обработчик посылает управляющее сообщение с кодом MCI_PAUSE. Для продолжения проигрывания после временного останова используется команда MCI_PLAY, для которой не задается начальная позиция (команда MCI_RESUME не поддерживается драйвером устройства чтения CD ROM). В этом случае проигрывание возобновляется с текущей позиции, то есть с прерванного места.

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

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

Код текущего состояния устройства записывается в переменную nMode. Далее обработчик сообщения WM_TIMER с помощью команды MCI_STATUS проверяет, вставлен ли в устройство компакт-диск. Если диск вставлен, определяется количество дорожек. определенное значение сохраняется в переменной nTrackCnt.

Номер текущей дорожки также определяется каждый раз при обработке сообщения таймера (при условии, что компакт-диск вставлен в устройство и устройство находится в режиме проигрывания). Если этот номер изменился, новое значение отображается в статическом органе управления диалоговой панели с идентификатором hwndCurTrack:

if(nCurTrack != (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn))
{
  BYTE szBuf[20];
  nCurTrack = (UINT)MCI_TMSF_TRACK(MCIStatus.dwReturn);
  SetWindowText(hwndCurTrack, itoa(nCurTrack, szBuf, 10));
}

Макрокоманда MCI_TMSF_TRACK извлекает номер дорожки из значения, возвращенного командой MCI_STATUS с параметром MCI_STATUS_POSITION.

В файле mmsystem.h определены и другие макрокоманды, которые используются аналогичным образом для получения других полей: MCI_TMSF_FRAME , MCI_TMSF_MINUTE , MCI_TMSF_SECOND , MCI_MSF_FRAME , MCI_MSF_MINUTE , MCI_MSF_SECOND . Можно сделать и обратные преобразования. Например, можно использовать макрокоманду MCI_MAKE_TMSF для упаковки в двойное слово номера дорожки, минут, секунд и номера фрейма:

dwFormat = MCI_MAKE_TMSF(track, min, sec, frame);

В нашем приложении предусмотрена обработка сообщения MM_MCINOTIFY . Это сообщение используется для того чтобы "зациклить" проигрывание компакт-диска. После того как команда проигрывания будет выполнена до конца (то есть после того как будет завершено проигрывание последней дорожки компакт-диска), функция окна приложения получит сообщение MM_MCINOTIFY с параметром MCI_NOTIFY_SUCCESSFUL. Обработчик этого сообщения выглядит очень просто - он запускает проигрывание заново с первой дорожки:

case MM_MCINOTIFY:
{
  if(wParam == MCI_NOTIFY_SUCCESSFUL)
  {
    if(bMediaPresent)
       Play(hwnd, 1);
  }
  return 0;
}

При завершении работы приложения обработчик сообщения WM_DESTROY закрывает устройство чтения компакт-дисков и уничтожает таймер.

Функция Play, определенная в нашем приложении, запускает проигрывание компакт-диска начиная с заданной дорожки. В ней для формирования позиции используется макрокоманда MCI_MAKE_TMSF :

MCIPlay.dwFrom     = MCI_MAKE_TMSF(nTrack, 0, 0, 0);

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

Файл mcicdpl.hpp (листинг 3.2) содержит определения констант, используемых в приложении.


Листинг 3.2. Файл mcicdpl/mcicdpl.hpp


#define IDT_CURTRACK 200
#define IDB_STOP   101
#define IDB_PAUSE  102
#define IDB_RESUME 103
#define IDB_NEXT   104
#define IDB_PREV   105
#define IDB_EJECT  106
#define IDB_PLAY   100

Файл описания ресурсов приложения представлен в листинге 3.3. Он содержит определение пиктограммы и диалоговой панели, выступающей в роли главного окна приложения.


Листинг 3.3. Файл mcicdpl/mcicdpl.rc


#include "g:\tcwin\include\windows.h"
#include "mcicdpl.hpp"

APPICON ICON "mcicdpl.ico"

MCICDP DIALOG 45, 20, 153, 57
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX
CLASS "MCICDP"
CAPTION "Compact Disk Player"
BEGIN
  PUSHBUTTON "Play", IDB_PLAY, 84, 11, 31, 11,
     WS_CHILD | WS_VISIBLE | WS_TABSTOP
  PUSHBUTTON "Stop", IDB_STOP, 118, 11, 31, 11,
     WS_CHILD | WS_VISIBLE | WS_TABSTOP
  PUSHBUTTON "Pause", IDB_PAUSE, 84, 26, 31, 11,
     WS_CHILD | WS_VISIBLE | WS_TABSTOP
  PUSHBUTTON "Resume", IDB_RESUME, 118, 26, 31, 11,
     WS_CHILD | WS_VISIBLE | WS_TABSTOP
  PUSHBUTTON ">>I", IDB_NEXT, 6, 26, 31, 11,
     WS_CHILD | WS_VISIBLE | WS_TABSTOP
  PUSHBUTTON "I<<", IDB_PREV, 40, 26, 31, 11,
     WS_CHILD | WS_VISIBLE | WS_TABSTOP
  PUSHBUTTON "Eject", IDB_EJECT, 6, 41, 65, 11,
     WS_CHILD | WS_VISIBLE | WS_TABSTOP
  DEFPUSHBUTTON "Exit", IDOK, 84, 41, 65, 11,
     WS_CHILD | WS_VISIBLE | WS_TABSTOP
  LTEXT "Track:", -1, 12, 7, 35, 8,
     WS_CHILD | WS_VISIBLE | WS_GROUP
  LTEXT "00", IDT_CURTRACK, 35, 7, 16, 8,
     WS_CHILD | WS_VISIBLE | WS_GROUP
  CONTROL "", -1, "static",
     SS_BLACKFRAME | WS_CHILD | WS_VISIBLE, 6, 4, 65, 15
END

Файл определения модуля приложения MCICDPL представлен в листинге 3.4.


Листинг 3.4. Файл mcicdpl/mcicdpl.def


NAME        MCICDPL
DESCRIPTION 'Приложение MCICDPL, (C) 1994, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
STACKSIZE   8194
HEAPSIZE    1024
CODE        preload moveable discardable
DATA        preload moveable multiple

[Назад] [Содеожание] [Дальше]