Финансы Сайт на котором знают все про финансы

Встраиваем Flash в свое приложение

Как-то раз при разработке одной программы у меня возникла необходимость встроить какую-нибудь красивую мини-игру в раздел About. Так как делать красивую игру средствами WinAPI — это то ещё удовольствие и по срокам и по времени, то я решил просто-напросто встроить flash-файл в свой проект. Примерное описание того, как это сделать, мы и рассмотрим далее. В примере мы пойдем чуть дальше, а именно не только встроим флеш-ролик в окно, но и добавим парочку элементов управления. В результате у нас получится нечто подобное:

Таким образом, разобрав пример, вы запросто сможете встроить этого замечательного (или какого-нибудь другого) единорога в свое приложение, пускай и цена этого выкрутаса будет ~2.5 мегабайта.

Итак, приступим. Как всегда, начинаем с инклудов, констант, глобальных переменных и прочей лабуды.

//Регистрируем содержимое библиотеки типов и переименовываем созданное пространство имен
#import "progid:ShockwaveFlash.ShockwaveFlash" rename_namespace("Flash")
//atl* - необходимы для работы с классами из Active Template Library (ATL)
#include 
#include 
#include 
#include 
#include 

//Флешка, которая будет отображаться в программе по умолчанию
#define DEFAULT_FLASH TEXT("flash.swf")
//Идентификаторы кнопок на форме для обработки события WM_COMMAND
#define ID_PLAY 101
#define ID_STOP 102
#define ID_LOAD 103
//Размер окна плеера по-умолчанию
#define WIN_W 600
#define WIN_H 550
//Размер кнопок управления и интервал между ними
#define BUTTON_W 60
#define BUTTON_H 20
#define BUTTON_STEP 15
//CComModule - модуль COM-сервера, необходим для доступа к его компонентам
//более подробно тут: http://msdn.microsoft.com/en-us/library/1300df24.aspx
CComModule _ccm;
HINSTANCE ghInst;
HWND ghWnd;
//Прототипы используемых в проекте функций
DWORD GetOpenName(TCHAR * outbuf, const TCHAR * filter, const TCHAR * title);
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam);
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmdLine, int nCmdShow);

Теперь WinMain, в ней создадим обыкновенное окно, которое будет содержать дочернее окно для вывода flash-контента и несколько элементов управления.

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASS wc = {0};
    MSG msg;
    ghInst = hInstance;
    
    //Инициализируем COM-библиотеку
    CoInitialize(NULL);
    //Регистриуем класс AtlAxWin*
    //http://msdn.microsoft.com/ru-ru/library/54fexdxd.aspx
    AtlAxWinInit();
    //Определяем атрибуты основного окна
    wc.lpszClassName = L"swf";
    wc.hInstance     = hInstance;
    wc.hbrBackground = GetSysColorBrush(COLOR_3DFACE);
    wc.lpfnWndProc   = WndProc;
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);

    //Регистриуем класс основного окна
    if(!RegisterClass(&wc))
        return MessageBox(HWND_DESKTOP, L"RegisterClass", L"Error", MB_ICONERROR | MB_OK);
    //Создаем окно
    HWND hWnd = CreateWindow
    (
        wc.lpszClassName,
        L"swf",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        0, 0, WIN_W, WIN_H,
        NULL,
        NULL,
        hInstance,
        NULL
    ); 
    
    if(!hWnd)
        return MessageBox(HWND_DESKTOP, L"CreateWindow", L"Error", MB_ICONERROR | MB_OK);
    
    //Запускаем процесс обработки оконных сообщений
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    //Выгружаем COM-библиотеку, очищаем ресурсы, бла-бла-бла
    CoUninitialize();
    
    return msg.wParam;
}

Следующей идет функция обработки оконных сообщений WndProc.

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
    //Указатель на объект ShockwaveFlash
    static Flash::IShockwaveFlash * flash_o;
    //Хендлы кнопок управления и окна для флешки
    static HWND hWndAX, play, stop, load;
    static RECT rc;
    //Буфер для хранения пути открываемого swf-файла
    WCHAR file_path[MAX_PATH];
    
    ghWnd = hWnd;
    

    switch(uMessage)
    {
        case WM_CREATE:
            //Получаем размер клиентской области окна
            GetClientRect(ghWnd, &rc);
            //Создаем кнопки управления
            play = CreateWindow
            (
                L"button",
                L"Play",
                WS_CHILD | WS_VISIBLE,
                rc.left + BUTTON_STEP, rc.bottom - 25, BUTTON_W, BUTTON_H,
                hWnd,
                (HMENU) ID_PLAY,
                NULL,
                NULL
            );

            stop = CreateWindow
            (
                L"button",
                L"Stop",
                WS_CHILD | WS_VISIBLE,
                rc.left + BUTTON_W + BUTTON_STEP, rc.bottom - 25, BUTTON_W, BUTTON_H,
                hWnd,
                (HMENU) ID_STOP,
                NULL,
                NULL
            );

            load = CreateWindow
            (
                L"button",
                L"Load",
                WS_CHILD | WS_VISIBLE,
                rc.left + BUTTON_W + BUTTON_W + BUTTON_STEP, rc.bottom - 25, BUTTON_W, BUTTON_H,
                hWnd,
                (HMENU) ID_LOAD,
                NULL,
                NULL
            );
            //Создаем окно, в котором будет выводиться flash-содержимое
            hWndAX = CreateWindowEx
            (
                0,
                CAxWindow2::GetWndClassName(),
                NULL,
                WS_CHILD | WS_VISIBLE,
                0,
                0,
                WIN_W,
                WIN_H - 70,
                hWnd,
                NULL,
                ghInst,
                NULL
            );
            //Создаем объект ShockwaveFlash и сохраняем указатель на него
            if
            (
                FAILED
                (
                    CoCreateInstance
                    (
                        __uuidof(Flash::ShockwaveFlash),
                        NULL,
                        CLSCTX_ALL,
                        __uuidof(Flash::IShockwaveFlash),
                        (void**)&flash_o
                    )
                )
            )
                return -1;
            
            //Прикрепляем созданный объект к окну
            if(FAILED(AtlAxAttachControl(flash_o, hWndAX, NULL)))
            {
                flash_o->Release();
                return -1;
            }
            //Получаем директорию, откуда был запущен процесс
            GetCurrentDirectory(MAX_PATH, file_path);
            //Формируем путь
            swprintf_s(file_path, MAX_PATH, L"%ws\\%ws", file_path, DEFAULT_FLASH);
            //Загружаем флеш-файл
            flash_o->LoadMovie(0, file_path);
            //Отключаем расширенное меню флеша (которое появляется при нажатии пкм)
            flash_o->Menu = VARIANT_FALSE;
            //Определяем размеры окна
            GetWindowRect(hWnd, &rc);
            //Перемещаем окно в центр экрана
            SetWindowPos
            (
                hWnd,
                NULL,
                (GetSystemMetrics(SM_CXSCREEN) - rc.right) / 2,
                (GetSystemMetrics(SM_CYSCREEN) - rc.bottom) / 2,
                0,
                0,
                SWP_NOZORDER | SWP_NOSIZE
            );
            
        return 0;

        case WM_COMMAND:
            //Обрабатываем нажатия на элементы управления в окне
            switch(LOWORD(wParam))
            {
                case ID_PLAY:
                    flash_o->Play();
                break;
                case ID_STOP:
                    flash_o->Stop();
                break;
                case ID_LOAD:
                    if(GetOpenName(file_path, TEXT("Flash (*.swf)\0*.swf\0Все файлы (*.*)\0*.*\0\0"), TEXT("Открыть...")))
                        flash_o->LoadMovie(0, file_path);
                break;
            }
        return 0;
        //Обрабатываем изменение размеров окна, эдакий резиновый интерфейс
        case WM_SIZE:
            //Изменяем размеры внутреннего окна
            MoveWindow(hWndAX, 0, 0, LOWORD(lParam), HIWORD(lParam) - 70, TRUE);
            
            GetClientRect(hWnd, &rc);
            //Перемещаем элементы управления
            MoveWindow(play, rc.left + BUTTON_STEP, rc.bottom - 25, BUTTON_W, BUTTON_H, TRUE);
            MoveWindow(stop, rc.left + BUTTON_W + BUTTON_STEP, rc.bottom - 25, BUTTON_W, BUTTON_H, TRUE);
            MoveWindow(load, rc.left + BUTTON_W + BUTTON_W + BUTTON_STEP, rc.bottom - 25, BUTTON_W, BUTTON_H, TRUE);
        return 0;
        
        case WM_CLOSE:
            //Уничтожаем окна
            DestroyWindow(hWndAX);
            DestroyWindow(hWnd);
        return 0;
        
        case WM_DESTROY:
            //Освобождаем объект
            flash_o->Release();
            PostQuitMessage(0);
        return 0;
    }
    
    return DefWindowProc(hWnd, uMessage, wParam, lParam);
}

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

DWORD GetOpenName(TCHAR * outbuf, const TCHAR * filter, const TCHAR * title)
{
    OPENFILENAME ofn = {0};
    TCHAR buf[MAX_PATH + 2];

    GetModuleFileName(ghInst, buf, MAX_PATH);

    TCHAR * tmp = StrRChr(buf, NULL, L'\\');
    if(tmp != 0)
    {
        *tmp = 0;
        ofn.lpstrInitialDir = buf;
    }

    ofn.hInstance = ghInst;
    ofn.hwndOwner = ghWnd;
    ofn.lStructSize = sizeof(OPENFILENAME);
    ofn.lpstrFilter = filter;
    ofn.nFilterIndex = 1;
    ofn.lpstrFile = outbuf;
    ofn.lpstrFile[0] = 0;
    ofn.lpstrFile[1] = 0;
    ofn.nMaxFile = MAX_PATH;
    ofn.lpstrTitle = title;
    ofn.Flags = OFN_EXPLORER | OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_NONETWORKBUTTON | OFN_PATHMUSTEXIST;

    return GetOpenFileName(&ofn);
}

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

Скачать: исходный код + бинарник с флешкой