BREWアプリを Windows へ移植(4)
IShell インターフェースの、AddRef(), Release(), CreateInstance(), GetDeviceInfo(), SetTimer(), CancelTimer() の実装です。
AddRef(), Release() の実装はいつもと同じです。
uint32 WinShell::AddRef( IShell* pIShell ){ return ++((WinShell*)pIShell)->_ref; } uint32 WinShell::Release( IShell* pIShell ){ WinShell* pMe = (WinShell*)pIShell; if( --pMe->_ref == 0 ){ delete pMe; return 0; } return pMe->_ref; }
_ref は uint32型のメンバです。コンストラクタで1に初期化しておく必要があります。
GetDeviceInfo() は、本当は構造体のサイズをチェックしたり null チェックをしたり値をちゃんと代入したりしてやらないといけないのですが、面倒なので0で初期化して適当にそれっぽい値を代入しておきます。
void WinShell::GetDeviceInfo(IShell * po, AEEDeviceInfo * pi){ MEMSET( pi , 0 , sizeof( AEEDeviceInfo ) ); pi->cxScreen = Const::DeviceWidth; // 240 pi->cyScreen = Const::DeviceHeight; // 296 pi->dwRAM = 10 * 1024 * 1024; // 10MB pi->dwPlatformID = 10000; // いくつでもいいや }
CreateInstance() は、インスタンスの生成を行います。
特定の ClassID が渡された場合はそのインスタンスを、それ以外の場合はとりあえず AEEMod_Load() と IMODULE_CreateInstance() を呼び出して、成功するかどうかを見てやることにします。
ClassID が同じであれば成功するはずです。それ以外であればエラーと考えていいでしょう。
int WinShell::CreateInstance(IShell * po, AEECLSID ClsId, void ** ppobj){ WinShell* pMe = (WinShell*)po; *ppobj = null; switch( ClsId ){ case AEECLSID_DISPLAY: (*ppobj) = new WinDisplay(); break; case AEECLSID_HEAP: (*ppobj) = new WinHeap(); break; case AEECLSID_FILEMGR: (*ppobj) = new WinFileMgr(); break; // 以下いっぱい追加していく予定 default: // とりあえず AEEMod_Load を呼び出してみる { int ret = ::AEEMod_Load( pMe , WinAppFrame::Get()->GetHelper() , (IModule**)ppobj ); if( ret != SUCCESS ){ // throw std::exception( "正常に生成出来なかった" ); return ret; } } // IMODULE_CreateInstance() を呼び出さないと、クラスID が正しいかどうか判断出来ない。 { IApplet* pIApplet = null; int ret = IMODULE_CreateInstance( (IModule*)(*ppobj) , pMe , ClsId , (void**)&pIApplet ); if( ret != SUCCESS ){ // 失敗 IMODULE_Release( (IModule*)(*ppobj) ); (*ppobj) = null; }else{ // 成功。ちゃんと解放してやること。 IAPPLET_Release( pIApplet ); } return ret; } break; } if( (*ppobj) == null ){ return EFAILED; }else{ return SUCCESS; } }
本来であればアプレットの登録リストから情報を読み出して、その情報から生成するのが正しい方法かもしれないですが、そこまでやる必要は無いでしょう。
最後に SetTimer() の実装です。
ISHELL_SetTimerEx() は、
#define ISHELL_SetTimerEx(p,s,pcb) GET_PVTBL(p,IShell)->SetTimer(p,s,(PFNNOTIFY)pcb,(void *)pcb)
となっているので、PFNNOTIFY と void* に同じ値が入ってきた場合はこちらの処理を行う必要があります。
int WinShell::SetTimer(IShell * po, int32 dwMsecs, PFNNOTIFY pfn, void * pUser){ AEECallback call; AEECallback* pCall; if( (void*)pfn == pUser ){ pCall = (AEECallback*)pfn; }else{ call.pfnNotify = pfn; call.pNotifyData = pUser; pCall = &call; } // CallbackList は list< AEECallback > の typedef for( CallbackList::iterator it = ((WinShell*)po)->_callbackList.begin() ; it != ((WinShell*)po)->_callbackList.end() ; ++it ){ // 同じタイマがあるか if( (*it).pfnNotify == pCall->pfnNotify && (*it).pNotifyData == pCall->pNotifyData ){ void* time = (void*)(GETTIMEMS() + dwMsecs); // 遅い方に合わせる if( (*it).pmc > time ){ (*it).pmc = time; } return SUCCESS; } } // pmc を満了時間の代わりとして使う pCall->pmc = (void*)(GETTIMEMS() + dwMsecs); ((WinShell*)po)->_callbackList.push_back( (*pCall) ); return SUCCESS; }
こんな感じでしょうか。
ただ、SetTimerEx() は本来、AEECallback へのポインタを登録するはずなのですが、実体をコピーしてリストに登録しているので、セットした後に変更したりは出来ないし、pfnNotify と pfnNotifyData で比較しているので、同じ関数を別々の AEECallback へ入れて処理をするというのも出来ません。
まあ、自分は SetTimer() しか使ってないのでこれでいいや(;´Д`)
これでとりあえず IShell の必要な部分の実装は終わりです。
あとは、
void * WinShell::LoadResData(IShell * po, const char * pszResFile, uint16 nResID, ResType nType){ throw std::exception( "not implemented" ); }
こんな感じで例外を投げてやればいいかと。