Пишем свою оболочку для FP под Windows. Шаг 1.
Запись от alexcon314 размещена 17.05.2011 в 02:13
Обновил(-а) alexcon314 20.05.2011 в 09:36 (расцветка кода)
Обновил(-а) alexcon314 20.05.2011 в 09:36 (расцветка кода)
Сегодня не будет чего-то принципиально нового. Обговорим узловые моменты и сделаем проект с заделом на будущее.
[UPD]
Здесь можно посмотреть на методы ActiveX: http://www.adobe.com/support/flash/p...tingwithflash/
Легкое погружение в Windows-программирование.
Все примерно представляют, как выглядит пустой проект флеш-ролика в FD?
Напомню максимально упрощенно:
package { import flash.display.Sprite; public class Main extends Sprite { public function Main():void { // TODO: create UI here // цикл прокачки сообщений – нативная сторона плеера } } }
#include "windows.h" int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { // TODO: create UI here MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; }
Вполне уместно весь код, создающий и обслуживающий главное окно приложения, вынести в отдельный класс. Я назвал его CFlashWnd и намерен использовать его далее как базовый класс приложения. В итоге у нас получится что-то типа:
#include "windows.h" #include "myflash.h" int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; MyFlash* flashWnd = new MyFlash();// MyFlash – наследник CFlashWnd while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int) msg.wParam; }
Собственно, внимательно рассмотрев дефолтный файл исходника Win32-проекта, можно легко увидеть все.
Но этого будет мало. В нашем случае в созданном окне надо разместить флеш-плеер. В этом нам на помощь придет нативная windows-библиотека ATL(Active Template Library). Нет-нет, не целиком, всего только две функции этой библиотеки и один класс окна из нее.
ATL: меньше кода.
AtlAxWinInit() - это первая из двух функций. Она просто запускает внутренние механизмы этой библиотеки.
AtlAxAttachControl() - вторая функция. Она предназначена для аттача контрола (флеш-плеера) на окно.
“AtlAxWin” — это тот самый нужный нам класс окна.
Экспресс-студия не может непосредственно распознать факт использования ATL в коде. У нее просто нет на борту той части сдк, что касается ATL. Не страшно. Мы просто подгрузим либу в рантайме и вызовем нужные нам функции вручную.
Произойдет это как то так:
// определяем типы будущих указателей на функции typedef BOOL ( WINAPI *LPAtlAxWinInit )(); typedef HRESULT ( WINAPI *LPAtlAxAttachControl)(IUnknown* pControl, HWND hWnd, IUnknown** ppUnkContainer); ... // грузим ATL HMODULE hModule = ::LoadLibrary(L"atl"); LPAtlAxWinInit pAtlAxWinInit = (LPAtlAxWinInit)::GetProcAddress(hModule,"AtlAxWinInit"); // Инициализация либы pAtlAxWinInit(); // создали окно класса AtlAxWin HWND hWndHost = ::CreateWindow(L"AtlAxWin", lpWindowName, dwStyle, x, y, nWidth, nHeight, hWndParent, hMenu, hInst, lpParam); .. // тут мы создаем экземпляр плеера под именем flash .. // аттачим контрол на окно. LPAtlAxAttachControl pAtlAxAttachControl = (LPAtlAxAttachControl)::GetProcAddress(hModule,"AtlAxAttachControl"); HRESULT hr = pAtlAxAttachControl(flash,hWndHost,NULL); // Все.
Ну, тут тоже все просто. Сначала подключим контрол к проекту с помощью директивы #import:
#import "PROGID:ShockwaveFlash.ShockwaveFlash" named_guids no_namespace raw_interfaces_only no_auto_exclude rename("IServiceProvider","IServiceProvider2")
#import “c:\\WINDOWS\\system32\\Macromed\\Flash\\Flash10h.ocx” named_guids no_namespace raw_interfaces_only no_auto_exclude rename("IServiceProvider","IServiceProvider2")
Теперь мы прямо в коде можем использовать тип IShockwaveFlash – основной интерфейс флеш-контрола. А так же “человеческие” имена для прочей COM-аттибутики.
Собственно, создаем плеер:
IShockwaveFlash* flash = NULL; ::CoInitialize(NULL); ::CoCreateInstance(CLSID_ShockwaveFlash, 0, CLSCTX_ALL, __uuidof(IShockwaveFlash), (void **)&flash);
Студия услужливо автокомплитит все доступные методы контрола. Тоже очень удобно.
Замечу, что этот код сработает, только если на целевой машине установлен и зарегистрирован флеш-контрол, как это обычно происходит при скачивани плеера с сайта Адоба. Этот пример и есть «штатное» использование плеера.
Ок. Узловые моменты раскрыты. Собираем все в кучу.
Тестовый проект
Создаем новый Win32-проект в студии.
По-ходу несколько хинтов для свойств проекта (правая кнопка мыши по проекту в дереве - Свойства), во избежание эксцессов:
- находим Компоновщик (Linker)-> включить инкрементную компоновку-> выставить позицию “Нет (INCREMENTAL:NO)”
- находим C++->Общие, ставим позицию "Формат отладочной информации" в "База данных программы /Zi";
- находим C++->Предкомпилированные заголовки, ставим позицию "Создавать прекомпилированные заголовки.." в "Не использовать...";
- находим С++-> Cоздание кода-> "Библиотека времени выполнения" в “Многопоточная /MT” (можно и для Debug и для Release режимов). Этот хинт важный, он позволит студии слинковать нужные рантаймы статическии ваша прога запустится на любой версии винды (ну, малость размер ехе пострадает, но это мелочи). Нужно это, вобщем-то, в финале, просто не забывайте об этом.
Далее, пробежимся по базовму классу( CFlashWnd.h):
#pragma once #include "windows.h" #include "resource.h" #import "PROGID:ShockwaveFlash.ShockwaveFlash" named_guids no_namespace raw_interfaces_only no_auto_exclude rename("IServiceProvider","IServiceProvider2") #include "winver.h" #pragma comment(lib,"Version.lib") //definitions #define FLHOSTWINDOW_CLASS L"AtlAxWin" #define FLPROG_ID L"ShockwaveFlash.ShockwaveFlash" #define FLCLASS_ID L"{D27CDB6E-AE6D-11cf-96B8-444553540000}" #define FLTYPELIB_ID L"{D27CDB6B-AE6D-11cf-96B8-444553540000}" #define FLWINDOW_CLASS L"MacromediaFlashPlayerActiveX" //typedefs typedef BOOL(WINAPI*LPAtlAxWinInit)(); typedef HRESULT(WINAPI *LPAtlAxAttachControl)(IUnknown* pControl, HWND hWnd, IUnknown** ppUnkContainer); class CFlashWnd { protected: HWND hWndHost; BOOL isAttached; BOOL isInit; BOOL isMovieLoaded; static HINSTANCE hInst; private: IShockwaveFlash* flash; static ATOM RegisterHostWindowClass (); static LRESULT CALLBACK HostWndProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); static INT_PTR CALLBACK DlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam); public: CFlashWnd(void); ~CFlashWnd(void); // main window control interface DWORD dwThreadId; HWND hwndParent; HWND CreateHostWindow ( LPCWSTR lpWindowName = NULL, DWORD dwStyle = WS_OVERLAPPEDWINDOW, int nCmdShow = SW_SHOWNORMAL, int x = CW_USEDEFAULT, int y = 0, int nWidth = CW_USEDEFAULT, int nHeight = 0, HWND hWndParent = NULL, HMENU hMenu = NULL, LPVOID lpParam = NULL ); int SetWindowSize(int cx, int cy); int CenterWindow(); HWND GetHostWindow(); BOOL SetWindowText(WCHAR* str); BOOL MessageBox (WCHAR* text, WCHAR* title); BOOL Destroy(); IUnknown* GetControl(); HRESULT AttachControl(); // Flash Control Init static HRESULT GetInstallVersion(WCHAR* ver); HRESULT CreateInstance(); // common HRESULT LoadMovie(WCHAR* szFileName); HRESULT SetWMODE(WCHAR* str); HRESULT SetSALIGN(WCHAR* str); HRESULT SetALIGNMODE(int mode); HRESULT SetSCALEMODE(int mode); //static CONSTANTS //ScaleMode values: static const int SCALEMODE_SHOWALL ; //0 static const int SCALEMODE_NOBORDER; //1 static const int SCALEMODE_EXACTFIT; //2 static const int SCALEMODE_NOSCALE ; //3 //AlignMode values: static const int ALIGN_C; //0(default) static const int ALIGN_L; //1 static const int ALIGN_R; //2 static const int ALIGN_T; //4 static const int ALIGN_B; //8 //SAlign values: static const LPOLESTR SALIGN_L ; //"L" static const LPOLESTR SALIGN_T ; //"T" static const LPOLESTR SALIGN_R ; //"R" static const LPOLESTR SALIGN_B ; //"B" static const LPOLESTR SALIGN_TL; //"TL" static const LPOLESTR SALIGN_TR; //"TL" static const LPOLESTR SALIGN_BL; //"BL" static const LPOLESTR SALIGN_BR; //"BR" //WMode values: static const LPOLESTR WMODE_TRANSPARENT; //"transparent" static const LPOLESTR WMODE_OPAQUE; //"opaque" static const LPOLESTR WMODE_WINDOW; //"window" };
Обратите внимание, что функция оконной процедуры HostWndProc() – статическая. Это особенность «классового» подхода. Потому в CreateHostWindow() я сохранияю указатель на экземпляр класса в структуре данных окна:
C тем, чтобы вынуть его в статической оконной процедуре: (если надо будет, а будет надо)
About() – тоже статик, ну, это просто оконная процедура диалога About. Перешла от дефолтного проекта, так же как и меню с иконками. Почему бы и нет?
Кстати, рекомендую посмотреть текст .rc-файла и разобраться что к чему. Скоро нам потребуется с ним работать. Экспресс студия не умеет работать как редактор ресурсов, потому .rc придется править ручками.
Как пример базового класса окна, можно посмотреть реализацию класса CWindow из ATL. Его описание есть в MSDN.
Теперь test.cpp:
#include <windows.h> #include "test.h" #include "MyFlash.h" int APIENTRY wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { MSG msg; HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST)); CoInitialize(NULL); MyFlash* flashWnd = new MyFlash(); while (GetMessage(&msg, NULL, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } CoUninitialize(); return (int) msg.wParam; }
Важным является вызов CoInitialize(). Тем смым активируется COM-рантайм Windows. Ну, не 5 строк, конечно, но и не 20.
Ах, да, MyFlash – основной класс приложения:
//.h #pragma once #include "FlashWnd.h" class MyFlash : public CFlashWnd { private: void init(); public: MyFlash(void){ init();}; ~MyFlash(void){}; }; //.cpp #include "MyFlash.h" void MyFlash::init() { WCHAR ver[16]={0}; GetInstallVersion(ver); CreateHostWindow(); SetWindowSize(550,600); CenterWindow(); SetWindowText(ver); CreateInstance(); AttachControl(); WCHAR buf[MAX_PATH]={0}; ::GetCurrentDirectory(MAX_PATH,buf); lstrcat(buf,L"\\..\\common\\movie.swf"); LoadMovie(buf); }
Теперь немного об удобствах. Для работы одновременно с флэшем и с++ удобно заставить их IDE кидать выходные файлы(.swf и .exe) в одну папку.
Я заготовил тестовый проект для FD и настроил относительные пути в студии и в FD должным образом. Сегодня во вложении оба проекта и папка совместной публикации common.
Ну, и напоследок о функции CFlashWnd::GetInstallVersion().
Я счел полезным уметь определять факт установки плеера в системе и его версию.
Функция принимает указатель на буфер, куда будет записано строковое представление версии плеера, возвращает S_OK (это просто 0), если плеер есть, и S_FALSE, если плеер не установлен. В функции задействована version.dll – тоже одна из windows-библиотек. Вобщем, смотрите.
В следующий раз рассмотрим «нештатные» загрузки плеера: вручную с диска и из ресурсов приложения.
Всего комментариев 10
Комментарии
17.05.2011 03:21 | |
Объемно получилось) На С# гораздо меньше кода нужно для работы с контролом. Спасибо за статью Жду продолжения.
|
17.05.2011 14:02 | |
Я не хочу забегать вперед. Все будет, со временем.
|
17.05.2011 14:33 | |
Цитата:
Объемно получилось) На С# гораздо меньше кода нужно для работы с контролом. Спасибо за статью Жду продолжения.
|
17.05.2011 16:40 | |
Цитата:
Как по мне, лучше бы ExternalInterface работал в одну сторону. Отправлял данные и все
|
|
Обновил(-а) i.o. 17.05.2011 в 16:50
|
17.05.2011 16:56 | |
В одну сторону работает FSCommand.
|
Последние записи от alexcon314
- Пишем свою оболочку для FP под Windows. Шаг 6. (19.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 5. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 4. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 3. (18.05.2011)
- Пишем свою оболочку для FP под Windows. Шаг 2. (17.05.2011)