Мультимедиа для 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:
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_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:
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_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 |