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

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

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

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

Запись от alexcon314 размещена 18.05.2011 в 18:03
Обновил(-а) alexcon314 20.05.2011 в 09:34

Advise Sink: cоветую утопиться.

Ну, вот. Похоже на то, что на этом шаге наши пути разойдутся. Если, конечно, они не разошлись раньше)). С хостом плеера более-менее разобрались. Осталось научить нашу оболочку общаться с плеером в духе «вопрос-ответ». Ну, и конечно, отдавать ему приказы. Как вы догадались, речь пойдет о взаимодействии оболочки и плеера через ExternalInterface. Видимо, это последнее, что можно отнести к «стандартным» знаниям об оболочке FP. Далее возможны варианты, то, как вы реализуете это взаимодействие — целиком ваша прерогатива. Я, по всей видимости, пойду своим путем. Конечно, я не замолкну и продолжу свои публикации насколько меня хватит... но это уже будет совсем другая песня. Ах, да. Есть еще одна фича про запас. Но о ней позже.
К делу. Опять нас выручит один из COM-интерфейсов, на этот раз IDispatch.
Но не все так просто. Процедура увязывания внешних обработчиков запросов контрола (событий, если хотите) с самим контролом не совсем тривиальна. Попробую несколько прояснить ситуацию.
Смотрите, в плеере что-то произошло, такое, что ему надо об этом сообщить хосту. Как в этом случае можно поступить? Правильно. Реализовать что-то навроде родимого EventDispatcher'a. На языке COM это выглядит примерно так: контролу специальным методом (через промежуточный интерфейс), подставляется стандартный интерфейс (IDispatch) для ловли этих самых его запросов-событий. Т.к. интерфейс стандартный, с его завязкой в контрол проблем нет, он специально так и спроектирован. У этого интерфейса есть, собственно, одна функция Invoke(), которую плеер дергает, когда ему надо. Прелесть в том, что мы сами реализуем эту функцию, ну, ее тело, конечно. Т.о. мы получаем великолепную возможность собственноручно обрабатывать запросы плеера, что называется, «с пылу-с жару). В эту функцию валятся все запросы. АПИ плеера должно бы документировать их идентификаторы (ID). Где-то я видел что-то вроде таблички сопоставления ID и запросов, но, к несчастью, не помню где. Да все нам и не надо. Потом, вы сами сможете вычислить эти идентификаторы с помощью дебаггера прямо в студии, ничего сложного. Потом пропускаем ID запроса через банальный switch и возвращаем, если это возможно результат. Вот и вся кухня.

Имплементим IDispatch в наш базовый класс.

Тут есть такая возможность — унаследовать наш класс от абстрактного IDispatch, заменив абстрактные члены на нашу реализацию. И подставить его плееру, в качестве «слушателя» событий. Так и поступим.
Код AS3:
//  CFlashWnd.h (дополнение)
class CFlashWnd:
	public  IDispatch
 
{
    ...
// starting events listening
	HRESULT SetReturnValue(WCHAR* xml_responce);
	HRESULT AdviseSink();
	// IDispatch methods
	STDMETHOD(QueryInterface)( REFIID riid, void ** ppvObject);
	ULONG STDMETHODCALLTYPE AddRef();
	ULONG STDMETHODCALLTYPE Release();
    	STDMETHOD(GetIDsOfNames)( REFIID riid, 
                              OLECHAR FAR *FAR *rgszNames,
                              unsigned int cNames, 
                              LCID lcid, 
                              DISPID FAR *rgDispId )
    	{ return( E_NOTIMPL ); }
 
    STDMETHOD(GetTypeInfo)( unsigned int iTInfo, 
                            LCID lcid, 
                            ITypeInfo FAR *FAR *ppTInfo )
    { return( E_NOTIMPL ); }
 
    STDMETHOD(GetTypeInfoCount)( unsigned int FAR *pctinfo )
    {
        return( E_NOTIMPL );
    }
 
    STDMETHOD(Invoke)( DISPID  dispIdMember,      
                       REFIID  riid,              
                       LCID  lcid,                
                       WORD  wFlags,              
                       DISPPARAMS FAR*  pDispParams,  
                       VARIANT FAR*  pVarResult,  
                       EXCEPINFO FAR*  pExcepInfo,  
                       unsigned int FAR*  puArgErr );
 
	// Override this methods if needed
virtual HRESULT OnReadyStateChange	(long newState )			{return S_OK;}
virtual HRESULT OnProgress		(long percentDone )		{return S_OK;}
virtual HRESULT FSCommand		(_bstr_t command,_bstr_t args )	{return S_OK;}
virtual HRESULT FlashCall		(_bstr_t xml_request )		{return S_OK;}
Код AS3:
//  CFlashWnd.cpp  (дополнение)
HRESULT STDMETHODCALLTYPE CFlashWnd::QueryInterface(REFIID iid, void** ppvObject)
{
	if (iid == IID_IUnknown)
	{
	  dwRefCount++;
	  *ppvObject = (void *)this;
	  return S_OK;
	}
	if (iid == DIID__IShockwaveFlashEvents)
	{
	  dwRefCount++;
	  *ppvObject = (void *)this;
	  return S_OK;
	}
	return E_NOINTERFACE;
}
 
ULONG STDMETHODCALLTYPE CFlashWnd::AddRef()
{
	dwRefCount++;
	return dwRefCount;
}
 
ULONG STDMETHODCALLTYPE CFlashWnd::Release()
{
	dwRefCount--;
	return dwRefCount;
}
HRESULT CFlashWnd::Invoke(
                            DISPID  dispIdMember,      
                            REFIID  riid,              
                            LCID  lcid,                
                            WORD  wFlags,              
                            DISPPARAMS FAR*  pDispParams,  
                            VARIANT FAR*  pVarResult,  
                            EXCEPINFO FAR*  pExcepInfo,  
                            unsigned int FAR*  puArgErr )
{
    if (!pDispParams)
        return E_POINTER;
 
    if (pDispParams->cNamedArgs != 0)
        return DISP_E_NONAMEDARGS;
 
    HRESULT hr = DISP_E_MEMBERNOTFOUND;
    switch (dispIdMember)
    {
	case 0xC5:// ExternalInterface call
		return FlashCall(pDispParams->rgvarg[0].bstrVal);
		break;
	case 0x96:// fscommand call
		return FSCommand(pDispParams->rgvarg[1].bstrVal, pDispParams->rgvarg[0].bstrVal );
		break;
	case 0x7A6: // OnProgress ???                 
		return OnProgress(pDispParams->rgvarg[0].intVal);
		break;
	case 0xFFFFFD9F: // OnReadyStateChange ???
		if(pDispParams->rgvarg[0].lVal == 4)
			isReady = TRUE;
		return OnReadyStateChange(pDispParams->rgvarg[0].lVal);
		break;
    }
 
    return  hr;
}
HRESULT CFlashWnd::SetReturnValue(WCHAR* xml_responce)
{
	if(isInit)
		return flash->SetReturnValue(xml_responce);
 
	return S_FALSE;
}
HRESULT CFlashWnd::AdviseSink()
{
	if(!isInit)
		return S_FALSE;
 
	IConnectionPointContainer*	lpConCont = NULL;
	HRESULT hr = flash->QueryInterface(IID_IConnectionPointContainer, (void**)&lpConCont);
	if (hr != S_OK)
		return hr;
 
	hr = lpConCont->FindConnectionPoint(DIID__IShockwaveFlashEvents, &lpConPoint);
	lpConCont->Release();
	lpConCont = NULL;
	if (hr != S_OK)
		return hr;
 
	hr = lpConPoint->Advise(this, &dwConPointID);
	return hr;
}
{ return( E_NOTIMPL ); } означает, что мы данный метод не реализуем, он нам не нужен.
Обратите внимание на реализацию Invoke() и AdviseSink().
В Invoke(), как я и говорил, мы прогоняем ID запроса через гребенку свичей, вызывая наши обработчики на каждый тип запроса. Например, 0xС5 -> FlashCall() и т.д.
А в AdviseSink() реализован механизм «регистрации» нашего класса как слушателя событий плера. Промежуточным интерфейсом тут является IСonnectionPoint, звучное название, не так ли?
Осталось дополнить, что обработчики, типа FlashCall(), я сделал виртуальными. В наследнике базового класса вы их можете переопределить на свои или оставить как есть, тогда ничего происходить не будет и все.
Да, знаками вопроса в Invoke() я отметил ID запросов, которыми не уверен как пользоваться и не уверен в правильной их интерпретации. Возможно, кто-то другой внесет ясность.
[UPD]
Цитата:
OnReadyStateChange(state)
Generated when the ready state of the control changes. The possible states are: 0=Loading, 1=Uninitialized, 2=Loaded, 3=Interactive, 4=Complete.
Использование
Код AS3:
#pragma once
#include "flashwnd.h"
 
class MyFlash :
	public CFlashWnd
{
private:
	void init();
public:
	MyFlash (void){ init();};
	~MyFlash(void){};
	HRESULT FlashCall (_bstr_t xml_request );
};
Код AS3:
#include "MyFlash.h"
 
void MyFlash::init()
{	
	WCHAR ver[16]={0};
	GetInstallVersion(ver);
	CreateHostWindow();
	SetWindowText(ver);
	SetWindowSize(550,600);
	CenterWindow();
	CreateInstance();
	AdviseSink();
	AttachControl();
 
	WCHAR buf[MAX_PATH]={0};
	::GetCurrentDirectory(MAX_PATH,buf);
    	lstrcat(buf,L"\\..\\common\\movie.swf");
	LoadMovie(buf);
 
}
 
HRESULT MyFlash::FlashCall (_bstr_t xml_request)
{
	// TODO: handle request here
	SetReturnValue(L”<string> Hello!</string>”);
    	return S_OK;
}
Рекомендую полностью перестроить проект после внесения изменений в исходники.
В папке с фд-проектом я заготовил тестовую флешку с немудреным кодом, посмотрите.
Хочу напомнить, что основным классом приложения является MyFlash. Не такой уж он и сложный вырисовался в итоге, да?)) Это я к тому, что есть куда расти.

Это все наброски, вы понимаете. На следующем шаге попробуем что-нибудь изобразить уже в плане «движка» для оболочки, взаимодействующей с плеером. Ну, и обещанная фича - «Создание динамических диалоговых окон и дочерних форм на основе флеш-плеера» - в одной из последующих статей.
Вложения
Тип файла: zip test5.zip (145.5 Кб, 274 просмотров)
Всего комментариев 1

Комментарии

Старый 26.01.2013 10:41 alexcon314 вне форума
alexcon314

Оболочка вызывает флеш

Как-то так вышло, что существенную часть вопроса о взаимодействии оболочки и плеера я упустил, а именно "как вызывать функцию во флеше?". На этот недостаток мне указали в личной переписке. Дошли руки, исправляюсь.
Итак, чтобы взывать функцию на стороне флеша из оболочки, нужно использовать метод экземпляра флеш-контрола IShockwaveFlash::CallFunction(). Вот ее определение:
Код:
virtual HRESULT __stdcall CallFunction (
        /*[in]*/ BSTR request,
        /*[out,retval]*/ BSTR * response )
Как видно, первый параметр - строка-запрос к плееру с "просьбой" выполнить функцию, второй - строка, содержащая результат.
Ну и вот, собственно, как это можно использовать (дополняем файлы проекта из этой статьи следующими строками):
файл FlashWnd.h (просто добавим метод классу CFlashWnd)
Код:
 // common 
BSTR CallFunction(WCHAR* request);
файл FlashWnd.cpp (реализуем добавленный метод)
Код:
BSTR CFlashWnd::CallFunction(WCHAR* request)
{
	if(!isInit)
		return NULL;
	BSTR resp;
	HRESULT ret = flash->CallFunction(request, &resp);	
	return resp;
}
файл MyFlash.cpp (ну, как бы "на-коленочный" пример использования, уж простите)
Код:
void MyFlash::init()
{
	WCHAR ver[16]={0};
	GetInstallVersion(ver);
	CreateHostWindow();
	SetWindowText(ver);
	SetWindowSize(550,600);
	CenterWindow();
	CreateInstance();
	AdviseSink();
	AttachControl();

	WCHAR buf[MAX_PATH]={0};
	::GetCurrentDirectory(MAX_PATH,buf);
    lstrcat(buf,L"\\..\\common\\movie.swf");
	LoadMovie(buf);
	BSTR ret = CallFunction(L"<invoke name=\"getStatus\" returntype=\"xml\" />");
	::MessageBox(ret, L"CallFunction result");
	::SysFreeString(ret);
}
файл FD\src\gui\GuiTest5.as (тут, думаю, понятно - добавили коллбек getStatus(); ободряющий ответ "My status is OK!" - то что нужно )
Код AS3:
package gui 
{
	import flash.events.MouseEvent;
	import flash.external.ExternalInterface;
	import gui.*;
	// simple external interface test
	public class GuiTest5 extends SimpleGui
	{
		public function GuiTest5() 
		{
			super();
			o1(ExternalInterface.available);
			ExternalInterface.addCallback("getStatus", getStatus);
 
		}
		private function getStatus():String {
			o1("Requesting from container...");
			return "My status is OK!";
		}
		override public function btn1_clickHandler(evtObj:MouseEvent):void {
 
			var res:String = ExternalInterface.call("req", "Hello,World!");
			o2(res);
		}
		override public function btn2_clickHandler(evtObj:MouseEvent):void {
 
		}
	}
}
Вот как бы и вся любовь. Обратите внимание, что строка-запрос на вызов функции во флеше - это xml-форматированная стока, так же как и строка-ответ. Кроме того, не забывайте освобождать память, выделенную плеером для строки-ответа, если будете этими штучками пользоваться, по-внимательнее, вобщем.
Обновил(-а) alexcon314 26.01.2013 в 19:57
 

 


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


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