Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Регистрация Блоги Правила Справка Пользователи Календарь Поиск рулит! Сообщения за день Все разделы прочитаны
 

Вернуться   Форум Flasher.ru > Блоги > alexcon314

Оценить эту запись

Пишем свою оболочку для FP под Windows. Шаг 1.

Запись от alexcon314 размещена 17.05.2011 в 02:13
Обновил(-а) alexcon314 20.05.2011 в 09:36 (расцветка кода)

Сегодня не будет чего-то принципиально нового. Обговорим узловые моменты и сделаем проект с заделом на будущее.
[UPD]
Здесь можно посмотреть на методы ActiveX: http://www.adobe.com/support/flash/p...tingwithflash/

Легкое погружение в Windows-программирование.

Все примерно представляют, как выглядит пустой проект флеш-ролика в FD?
Напомню максимально упрощенно:
Код AS3:
package 
{
	import flash.display.Sprite;
 
	public class Main extends Sprite 
	{		
		public function Main():void 
		{
			// TODO: create UI here
			// цикл прокачки сообщений – нативная сторона плеера
		}
 
	}
}
Осмелюсь утверждать, что и минимальный код Win-приложения выглядит не сложнее:
Код AS3:
#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;
}
При старте приложения ОС создает главный поток, точкой входа для которого и является wWinMain(). Далее, создается основное окно, поток «подвешивается» в бесконечном цикле, проверяющем системную очередь сообщений, до тех пор, пока не получит сообщение о выходе.
Вполне уместно весь код, создающий и обслуживающий главное окно приложения, вынести в отдельный класс. Я назвал его CFlashWnd и намерен использовать его далее как базовый класс приложения. В итоге у нас получится что-то типа:


Код AS3:
#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. Не страшно. Мы просто подгрузим либу в рантайме и вызовем нужные нам функции вручную.
Произойдет это как то так:
Код AS3:
// определяем типы будущих указателей на функции
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:
Код AS3:
#import  "PROGID:ShockwaveFlash.ShockwaveFlash" named_guids no_namespace raw_interfaces_only no_auto_exclude rename("IServiceProvider","IServiceProvider2")
Студия сама «раздраконит» плеер, вынет из него все интерфейсы, создаст файл .tlh (type library header) с их описаниями и подключит этот файл к проекту. Очень удобно. Атрибуты директивы я выбрал такими, чтобы не было проблем при компиляции в большинстве случаев. Замечу, что использовать эту замечательную директиву можно и так:
Код AS3:
#import  “c:\\WINDOWS\\system32\\Macromed\\Flash\\Flash10h.ocx” named_guids no_namespace raw_interfaces_only no_auto_exclude rename("IServiceProvider","IServiceProvider2")
Т.е. можно просто указать файл плеера, даже не регистрируя его в своей системе.
Теперь мы прямо в коде можем использовать тип IShockwaveFlash – основной интерфейс флеш-контрола. А так же “человеческие” имена для прочей COM-аттибутики.
Собственно, создаем плеер:
Код AS3:
IShockwaveFlash* flash = NULL;
::CoInitialize(NULL);
::CoCreateInstance(CLSID_ShockwaveFlash, 0, CLSCTX_ALL, __uuidof(IShockwaveFlash), (void **)&flash);
CoCreateInstance(), ориентируясь на параметры, создаст экземпляр плеера и сохранит указатель на него в переменную flash. После этого мы можем работать с контролом по этому указателю напрямую:
Код AS3:
// загружаем мувик
flash->put_Movie(L”movieFileName.swf”);
Студия услужливо автокомплитит все доступные методы контрола. Тоже очень удобно.
Замечу, что этот код сработает, только если на целевой машине установлен и зарегистрирован флеш-контрол, как это обычно происходит при скачивани плеера с сайта Адоба. Этот пример и есть «штатное» использование плеера.
Ок. Узловые моменты раскрыты. Собираем все в кучу.

Тестовый проект

Создаем новый Win32-проект в студии.
По-ходу несколько хинтов для свойств проекта (правая кнопка мыши по проекту в дереве - Свойства), во избежание эксцессов:
- находим Компоновщик (Linker)-> включить инкрементную компоновку-> выставить позицию “Нет (INCREMENTAL:NO)”
- находим C++->Общие, ставим позицию "Формат отладочной информации" в "База данных программы /Zi";
- находим C++->Предкомпилированные заголовки, ставим позицию "Создавать прекомпилированные заголовки.." в "Не использовать...";
- находим С++-> Cоздание кода-> "Библиотека времени выполнения" в “Многопоточная /MT” (можно и для Debug и для Release режимов). Этот хинт важный, он позволит студии слинковать нужные рантаймы статическии ваша прога запустится на любой версии винды (ну, малость размер ехе пострадает, но это мелочи). Нужно это, вобщем-то, в финале, просто не забывайте об этом.
Далее, пробежимся по базовму классу( CFlashWnd.h):
Код AS3:
#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() я сохранияю указатель на экземпляр класса в структуре данных окна:
Код AS3:
::SetWindowLongPtr(hWndHost,GWL_USERDATA,(int)this);
C тем, чтобы вынуть его в статической оконной процедуре: (если надо будет, а будет надо)
Код AS3:
CFlashWnd* f=(CFlashWnd*)GetWindowLongPtr(hWnd,GWL_USERDATA);
About() – тоже статик, ну, это просто оконная процедура диалога About. Перешла от дефолтного проекта, так же как и меню с иконками. Почему бы и нет?
Кстати, рекомендую посмотреть текст .rc-файла и разобраться что к чему. Скоро нам потребуется с ним работать. Экспресс студия не умеет работать как редактор ресурсов, потому .rc придется править ручками.
Как пример базового класса окна, можно посмотреть реализацию класса CWindow из ATL. Его описание есть в MSDN.
Теперь test.cpp:
Код AS3:
#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 – основной класс приложения:
Код AS3:
//.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-библиотек. Вобщем, смотрите.

В следующий раз рассмотрим «нештатные» загрузки плеера: вручную с диска и из ресурсов приложения.
Вложения
Тип файла: zip test.zip (82.1 Кб, 167 просмотров)
Всего комментариев 10

Комментарии

Старый 17.05.2011 03:21 VitaliyKrivtsov вне форума
VitaliyKrivtsov
 
Аватар для VitaliyKrivtsov
Объемно получилось) На С# гораздо меньше кода нужно для работы с контролом. Спасибо за статью Жду продолжения.
Старый 17.05.2011 13:29 -De- вне форума
-De-
 
Аватар для -De-
Хочется вызов функции из флэш и во флэш (аналог ExternalInterface) - будет оч. полезно. На примере загруки/сохранения файла с винта (мне пригодится часто просят). Можно будет легко полноценные тулзы писать - профит.
Старый 17.05.2011 14:02 alexcon314 вне форума
alexcon314
Я не хочу забегать вперед. Все будет, со временем.
Старый 17.05.2011 14:33 arkadattx вне форума
arkadattx
Цитата:
Объемно получилось) На С# гораздо меньше кода нужно для работы с контролом. Спасибо за статью Жду продолжения.
Было бы очень интересно посмотреть на С# - интерпретацию.
Старый 17.05.2011 15:20 VitaliyKrivtsov вне форума
VitaliyKrivtsov
 
Аватар для VitaliyKrivtsov
arkadattx, скачайте исходники примера такого приложения из сайтa adobe, посмотрите. Или краткий обзор в хелпе. Единственная проблема - это нужно писать свой прокси-сервер, для обработки ответа от ExternalInterface и формирования ответа флешке. Да и ExternalInterface не лучший вариант для общения с контейнером. Пока флешка не дождется ответа от контейнера, код дальше выполнятся не будет. С одной стороны - это плюс, а с другой - если не нужно ждать ответа - большой минус.
Старый 17.05.2011 15:26 gloomyBrain вне форума
gloomyBrain
 
Аватар для gloomyBrain
Цитата:
с другой - если не нужно ждать ответа - большой минус
Всегда можно сделать это асинхронным - запомнили полученное от флешки и вернули пустоту. А потом когда все вычислили - через тот же ExternalInterface вернули нужный результат
Старый 17.05.2011 15:46 VitaliyKrivtsov вне форума
VitaliyKrivtsov
 
Аватар для VitaliyKrivtsov
Цитата:
Всегда можно сделать это асинхронным - запомнили полученное от флешки и вернули пустоту. А потом когда все вычислили - через тот же ExternalInterface вернули нужный результат
Это уже как выход из такой ситуации. В данный момент так и делаю. Пришлось переделать исходники адоба. Как по мне, лучше бы ExternalInterface работал в одну сторону. Отправлял данные и все. Но так задумал великий Adobe - ничего не поделаешь. Приблизительно на отправку и получения ответа уходит 5 миллисекунд.
Старый 17.05.2011 16:40 i.o. вне форума
i.o.
 
Аватар для i.o.
Цитата:
Как по мне, лучше бы ExternalInterface работал в одну сторону. Отправлял данные и все
А флэшка осталась бы немой или глухой что ли?
Обновил(-а) i.o. 17.05.2011 в 16:50
Старый 17.05.2011 16:56 alexcon314 вне форума
alexcon314
В одну сторону работает FSCommand.
Старый 17.05.2011 21:53 VitaliyKrivtsov вне форума
VitaliyKrivtsov
 
Аватар для VitaliyKrivtsov
Цитата:
В одну сторону работает FSCommand.
Вовсе забыл про FSCommand.
Цитата:
А флэшка осталась бы немой или глухой что ли?
Здесь имел ввиду, что если бы ExternalInterface работал примерно как FSCommand, но при таком раскладе ExternalInterface не чем не отличался бы от FSCommand. Здесь скорей всего, будет лучшим вариантом - использование ExternalInterface и FSCommand в тех случаях, где их применение является оптимальным решениям.
 

 


Часовой пояс GMT +4, время: 08:50.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2019, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.