Пишем свою оболочку для FP под Windows. Шаг 5.
Advise Sink: cоветую утопиться.
Ну, вот. Похоже на то, что на этом шаге наши пути разойдутся. Если, конечно, они не разошлись раньше)). С хостом плеера более-менее разобрались. Осталось научить нашу оболочку общаться с плеером в духе «вопрос-ответ». Ну, и конечно, отдавать ему приказы. Как вы догадались, речь пойдет о взаимодействии оболочки и плеера через ExternalInterface. Видимо, это последнее, что можно отнести к «стандартным» знаниям об оболочке FP. Далее возможны варианты, то, как вы реализуете это взаимодействие — целиком ваша прерогатива. Я, по всей видимости, пойду своим путем. Конечно, я не замолкну и продолжу свои публикации насколько меня хватит... но это уже будет совсем другая песня. Ах, да. Есть еще одна фича про запас. Но о ней позже.
К делу. Опять нас выручит один из COM-интерфейсов, на этот раз IDispatch.
Но не все так просто. Процедура увязывания внешних обработчиков запросов контрола (событий, если хотите) с самим контролом не совсем тривиальна. Попробую несколько прояснить ситуацию.
Смотрите, в плеере что-то произошло, такое, что ему надо об этом сообщить хосту. Как в этом случае можно поступить? Правильно. Реализовать что-то навроде родимого EventDispatcher'a. На языке COM это выглядит примерно так: контролу специальным методом (через промежуточный интерфейс), подставляется стандартный интерфейс (IDispatch) для ловли этих самых его запросов-событий. Т.к. интерфейс стандартный, с его завязкой в контрол проблем нет, он специально так и спроектирован. У этого интерфейса есть, собственно, одна функция Invoke(), которую плеер дергает, когда ему надо. Прелесть в том, что мы сами реализуем эту функцию, ну, ее тело, конечно. Т.о. мы получаем великолепную возможность собственноручно обрабатывать запросы плеера, что называется, «с пылу-с жару). В эту функцию валятся все запросы. АПИ плеера должно бы документировать их идентификаторы (ID). Где-то я видел что-то вроде таблички сопоставления ID и запросов, но, к несчастью, не помню где. Да все нам и не надо. Потом, вы сами сможете вычислить эти идентификаторы с помощью дебаггера прямо в студии, ничего сложного. Потом пропускаем ID запроса через банальный switch и возвращаем, если это возможно результат. Вот и вся кухня.
Имплементим IDispatch в наш базовый класс.
Тут есть такая возможность — унаследовать наш класс от абстрактного IDispatch, заменив абстрактные члены на нашу реализацию. И подставить его плееру, в качестве «слушателя» событий. Так и поступим.
// 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;}
// 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; }
Обратите внимание на реализацию 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.
Generated when the ready state of the control changes. The possible states are: 0=Loading, 1=Uninitialized, 2=Loaded, 3=Interactive, 4=Complete.
#pragma once #include "flashwnd.h" class MyFlash : public CFlashWnd { private: void init(); public: MyFlash (void){ init();}; ~MyFlash(void){}; HRESULT FlashCall (_bstr_t xml_request ); };
#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. Не такой уж он и сложный вырисовался в итоге, да?)) Это я к тому, что есть куда расти.
Это все наброски, вы понимаете. На следующем шаге попробуем что-нибудь изобразить уже в плане «движка» для оболочки, взаимодействующей с плеером. Ну, и обещанная фича - «Создание динамических диалоговых окон и дочерних форм на основе флеш-плеера» - в одной из последующих статей.
Всего комментариев 1
Комментарии
![]() ![]() |
|
Оболочка вызывает флешКак-то так вышло, что существенную часть вопроса о взаимодействии оболочки и плеера я упустил, а именно "как вызывать функцию во флеше?". На этот недостаток мне указали в личной переписке. Дошли руки, исправляюсь.
Итак, чтобы взывать функцию на стороне флеша из оболочки, нужно использовать метод экземпляра флеш-контрола IShockwaveFlash::CallFunction(). Вот ее определение: Код:
virtual HRESULT __stdcall CallFunction ( /*[in]*/ BSTR request, /*[out,retval]*/ BSTR * response ) Ну и вот, собственно, как это можно использовать (дополняем файлы проекта из этой статьи следующими строками): файл FlashWnd.h (просто добавим метод классу CFlashWnd) Код:
// common BSTR CallFunction(WCHAR* request); Код:
BSTR CFlashWnd::CallFunction(WCHAR* request) { if(!isInit) return NULL; BSTR resp; HRESULT ret = flash->CallFunction(request, &resp); return resp; } Код:
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); } ![]() 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 { } } } |
|
Обновил(-а) alexcon314 26.01.2013 в 19:57
|
Последние записи от 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)