Приложение 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