Мультимедиа для Windows© Александр Фролов, Григорий ФроловТом 15, М.: Диалог-МИФИ, 1994, 284 стр. 5.3. Работа с окном MCIВ этом разделе мы научимся создавать приложения для проигрывания файлов мультимедиа и дорожек компакт-дисков (а также для записи звуковых файлов) с использованием окна MCI. Однако перед тем как приступить к описанию способов работы с этим окном, рассмотрим пример простейшего приложения, использующего окно MCI для проигрывания файлов мультимедиа. Приложение MCIWNDПриложение MCIWND создает окно MCI (рис. 5.10) и... все!
Рис. 5.10. Окно MCI Это окно, когда в него ничего не загружено, имеет стандартный заголовок и три органа управления - кнопку воспроизведения, кнопку доступа к меню и полосу просмотра. Запустите загрузочный модуль этого приложения и нажмите кнопку доступа к меню (рис. 5.11).
Рис. 5.11. Кнопка доступа к меню На экране появится почти стандартная диалоговая панель "Open", с помощью которой вы сможете выбрать файл мультимедиа для просмотра или прослушивания (рис. 5.12).
Рис. 5.12. Диалоговая панель "Open" Обратите внимание, что в правом нижнем углу диалоговой панели имеются органы управления (кнопка и полоса просмотра), предназначенные для предварительного просмотра или прослушивания содержимого файла. Это очень удобно, так как при поиске файла не нужно много раз открывать и закрывать диалоговую панель "Open". После загрузки wav-файла меню приложения модифицируется (рис. 5.13).
Рис. 5.13. Меню для работы с wav-файлом В нем появляется строка "Close", с помощью которой можно закрыть файл, строка "Copy", позволяющая скопировать содержимое файла в Clipboard, а также строка "Command...". Последняя предназначена для передачи устройству произвольной управляющей строки MCI (соответствующая диалоговая панель показана на рис. 5.14).
Рис. 5.14. Диалоговая панель для передачи управляющей строки MCI При выборе avi-файлов можно посмотреть их содержимое в небольшом окне (рис. 5.15).
Рис. 5.15. Выбор avi-файла Для работы с avi-файлами используется расширенное меню (рис. 5.16).
Рис. 5.16. Меню для работы с avi-файлами Строка "View" предназначена для управления размером окна. С помощью строк "Volume" и "Speed" можно регулировать, соответственно, громкость звука и скорость воспроизведения видео. Строка "Configure..." предназначена для установки параметров проигрывателя. При ее выборе на экране появляется диалоговая панель "Video Playback Options" (рис. 5.17).
Рис. 5.17. Диалоговая панель "Video Playback Options" В поле "Video Mode" вы можете включить режим отображения видео в окне (переключатель "Window") или на полном экране видеомонитора (переключатель "Full Screen"). Переключатель "Zoom by 2" позволяет уменьшить размер окна в два раза. Если включен переключатель "Skip video frames if behind", для обеспечения непрерывности звука при необходимости будут пропускаться видеокадры. Таким образом, приложение MCIWND выполняет довольно много функций. Вы, возможно, будете удивлены тем, что исходный текст приложения занимает не более двух страниц (листинг 5.1). Листинг 5.1. Файл mciwnd/mciwnd.c #include <windows.h> #include <vfw.h> static char szAppName[]="MCIWnd"; static HWND hwnd; // ===================================== // Функция WinMain // ===================================== int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; WORD wVersion; // Проверяем версию Video for Windows wVersion = HIWORD(VideoForWindowsVersion()); if(wVersion < 0x010a) { MessageBox(NULL, "Используйте Video for Windows" " версии 1.1 или более поздней версии", "MCIWnd Error", MB_OK | MB_ICONHAND); return FALSE; } // Создаем окно класса MCIWND hwnd = MCIWndCreate(NULL, hInstance, MCIWNDF_SHOWNAME | MCIWNDF_SHOWMODE | WS_OVERLAPPEDWINDOW | WS_VISIBLE, NULL); if(hwnd == NULL) return -1; // Устанавливаем заголовок окна SetWindowText(hwnd, szAppName); // Запускаем цикл обработки сообщений while(GetMessage(&msg,NULL,0,0)) { TranslateMessage(&msg); DispatchMessage(&msg); if(!IsWindow(hwnd)) PostQuitMessage(0); } return msg.wParam; } Исходный текст приложения подготовлен для трансляторов Microsoft C++ версии 7.0 или Microsoft Visual C++ версий 1.0 или 1.5, так как поставляющаяся в составе Video for Windows Development Kit библиотека vfw.lib совместима именно с этими трансляторами. После файла windows.h в исходный текст необходимо включить файл vfw.h , который поставляется вместе с Video for Windows Development Kit. Он содержит определения всех необходимых констант, структур данных и прототипы функций. В самом начале работы приложение вызывает функцию VideoForWindowsVersion , возвращающую в старшем слове версию Video for Windows: wVersion = HIWORD(VideoForWindowsVersion()); Версия Video for Windows должна быть не ниже 1.1. Далее приложение создает окно MCI, вызывая для этого функцию MCIWndCreate : hwnd = MCIWndCreate(NULL, hInstance, MCIWNDF_SHOWNAME | MCIWNDF_SHOWMODE | WS_OVERLAPPEDWINDOW | WS_VISIBLE, NULL); Функция MCIWndCreate позволяет определить обычные стили окна, такие как WS_OVERLAPPEDWINDOW и WS_VISIBLE, а также специфические для окна MCI - MCIWNDF_SHOWNAME и MCIWNDF_SHOWMODE. Позже мы рассмотрим подробнее эту функцию и дополнительные стили окна. После создания окна запускается цикл обработки сообщений, который имеет одну особенность - в нем периодически вызывается функция IsWindow , которая определена в стандартном программном интерфейсе Windows: if(!IsWindow(hwnd)) PostQuitMessage(0); Эта функция проверяет идентификатор окна, передаваемого ей через параметр. Если этот идентификатор правильный, возвращается TRUE. После того как пользователь уничтожит окно MCI, его идентификатор станет недействительным. В этом случае функция IsWindow вернет значение FALSE и будет вызвана функция PostQuitMessage, в результате чего работа приложения завершится. Вот и все! По сложности исходного текста это приложение напоминает наши первые приложения из 10 тома "Библиотеки системного программиста", однако выполняемые им функции во много раз сложнее. Все дело тут, разумеется, в реализации класса окна MCI. Файл описания ресурсов приведен в листинге 5.2. Листинг 5.2. Файл mciwnd/mciwnd.rc AppIcon ICON mciwnd.ico Файл определения модуля для приложения MCIWND не имеет никаких особенностей (листинг 5.3). Листинг 5.3. Файл mciwnd/mciwnd.def NAME MCIWNDPL DESCRIPTION 'Приложение MCIWND, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 8196 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple Теперь о том, как из исходного текста создать загрузочный модуль приложения MCIWND. При использовании транслятора Microsoft C++ версии 7.0 вы можете воспользоваться файлом makefile , представленном в листинге 5.3. Листинг 5.3. Файл mciwnd/makefile NAME = mciwnd OBJ = mciwnd.obj LIBS = libw slibcew vfw !if "$(DEBUG)" == "NO" DEF = CLOPT = MASMOPT = LINKOPT = !else DEF = -DDEBUG CLOPT = -Zid MASMOPT = -Zi LINKOPT = /CO/LI !endif CC = cl -c -W3 -AS -Zp -G2sw -Oxas $(DEF) $(CLOPT) -DWIN31 ASM = masm -Mx $(MASMOPT) LINK= link /NOE/NOD/LI/MAP/AL:16/ONERROR:NOEXE $(LINKOPT) RC = rc .c.obj: $(CC) $*.c .asm.obj: $(ASM) $*; goal: $(NAME).exe $(NAME).exe: $(OBJ) $(NAME).res $(NAME).def makefile $(LINK) $(OBJ), $(NAME), $(NAME),$(LIBS), $(NAME).def $(RC) -31 $(NAME).res -mapsym $(NAME).map $(NAME).res: $(NAME).rc $(NAME).ico $(RC) -r $(NAME).rc clean: del $(NAME).exe del *.res del *.obj del *.map del *.sym del *.pdb copy: copy $(NAME).exe ..\..\bin copy $(NAME).sym ..\..\bin depend: mv makefile makefile.old sed "/^# START Dependencies/,/^# END Dependencies/D" makefile.old > makefile del makefile.old echo # START Dependencies >> makefile includes -l *.c *.asm >> makefile echo # END Dependencies >> makefile Особенностью транслятора Microsoft C++ версии 7.0 (а также версии 8.0, входящей в состав Visual C++, и трансляторов более ранних версий, запускаемых в среде MS-DOS) является использование переменных среды. Для правильной установки переменных среды подготовьте bat-файл, содежащий следующие команды (предполагается, что транслятор установлен в каталоге g:msvc, а Video for Windows Development Kit - в каталоге g:\vfwdk): @echo off set TOOLROOTDIR=G:\MSVC set PATH=G:\MSVC\BIN;%PATH% set INCLUDE=G:\MSVC\INCLUDE;g:\vfwdk\inc;%INCLUDE% set LIB=G:\MSVC\LIB;g:\vfwdk\lib;g:\windev\lib;%LIB% set INIT=G:\MSVC;%INIT% Находясь в среде MS-DOS, сделайте текущим каталог, содержащий все файлы приложения MCIWND, и, после запуска приведенного выше пакетного файла, запустите программу nmake без параметров. В результате будет создан загрузочный модуль приложения. Для того чтобы загрузочный модуль не содержал отладочной информации, добавьте в начало файла makefile следующую строку: DEBUG = NO Можно также запустить программу nmake с параметром DEBUG=NO: nmake DEBUG=NO Намного удобнее работать в среде Visual C++. Вместе с исходными текстами приложения MCIWND на дискете поставляется файл проекта mciwnd.mak, предназначенный для системы Visual C++. Запустите Visual C++ и из меню "Project" выберите строку "Open...". С помощью появившейся диалоговой панели откройте файл проекта mciwnd.mak. Нажмите на самую левую кнопку в полосе инструментов. Появится список файлов, имеющих отношение к проекту. Выберите файл исходного текста. Возможно, что при этом вместо русских букв в окне редактирования вы увидите нечто, не поддающееся прочтению. В этом случае следует установить для редактора шрифт с русскими буквами. Для изменения шрифта сделайте текущим окно редактирования и выберите в меню "Options" строку "Font...". На экране появится диалоговая панель "Font". Выберите в ней подходящий шрифт, имеющий русские буквы, и нажмите кнопку "Use as Default Font". При этом для всех создаваемыех вновь окон будет использоваться выбранный вами шрифт. Для завершения работы с диалоговой панелью нажмите кнопку "OK". Не забудьте добавить пути к каталогам vfwdk\inc, wfwdk\lib и windev\lib с помощью диалоговой панели "Directories", которую можно вызвать, если в меню "Options" выбрать строку "Directories...". После всех этих подготовительных действий выберите из меню "Project" строку "Build MCIWND.EXE". Будет запущен процесс создания загрузочного модуля (в фоновом режиме). После завершения этого процесса можно запустить приложение, выбрав из этого же меню строку "Execute MCIWND.EXE", или перейти в режим отладки, воспользовавшись меню "Debug". Использование класса окна MCIПриложение может создать окно MCI, указав его параметры, управлять им с помощью передачи сообщений через удобный в использовании набор макрокоманд и функций, а также удалить окно MCI. Этих функций и макрокоманд так много, что мы не можем описать их все подробно из-за ограниченного объема нашей книги. Рассмотрим некоторые, самые полезные на наш взгляд, функции и макрокоманды, предназначенные для работы с окном MCI. Полную информацию вы сможете найти в документации, которая поставляется вместе с Video for Windows Development Kit. Создание окнаДля создания окна MCI проще всего воспользоваться функцией MCIWndCreate . Функция MCIWndCreate HWND MCIWndCreate( HWND hwndParent, // идентификатор родительского окна HINSTANCE hInstance, // идентификатор приложения DWORD dwStyle, // стиль окна LPSTR szFile); // имя устройства или путь к файлу Параметры функции: hwndParent Через этот параметр приложение передает функции идентификатор родительского окна, то есть окна, создавшего окно MCI. Если родительского окна нет, в качестве этого параметра можно указать NULL hInstance Идентификатор приложения, полученных через параметры функции WinMain или LibMain (для DLL-библиотеки) dwStyle Стиль создаваемого окна. Можно указывать стили, стандартные для функции CreateWindow, а также дополнительные, список которых приведен ниже. Если стандратные стили не указаны (что допустимо), то если есть родительское окно, используются стили WS_CHILD, WS_BORDER, и WS_VISIBLE. Если же параметр hwndParent указан как NULL, используются стили WS_OVERLAPPEDWINDOW и WS_VISIBLE. Для создания невидимого окна следует использовать один из стандартных стилей, например, WS_CHILD szFile Указатель на текстовую строку, содержащую имя устройства (например, "cdaudio") или путь к файлу Возвращаемое значение: Идентификатор созданного окна при успешном завершении или NULL при ошибке Привдем список дополнительных стилей, которые можно использовать при создании окна MCI.
Другой способ создания окна MCI заключается в регистрации класса окна MCIWND_WINDOW_CLASS функцией MCIWndRegisterClass , не имеющей парамеров, и создании на базе этого класса окна функцией CreateWindow . В случае успеха функция MCIWndRegisterClass возвращает значение FALSE. Удаление окнаЕсли окно MCI больше не нужно, его можно удалить макрокомандой MCIWndDestroy . Идентификатор удаляемого окна MCI передается этой функции в качестве единственного параметра: #define MCIWndDestroy(hwnd) (VOID)MCIWndSM(hwnd, WM_CLOSE, 0, 0) Макро MCIWndSM определяется как функция SendMessage (см. файл mciwnd.h, поставляемый вместе с Video for Windows Development Kit и включаемый в файл vfw.h). Для того чтобы закрыть устройство, открытое ранее в окне MCI без удаления окна, используйте макрокоманду MCIWndClose, передав ей идентификатор окна. Загрузка файла или выбор устройстваПри создании окна MCI вы можете указать имя устройства или файл мультимедиа, передав указатель на соответствующую текстовую строку через последний параметр функции MCIWndCreate. Можно использовать и другие способы. Макрокоманда MCIWndOpen позволяет для созданного ранее окна открыть устройство или файл, например: MCIWndOpen(hwnd, "push.wav", MCIWNDOPENF_NEW); Через первый параметр передается идентификатор окна MCI, через второй - указатель на строку, содержащую имя устройства или файла. В третьем параметре при создании нового файла нужно указать флаг MCIWNDOPENF_NEW . Напомним, что закрыть устройство или файл можно с помощью макрокоманды MCIWndClose . Функция MCIWndOpenDialog позволяет вывести на экран диалоговую панель, с помощью которой пользователь может выбрать файл и загрузить его в окно MCI. В качестве единственного параметра этой функции следует передать идентификатор окна MCI. Функция возвращает значение 0L (размером в двойное слово) в случае успеха или код ошибки. Для выбора файла можно также использовать функцию GetOpenFileNamePreview , входящую в программный интерфейс Video for Windows и аналогичную функции GetOpenFileName из библиотеки commdlg.dll . Диалоговая панель, появляющаяся на экране при вызове этой функции, содержит окно для предварительного прослушивания или просмотра файла (рис. 5.15). Новый файл создается макрокомандой MCIWndNew , которой в качестве первого параметра следует передать идентификатор окна MCI, а в качестве второго - указатель на текстовую строку, содержащей имя устройства. Пример использования этой макрокоманды ести в приложении MCIWNDC, которое мы скоро рассмотрим. Управление проигрываниемМакрокоманда MCIWndPlay включает режим проигрывания для окна MCI, идентификатор которого передается ей в качестве единственного параметра. Если вам нужно начать проигрывание с заданной позиции, воспользуйтесь макрокомандой MCIWndPlayFrom . Первый параметр этой макрокоманды задает идентификатор окна MCI, второй (размером в двойное слово) - позицию для начала проигрывания. Есть возможность задать конечную позицию. Макрокоманда MCIWndPlayFromTo , имеющая три параметра, аналогична макрокоманде MCIWndPlayFrom , но позволяет через третий параметр задать конечную позицию, при достижении которой проигрывание будет остановлено. Вы даже можете запустить проигрывние в обратную сторону - от конца к началу файла (если драйвер устройства позволит вам это сделать). Достаточно вызвать макрокоманду MCIWndPlayReverse . Она имеет один параметр (идентификатор окна MCI) и запускает проигрывание от текущей позиции к началу файла. Макрокоманда MCIWndSetRepeat позволяет включить режим циклического проигрывания. Через первый параметр этой макрокоманде передается идентификатор окна MCI, через второй для включение режима циклического проигрывания нужно передать значение TRUE. Макрокоманды MCIWndStop и MCIWndPause предназначены для выполнения, соответственно, останова и временного останова проигрывания. Для того чтобы продолжить проигрывание после временного останова, используйте макрокоманду MCIWndResume . Все три макрокоманды имеют только один параметр - идентификатор окна MCI. Управление записью и сохранение данныхДля включения режима записи с текущей позиции предназначена макрокоманда MCIWndRecord . В качестве единственного параметра ей нужно передать идентификатор окна MCI. С помощью этой макрокоманды вы сможете организовать запись звуковых данных (как это сделано в нашем приложении MCIWNDC, которое мы рассмотрим чуть позже). С помощью окна MCI вы не можете записывать avi-файлы, для этого предназначено окно класса AVICap. Для сохранения записанных данных можно воспользоваться макрокомандами MCIWndSave или MCIWndSaveDialog . Макрокоманда MCIWndSave имеет два параметра - идентификатор окна MCI и указатель на текстовую строку, в которой должен находиться путь к файлу. Записанные данные будут сохранены в этом файле. При вызове макрокоманды MCIWndSaveDialog , имеющий один параметр (идентификатор окна MCI) на экран выводится диалоговая панель, позволяющая пользователю сохранить данные в файле. Это модифицированная диалоговая панель "Save As..." с возможностью предварительного просмотра или прослушивания содержимого файлов мультимедиа. Кстати, эту же панель вы можете вывести и отдельно с помощью функции GetSaveFileNaamePreview , аналогичной функции GetSaveFileName из библиотеки commdlg.dll. ПозиционированиеМакрокоманда MCIWndSeek позволяет установить новую позицию для окна MCI, идентификатор которого передается ей в качестве первого параметра (здесь имеется в виду не расположение окна MCI на экране, а текущая позиция при проигрывании мультимедиа-файла). Через второй параметр передается значение новой позиции в формате двойного слова. Макрокоманды MCIWndHome и MCIWndEnd позволяют установить текущую позицию, соотетственно, в начало и конец. Обе эти макрокоманды имеют только один параметр - идентификатор окна MCI. С помощью макрокоманды MCIWndStep можно выполнить продвижение текущей позиции на один шаг. Величина шага задается в формате двойного слова в качестве второго параметра. Первый параметр - как всегда, идентификатор окна MCI. Другие макрокомандыПеречислим некоторые другие полезные макрокоманды, имеющие отношение к окну MCI. Макрокоманды MCIWndCanConfig , MCIWndCanEject , MCIWndCanPlay , MCIWndCanRecord , MCIWndCanSave , MCIWndCanWindow позволяют определить, соответственно, возможность конфигурирования, автоматической смены носителя, проигрывания, записи, сохранения и проигрывания в окне. Все эти макрокоманды имеют один параметр - идентификатор окна MCI. Если та или иная возможность поддерживается, соответствующая макрокоманда возвращает значение TRUE, в противном случае FALSE. В любой момент времени приложение может изменить стиль окна MCI, вызвав макрокоманду MCIWndChangeStyles . Пример испльзования этой макрокоманды есть в приложении MCIWNDC (см. ниже). Для определения текущего стиля окна MCI можно воспользоваться макрокомандой MCIGetStyles . Несколько макрокоманд предназначены для определения характеристик файла или такого носителя данных, как звуковой компакт-диск, загруженного в окно MCI. С помощью макрокоманд MCIWndGetLength , MCIWndGetStart , MCIWndGetEnd , MCIWndGetPosition , MCIWndGetPositionString приложение может определить, соотетственно, длину файла, начальную позицию, конечную позицию, текущую позицию и текущую позицию в виде текстовой строки. Приложение может установить скорость проигрывания, громкость и размеры окна MCI, вызвав, соответственно, макрокоманды MCIWndSetSpeed , MCIWndSetVolume , MCIWndSetZoom . Есть макрокоманды, с помощью которых можно определить текущее значение для скорости проигрывания, громкости и размеров окна MCI - MCIWndGetSpeed , MCIWndGetVolume и MCIWndGetZoom . Есть макрокоманды для изменения формата времени и определени текущего формата времени (MCIWndSetTimeFormat , MCIWndGetTimeFormat , MCIWndUseFrames , MCIWndUseTime ), для обновления информации о позиции при замене носителя данных (MCIWndValidateMedia ), для работы с таймером, палитрами и некоторые другие. Если же этого обширного списка все же не хватит, то с помощью макрокоманды MCIWndSendString вы сможете передать окну любую команду MCI, лишь бы ее поддерживало используемое устройство. Ответ драйвера на посланную команду можно получить в виде текстовой строки при помощи макрокоманды MCIWndReturnString . Извещения для родительского окнаПри создании окна MCI вы можете указать (определив соответствующие стили), что в случае возникновения ошибки, замены носителя данных, изменения режима, текущей позиции или размера окна отображения родительское окно должно получать извещающие сообщения. Рассмотрим эти сообщения. MCIWNDM_NOTIFYERRORСообщение MCIWNDM_NOTIFYERROR передается родительскому окну при возникновении ошибки. Параметр wParam содержит идентификатор окна MCI, параметр lParam - указатель на текстовую строку с описанием ошибки. MCIWNDM_NOTIFYMEDIAПри изменении носителя данных (устройства или файла) родительское окно получает сообщение MCIWNDM_NOTIFYMEDIA . Параметр lParam содержит указатель на текстовую строку с именем файла или устройства. Если устройство или файл закрыты, этот параметр имеет значение NULL. Параметр wParam не используется. MCIWNDM_NOTIFYMODEПри изменении режима работы родительское окно получает сообщение MCIWNDM_NOTIFYMODE , причем код нового режима находится в параметре lParam. Параметр wParam не используется. MCIWNDM_NOTIFYPOSСообщение MCIWNDM_NOTIFYPOS передается родительскому окну при изменении текущей позиции. Новая позиция находится в параметре lParam. Параметр wParam не используется. MCIWNDM_NOTIFYSIZEЕсли пользователь изменил размер окна MCI, родительское окно получает сообщение MCIWNDM_NOTIFYSIZE . Параметр wParam содержит идентификатор окна MCI, параметр lParam не используется и равен нулю. Приложение MCIWNDCТеперь нашей задачей будет создание такого приложения, которое выполняет управление окном MCI с помощью перечисленных выше макрокоманд. Приложение MCIWNDC (рис. 5.18) может проигрывать avi- и wav-файлы, файлы в стандарте MIDI, а также дорожки звуковых компакт-дисков. Кроме этого, приложение может записывать wav-файлы.
Рис. 5.18. Меню "File" приложения MCIWNDC Для проигрывания файлов мультимедиа их следует открыть при помощи строки "Open..." меню "File". При этом на экран будет выведена диалоговая панель "Open" с возможностью предварительного просмотра или прослушивания содержимого файла (рис. 5.19).
Рис. 5.19. Диалоговая панель "Open" приложения MCIWNDC Меню "Movie" (рис. 5.20) и "Styles" (рис. 5.22) предназначены для управления окном MCI.
Рис. 5.20. Меню "Movie" приложения MCIWNDC Строки "Play", "Play Reverse", "Record" и "Stop" предназначены, соответственно, для проигрывания, проигрывания в обратном направлении, записи и выполнения останова. С помощью строк "Home" и "End" выполняется позиционирование на начало и конец файла. Строки "Step Fwrd" и "Step Back" дают возможность выполнять пошаговое перемещение вперед и назад. Выбрав строку "Info...", вы увидите на экране диалоговую панель "Media Info" (рис. 5.21), в которой будет отображено имя устройства, путь к загруженному в окно MCI файлу и размер этого файла.
Рис. 5.21. Диалоговая панель "Media Info" С помощью строки "Play Bar" меню "Styles" (рис. 5.22) вы можете убрать или возвратить на место органы управления окном MCI.
Рис. 5.22. Меню "Styles" приложения MCIWNDC Диалоговая панель, используемая при сохранении записанных wav-файлов, содержит средства предварительного просмотра или прослушивания (рис. 5.23).
Рис. 5.23. Диалоговая панель "Save As..." приложения MCIWNDC Приложение MCIWNDC способно также проигрывать дорожки звуковых компакт-дисков. Внешний вид окна MCI, которое используется для этого, показан на рис. 5.24.
Рис. 5.24. Окно MCI для проигрывания дорожек звукового компакт-диска Несмотря на обилие возможностей, исходный текст приложения MCIWNDC занимает немного места (листинг 5.4). Листинг 5.4. Файл mciwndc/mciwndc.cpp // ------------------------------------------------ // Приложение MCIWNDC // Использование класса окна MCIWnd для // проигрывания и записи файлов мультимедиа // ------------------------------------------------ #define STRICT #include <windows.h> #include <windowsx.h> #include <commdlg.h> #include <memory.h> #include <vfw.h> #include "mciwndc.h" // Прототипы функций BOOL InitApp(HINSTANCE); LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM); BOOL mciwndSelectFile(LPSTR lpszFileName); // Глобальные переменные char const szClassName[] = "MCIWNDCClass"; char const szMovieClass[] = MCIWND_WINDOW_CLASS; char const szWindowTitle[] = "MCIWnd Player & Recorder"; HINSTANCE hInst; HWND hwndMovie = NULL; // ===================================== // Функция WinMain // ===================================== int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { MSG msg; // структура для работы с сообщениями HWND hwnd; // идентификатор главного окна приложения WORD wVersion; // версия Video for Windows if(hPrevInstance) return FALSE; // Проверяем версию Video for Windows wVersion = HIWORD(VideoForWindowsVersion()); if(wVersion < 0x010a) { MessageBox(NULL, "Используйте Video for Windows" " версии 1.1 или более поздней версии", "MCIWnd Error", MB_OK | MB_ICONHAND); return FALSE; } if(!InitApp(hInstance)) return FALSE; hInst = hInstance; hwnd = CreateWindow( szClassName, // имя класса окна szWindowTitle, // заголовок окна WS_OVERLAPPEDWINDOW, // стиль окна CW_USEDEFAULT, // размеры и расположение окна CW_USEDEFAULT, 400, 350, 0, 0, hInstance, NULL); if(!hwnd) return FALSE; ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); while(GetMessage(&msg, 0, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ===================================== // Функция InitApp // Выполняет регистрацию класса окна // ===================================== BOOL InitApp(HINSTANCE hInstance) { ATOM aWndClass; // атом для кода возврата WNDCLASS wc; // структура для регистрации memset(&wc, 0, sizeof(wc)); wc.lpszMenuName = "APP_MENU"; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = (WNDPROC) WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(hInstance, "APPICON"); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); 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_INITMENU // Инициализация меню // ------------------------------------------------------------ case WM_INITMENU: { // Определяем стиль окна MCI WORD wStyles = MCIWndGetStyles(hwndMovie); // Если окно MCI имеет панель управления, // отмечаем строку "Play Bar" меню "Styles" CheckMenuItem(GetMenu(hwnd), CM_STPBAR, (wStyles & MCIWNDF_NOPLAYBAR) ? MF_UNCHECKED : MF_CHECKED); return 0; } // ------------------------------------------------------------ // WM_COMMAND // Обработка сообщений от меню // ------------------------------------------------------------ case WM_COMMAND: { switch (wParam) { // ------------------------------------------------- // Строка "About" меню "Help" // ------------------------------------------------- case CM_HELPABOUT: { MessageBox(hwnd, "MCIWnd Player & Recorder, v.1.0\n" "(C) Frolov A.V., 1994", "About MCIWNDC", MB_OK | MB_ICONINFORMATION); return 0; } // ------------------------------------------------- // Строка "New Waveaudio" меню "File" // ------------------------------------------------- case CM_FILENEW: { // Если окно MCI уже было создано, удаляем его if(hwndMovie) MCIWndDestroy(hwndMovie); // Создаем новое окно для записи звука, // открываем драйвер устройства waveaudio hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)"waveaudio"); // Создаем новый файл MCIWndNew(hwndMovie, "waveaudio"); return 0; } // ------------------------------------------------- // Строка "Save Waveaudio As..." меню "File" // ------------------------------------------------- case CM_FILESAVEAS: { // Создаем диалоговую панель "Save As..." для // сохранения файла MCIWndSaveDialog(hwndMovie); return 0; } // ------------------------------------------------- // Строка "CD Audio" меню "File" // ------------------------------------------------- case CM_FILECDAUDIO: { // Если окно MCI уже было создано, удаляем его if(hwndMovie) MCIWndDestroy(hwndMovie); // Создаем новое окно для проигрывания CD, // открываем драйвер устройства cdaudio hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)"cdaudio"); return 0; } // ------------------------------------------------- // Строка "Open" меню "File" // ------------------------------------------------- case CM_FILEOPEN: { char szBuff[256]; // Если окно MCI уже было создано, удаляем его if(hwndMovie) MCIWndDestroy(hwndMovie); // Выбираем файл для загрузки в окно MCI if(mciwndSelectFile(szBuff)) { // Создаем окно MCI hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)szBuff); } return 0; } // ------------------------------------------------- // Строка "Close" меню "File" // ------------------------------------------------- case CM_FILECLOSE: { // Удаляем окно MCI MCIWndDestroy(hwndMovie); hwndMovie = NULL; return 0; } // ------------------------------------------------- // Меню "Movie" // ------------------------------------------------- case CM_MVIPLAY: // "Play" { // Проигрывание MCIWndPlay(hwndMovie); return 0; } case CM_MVIRPLAY: // "Play Reverse" { // Проигрывание в обратном направлении MCIWndPlayReverse(hwndMovie); return 0; } case CM_MVISTOP: // "Stop" { // Останов MCIWndStop(hwndMovie); return 0; } case CM_MVIRECORD: // "Record" { // Запись MCIWndRecord(hwndMovie); return 0; } case CM_MVIHOME: // "Home" { // Позиционирование в начало MCIWndHome(hwndMovie); return 0; } case CM_MVIEND: // "End" { // Позиционирование в конец MCIWndEnd(hwndMovie); return 0; } case CM_MVISTEP: // "Step Fwrd" { // Шаг вперед MCIWndStep(hwndMovie, 1); return 0; } case CM_MVIRSTEP: // "Step Back" { // Шаг назад MCIWndStep(hwndMovie, -1); return 0; } case CM_MVIINFO: // "Info..." { char szBuff[512], szBuff1[256]; long dwSize; // Если окно MCI создано, выводим информацию // о загруженном в него файле if(hwndMovie) { // Имя устройства MCIWndGetDevice(hwndMovie, (LPSTR)szBuff, 512); lstrcat(szBuff, (LPSTR)"\n"); // Путь к файлу или имя устройства MCIWndGetFileName(hwndMovie, (LPSTR)szBuff1, 256); lstrcat(szBuff, (LPSTR)szBuff1); lstrcat(szBuff, (LPSTR)"\n"); // Размер файла dwSize = MCIWndGetLength(hwndMovie); wsprintf(szBuff1, "Size: %ld ", (long)dwSize); lstrcat(szBuff, (LPSTR)szBuff1); // Формат времени MCIWndGetTimeFormat(hwndMovie, (LPSTR)szBuff1, 256); lstrcat(szBuff, (LPSTR)szBuff1); MessageBox(hwnd, szBuff, "Media Info", MB_OK | MB_ICONINFORMATION); } return 0; } // ------------------------------------------------- // Меню "Styles" // ------------------------------------------------- case CM_STPBAR: // "Play Bar" { // Определяем стили окна MCI WORD wStyles = MCIWndGetStyles(hwndMovie); // Инвертируем состояние стиля MCIWNDF_NOPLAYBAR MCIWndChangeStyles(hwndMovie, MCIWNDF_NOPLAYBAR, (wStyles & MCIWNDF_NOPLAYBAR) ? 0 : MCIWNDF_NOPLAYBAR); return 0; } // ------------------------------------------------- // Строка "Exit" меню "File" // Завершение работы приложения // ------------------------------------------------- case CM_FILEEXIT: { DestroyWindow(hwnd); return 0; } default: return 0; } } // Отслеживаем изменения в системной палитре case WM_PALETTECHANGED: { SendMessage(hwndMovie, msg, wParam, lParam); break; } case WM_QUERYNEWPALETTE: { return SendMessage(hwndMovie, msg, wParam, lParam); } // ------------------------------------------------------------ // WM_DESTROY // Уничтожение главного окна приложения // ------------------------------------------------------------ case WM_DESTROY: { PostQuitMessage(0); return 0; } default: break; } return DefWindowProc(hwnd, msg, wParam, lParam); } //----------------------------------------------------- // mciwndSelectFile // Выбор файла //----------------------------------------------------- BOOL mciwndSelectFile(LPSTR lpszFileName) { OPENFILENAME ofn; char szFile[256]; char szFileTitle[256]; char szFilter[256] = "Video Files\0*.avi\0" "Waveaudio Files\0*.wav\0" "MIDI Files\0*.mid;*.rmi\0" "Any Files\0*.*\0"; szFile[0] = '\0'; memset(&ofn, 0, sizeof(OPENFILENAME)); // Инициализируем нужные нам поля ofn.lStructSize = sizeof(OPENFILENAME); ofn.hwndOwner = NULL; ofn.lpstrFilter = szFilter; ofn.nFilterIndex = 1; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFileTitle = szFileTitle; ofn.nMaxFileTitle = sizeof(szFileTitle); ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY; // Выбираем входной файл if (GetOpenFileNamePreview(&ofn)) { // Копируем путь к выбранному файлу lstrcpy(lpszFileName, (LPSTR)szFile); return TRUE; } else return FALSE; } После проверки версии Video for Windows функция WinMain создает обычным образом главное окно приложения MCIWNDC и запускает цикл обработки сообщений. Эта часть приложения не имеет каких-либо особенностей. При выборе строки "New Waveaudio" из меню "File" проверяется содержимое глобальной переменной hwndMovie, в которой хранится идентификатор окна MCI. Сразу после запуска приложения эта переменная содержит нулевое значение. Если же окно MCI было создано ранее, оно удаляется при помощи макрокоманды MCIWndDestroy: if(hwndMovie) MCIWndDestroy(hwndMovie); Далее создается окно MCI, причем одновременно открывается устройство waveaudio: hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)"waveaudio"); Создаваемое при этом окно MCI является видимым, дочерним и имеет рамку. Среди органов управления присутствует кнопка записи, так как указан стиль окна MCIWNDF_RECORD. Затем создается новый файл, для чего используется макрокоманда MCIWndNew: MCIWndNew(hwndMovie, "waveaudio"); Сохранение записанных звуковых данных выполняется очень просто - с помощью макрокоманды MCIWndSaveDialog, позволяющей пользователю выбрать путь и имя файла: MCIWndSaveDialog(hwndMovie); При выборе строки "CD Audio" в меню "File" окно MCI создается следующим образом: hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)"cdaudio"); Обратите внимание, что указан стиль MCIWNDF_RECORD. Так как драйвер устройства чтения компакт-дисков не поддерживает (увы!) операцию записи, среди органов управления окна MCI кнопка записи так и не появится. Поэтому при создании собственного приложения для проигрывания компакт-дисков средствами окна MCI вам не нужно указывать этот стиль. Если приложение MCIWNDC используется для проигрывания файла, окно MCI создается при выборе строки "Open..." в меню "File". В этом случае приложение вызывает функцию mciwndSelectFile, которая позволяет пользователю выбрать файл и записывает путь к файлу в переменную szBuff. Далее окно MCI создается следующим образом: hwndMovie = MCIWndCreate( hwnd, hInst, WS_VISIBLE | WS_CHILD | WS_BORDER | MCIWNDF_RECORD, (LPSTR)szBuff); При создании окна MCI указан стиль MCIWNDF_RECORD, однако кнопка записи появится только при загрузке wav-файла, так как запись средствами окна MCI возможна только для устройства "waveaudio". При выборе из меню "File" строки "Close" окно MCI удаляется функцией MCIWndDestroy. Обработка сообщений от меню "Movie" сводится в основном к вызову соответствующей макрокоманды. Например, при выборе из этого меню строки "Play" вызывается макрокоманда MCIWndPlay: case CM_MVIPLAY: // "Play" { MCIWndPlay(hwndMovie); return 0; } Аналогичным образом обрабатываются остальные команды, за исключением команд позиционирования на один шаг вперед и один шаг назад: case CM_MVISTEP: // "Step Fwrd" { MCIWndStep(hwndMovie, 1); return 0; } case CM_MVIRSTEP: // "Step Back" { MCIWndStep(hwndMovie, -1); return 0; } Второй параметр макрокоманды MCIWndStep указывает величину шага в миллисекундах или кадрах (в зависимости от текущего формата времени), причем отрицательным значениям соответствует позиционирование в обратном направлении. При выборе из меню "Movie" строки "Info..." приложение определяет и выводит на экран информацию об используемом устройстве и загруженном файле. Для определения имени устройства используется макрокоманда MCIWndGetDevice : MCIWndGetDevice(hwndMovie, (LPSTR)szBuff, 512); Через первый параметр этой макрокоманде передается идентификатор окна MCI. Второй параметр - указатель на буфер, в который следует записать имя устройства. Третий параметр - размер буфера. Путь к загруженному устройству или имя устройства (если файл не используется) определяется с помощью макрокоманды MCIWndGetFileName аналогичным образом: MCIWndGetFileName(hwndMovie, (LPSTR)szBuff1, 256); Размер файла вычисляется макрокомандой MCIWndGetLength , возвращающей значение в формате двойного слова: dwSize = MCIWndGetLength(hwndMovie); Единица измерения размера зависит от текущего формата времени, который определяется в виде текстовой строки при помощи макрокоманды MCIWndGetTimeFormat : MCIWndGetTimeFormat(hwndMovie, (LPSTR)szBuff1, 256); Теперь о меню "Styles". При выборе из этого меню строки "Play Bar" приложение определяет текущий стили окна, и затем инвертирует стиль MCIWNDF_NOPLAYBAR: case CM_STPBAR: // "Play Bar" { WORD wStyles = MCIWndGetStyles(hwndMovie); MCIWndChangeStyles(hwndMovie, MCIWNDF_NOPLAYBAR, (wStyles & MCIWNDF_NOPLAYBAR) ? 0 : MCIWNDF_NOPLAYBAR); return 0; } Правильную отметку строки "Play Bar" в меню обеспечивает обработчик сообщения WM_INITMENU: case WM_INITMENU: { WORD wStyles = MCIWndGetStyles(hwndMovie); CheckMenuItem(GetMenu(hwnd), CM_STPBAR, (wStyles & MCIWNDF_NOPLAYBAR) ? MF_UNCHECKED : MF_CHECKED); return 0; } Этот обработчик отмечает или нет строку "Play Bar" меню "Styles" в зависимости от того, имеет ли окно MCI стиль MCIWNDF_NOPLAYBAR, или нет. Обработка сообщений об изменении системной палитры WM_PALETTECHANGED и о необходимости реализации палитры WM_QUERYNEWPALETTE заключается в непосредственной передаче соответствующих сообщений окну MCI, поэтому выполняется очень просто: case WM_PALETTECHANGED: { SendMessage(hwndMovie, msg, wParam, lParam); break; } case WM_QUERYNEWPALETTE: { return SendMessage(hwndMovie, msg, wParam, lParam); } Файл mciwndc.h (листинг 5.5) содержит определения констант, используемых в приложении MCIWNDC. Листинг 5.5. Файл mciwndc/mciwndc.h #define CM_HELPABOUT 301 #define CM_FILEEXIT 302 #define CM_FILEOPEN 303 #define CM_FILECLOSE 304 #define CM_FILENEW 305 #define CM_FILESAVEAS 306 #define CM_FILECDAUDIO 307 #define CM_MVIPLAY 401 #define CM_MVIRPLAY 402 #define CM_MVIHOME 403 #define CM_MVIEND 404 #define CM_MVISTEP 405 #define CM_MVIRSTEP 406 #define CM_MVISTOP 407 #define CM_MVIRECORD 408 #define CM_MVIINFO 409 #define CM_STPBAR 501 Файл определения ресурсов приложения представлен в листинге 5.6. Листинг 5.6. Файл mciwndc/mciwndc.rc #include "mciwndc.h" AppIcon ICON mciwndc.ico APP_MENU MENU BEGIN POPUP "&File" BEGIN MENUITEM "&Open...", CM_FILEOPEN MENUITEM "&Close", CM_FILECLOSE MENUITEM SEPARATOR MENUITEM "&New Waveaudio", CM_FILENEW MENUITEM "Save Waveaudio &As...", CM_FILESAVEAS MENUITEM SEPARATOR MENUITEM "CD Audio", CM_FILECDAUDIO MENUITEM SEPARATOR MENUITEM "E&xit", CM_FILEEXIT END POPUP "&Movie" BEGIN MENUITEM "&Play", CM_MVIPLAY MENUITEM "Play &Reverse", CM_MVIRPLAY MENUITEM "R&ecord", CM_MVIRECORD MENUITEM "&Stop", CM_MVISTOP MENUITEM SEPARATOR MENUITEM "&Home", CM_MVIHOME MENUITEM "&End", CM_MVIEND MENUITEM SEPARATOR MENUITEM "Step &Fwrd", CM_MVISTEP MENUITEM "Step &Back", CM_MVIRSTEP MENUITEM SEPARATOR MENUITEM "&Info...", CM_MVIINFO END POPUP "St&yles" BEGIN MENUITEM "&Play Bar", CM_STPBAR END POPUP "&Help" BEGIN MENUITEM "&About...", CM_HELPABOUT END END Файл определения модуля приложения MCIWNDC представлен в листинге 5.7. Листинг 5.7. Файл mciwndc/mciwndc.def NAME MCIWNDC DESCRIPTION 'Приложение MCIWNDC, (C) 1994, Frolov A.V.' EXETYPE windows STUB 'winstub.exe' STACKSIZE 10240 HEAPSIZE 1024 CODE preload moveable discardable DATA preload moveable multiple Для трансляции приложения в среде MS-DOS системой Microsoft C++ версии 7.0 или 8.0 (входящий в Visual C++ версии 1.0) вы можете использовать makefile, представленный в листинге 5.8. Листинг 5.8. Файл mciwndc/makefile NAME = mciwndc OBJ = mciwndc.obj LIBS = libw slibcew commdlg vfw !if "$(DEBUG)" == "NO" DEF = CLOPT = MASMOPT = LINKOPT = !else DEF = -DDEBUG CLOPT = -Zid MASMOPT = -Zi LINKOPT = /CO/LI !endif CC = cl -c -W3 -AS -Zp -G2sw -Oxas $(DEF) $(CLOPT) -DWIN31 ASM = masm -Mx $(MASMOPT) LINK= link /NOE/NOD/LI/MAP/AL:16/ONERROR:NOEXE $(LINKOPT) RC = rc .c.obj: $(CC) $*.c .asm.obj: $(ASM) $*; goal: $(NAME).exe $(NAME).exe: $(OBJ) $(NAME).res $(NAME).def makefile $(LINK) $(OBJ), $(NAME), $(NAME),$(LIBS), $(NAME).def $(RC) -31 $(NAME).res -mapsym $(NAME).map $(NAME).res: $(NAME).rc $(RC) -r $(NAME).rc clean: del $(NAME).exe del *.res del *.obj del *.map del *.sym del *.pdb copy: copy $(NAME).exe ..\..\bin copy $(NAME).sym ..\..\bin depend: mv makefile makefile.old sed "/^# START Dependencies/,/^# END Dependencies/D" makefile.old > makefile del makefile.old echo # START Dependencies >> makefile includes -l *.c *.asm >> makefile echo # END Dependencies >> makefile |