// LWStreamSystem.cpp : 定义 DLL 应用程序的入口点。 // #include "stdafx.h" #include "LWStreamSystem.h" #include "StreamThread.h" ILWStreamSystem* CreateStreamSystem() { static LWStreamSystem* _StreamSys = NULL; if (_StreamSys == NULL) { #if DBG_MEMORY LInitCore(); #endif _StreamSys = new LWStreamSystem; } //assert(_StreamSys); return _StreamSys; } class LWBindStatusCallback : public IBindStatusCallback { public: LWBindStatusCallback(LWStreamSystem *System, void *UserData) { m_StreamSystem = System; m_UserData = UserData; m_RefCnt = 1; } private: // IUnknown methods STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject) { *ppvObject = NULL; // IUnknown if (::IsEqualIID(riid, __uuidof(IUnknown))) { *ppvObject = this; } // IBindStatusCallback else if (::IsEqualIID(riid, __uuidof(IBindStatusCallback))) { *ppvObject = static_cast(this); } if (*ppvObject) { (*reinterpret_cast(ppvObject))->AddRef(); return S_OK; } return E_NOINTERFACE; } STDMETHOD_(ULONG, AddRef)(){ return ++m_RefCnt; } STDMETHOD_(ULONG, Release)(){ return --m_RefCnt; } // IBindStatusCallback methods STDMETHOD(OnStartBinding)(DWORD, IBinding *){ return S_OK; } STDMETHOD(GetPriority)(LONG *){ return E_NOTIMPL; } STDMETHOD(OnLowResource)(DWORD){ return S_OK; } STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) { //TRACE(_T("ulProgress: %lu, ulProgressMax: %lu\n"), // ulProgress, ulProgressMax); //TRACE(_T("ulStatusCode: %lu "), ulStatusCode); if (ulStatusCode < BINDSTATUS_FINDINGRESOURCE || ulStatusCode > BINDSTATUS_DECODING) ulStatusCode = BINDSTATUS_DECODING + 1; //TRACE(_T("(%s), szStatusText: %ls\n"), // plpszStatus[ulStatusCode - BINDSTATUS_FINDINGRESOURCE], // szStatusText); char statbuf[2048]; char cbuf[2048]; if (szStatusText == NULL) return S_OK; int len = wcslen(szStatusText); if (len > 2048 - 1) len = 2048 -1; if (len <= 0) return S_OK; wcstombs(cbuf, szStatusText, len); cbuf[len] = '\0'; BOOL printsomething = TRUE; int percent = 0L; switch(ulStatusCode) { case BINDSTATUS_FINDINGRESOURCE : sprintf(statbuf, "查找服务器 %s...", cbuf); break; case BINDSTATUS_CONNECTING : sprintf(statbuf, "连接 %s...", cbuf); break; case BINDSTATUS_BEGINDOWNLOADDATA : sprintf(statbuf, "开始下载数据...", cbuf); break; case BINDSTATUS_DOWNLOADINGDATA : percent = (int) ((float) ulProgress/(float)ulProgressMax * 100.f); sprintf(statbuf, "下载 %s (%d%%)...", cbuf, percent); if (m_StreamSystem) m_StreamSystem->NotifyProgress((float) percent / 100.f, m_UserData); break; case BINDSTATUS_ENDDOWNLOADDATA : sprintf(statbuf, "完成", cbuf); break; case BINDSTATUS_REDIRECTING : case BINDSTATUS_BEGINDOWNLOADCOMPONENTS : case BINDSTATUS_INSTALLINGCOMPONENTS : case BINDSTATUS_ENDDOWNLOADCOMPONENTS : case BINDSTATUS_USINGCACHEDCOPY : case BINDSTATUS_SENDINGREQUEST : case BINDSTATUS_CLASSIDAVAILABLE : case BINDSTATUS_MIMETYPEAVAILABLE : case BINDSTATUS_CACHEFILENAMEAVAILABLE : case BINDSTATUS_BEGINSYNCOPERATION : case BINDSTATUS_ENDSYNCOPERATION : case BINDSTATUS_BEGINUPLOADDATA : case BINDSTATUS_UPLOADINGDATA : case BINDSTATUS_ENDUPLOADDATA : case BINDSTATUS_PROTOCOLCLASSID : case BINDSTATUS_ENCODING : case BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : case BINDSTATUS_CLASSINSTALLLOCATION : case BINDSTATUS_DECODING : case BINDSTATUS_LOADINGMIMEHANDLER : default : printsomething = FALSE; break; } if (printsomething && m_StreamSystem && m_StreamSystem->GetClient()) { m_StreamSystem->GetClient()->OnSetStatusText(statbuf); } return S_OK; } STDMETHOD(OnStopBinding)(HRESULT, LPCWSTR){ return S_OK; } STDMETHOD(GetBindInfo)(DWORD *, BINDINFO *) { return S_OK; } STDMETHOD(OnDataAvailable)(DWORD, DWORD, FORMATETC *, STGMEDIUM *) { return S_OK;} STDMETHOD(OnObjectAvailable)(REFIID, IUnknown *) { return S_OK; } protected: ULONG m_RefCnt; LWStreamSystem* m_StreamSystem; void* m_UserData; }; LWStreamSystem::LWStreamSystem(void) { InitializeCriticalSection(&m_CriticalSec); m_Client = NULL; m_LoadRequests.clear(); m_StreamThread = NULL; } LWStreamSystem::~LWStreamSystem(void) { // 锁定 Lock(); m_StreamThread->m_StreamSystem = NULL; for (int i = 0; i < m_LoadRequests.size(); i++) { StreamRequest* Req = m_LoadRequests[i]; delete Req; } m_LoadRequests.clear(); Unlock(); DeleteCriticalSection(&m_CriticalSec); // 让线程来终止自己. m_StreamThread->End(); #if DBG_MEMORY LExitCore(); #endif STREAM_LOG("Stream system shutdown.\n"); } // 释放对象 void LWStreamSystem::Release() { delete this; } // 初始化 BOOL LWStreamSystem::Intialise() { memset(m_BaseUrl,0,1024); m_StreamThread = new StreamThread(this); if (m_StreamThread == NULL) { return FALSE; } m_StreamThread->Begin(); return TRUE; } BOOL LWStreamSystem::NeedStream() { Lock(); BOOL Ret = m_LoadRequests.empty(); Unlock(); return !Ret; } BOOL LWStreamSystem::Stream() { if(NeedStream()) { m_StreamThread->Resume(); } return TRUE; } #define USE_GROUP_PRIORITY 1 BOOL LWStreamSystem::ProcessAllRequests() { std::vector requests; // 将队列中的请求拷贝到链表中. Lock(); int sz = m_LoadRequests.size(); //int strmsz = m_streamLoadRequests.size(); int strmsz = 0; int totalsz = sz + strmsz; requests.reserve(sz + strmsz); while (!m_LoadRequests.empty()) { StreamRequest *pRequest = m_LoadRequests.front(); requests.push_back(pRequest); m_LoadRequests.pop_front(); } /*while (!m_streamLoadRequests.empty()) { sAnmLoadRequest *pRequest = m_streamLoadRequests.front(); requests.push_back(pRequest); m_streamLoadRequests.pop_front(); }*/ Unlock(); assert(m_StreamThread); if (totalsz > 0) { #if USE_GROUP_PRIORITY UINT OldPriority = THREAD_PRIORITY_NORMAL; if (m_StreamThread) { OldPriority = m_StreamThread->GetPriority(); m_StreamThread->SetPriority(THREAD_PRIORITY_HIGHEST); } #endif for (int i = 0; i < totalsz; i ++) { // N.B.: don't do this if we're shutting down // if (!m_app->Paused()) if (m_StreamThread->IsRunning()) ProcessRequest(requests[i]); } #if USE_GROUP_PRIORITY m_StreamThread->SetPriority(OldPriority); #endif } return TRUE; } void LWStreamSystem::SetBaseUrl(const char* BaseUrl) { Lock(); strcpy(m_BaseUrl,BaseUrl); Unlock(); } // 设置/返回Stream系统监听者 void LWStreamSystem::SetClient(ILWStreamClient* Clinet) { m_Client = Clinet; } ILWStreamClient* LWStreamSystem::GetClient() const { return m_Client; } // 以默认注册的回调结构创建资源 void LWStreamSystem::StreamResource(const char* FileName) { } // 以特定的回调结构创建资源. void LWStreamSystem::StreamResource(const char* FileName, const ResourceCallback* ResCB, void* UserData) { assert(ResCB); String* Url = new String(FileName); StreamRequest* Req = new StreamRequest(m_Client,Url,m_BaseUrl,ResCB->LoadedCB,ResCB->ActiveCB,ResCB->ProgressCB,UserData,FALSE); assert(Req); Lock(); //if (bAllowStreaming) // m_streamLoadRequests.push_back(pRequest); //else m_LoadRequests.push_back(Req); Unlock(); } // 注册资源对象的回调 // @param ResExt 资源扩展名 // @param ResCB 资源回调结构 BOOL LWStreamSystem::RegisterResource(const char* ResExt, const ResourceCallback* ResCB) { return TRUE; } BOOL LWStreamSystem::ProcessRequest(StreamRequest* Req) { assert(Req != NULL); String* RequestUrl; String* CachedFileName = NULL; String* CachedUrl = NULL; ILWResource* pAsset = NULL; BOOL stat = FALSE; BOOL bStreaming = FALSE; RequestUrl = Req->RequestUrl; if (RequestUrl->length() > 0) { stat = LoadCachedFile(RequestUrl, Req->RequestBaseUrl, &CachedFileName, &CachedUrl, &pAsset, Req); /*if (stat == TRUE) { break; }*/ } if (stat == TRUE) { // Lock the file itself so it isn't thrown out of the cache FILE *cachefilefp = NULL; if( !bStreaming ) { cachefilefp = fopen(CachedFileName->c_str(), "r"); if (cachefilefp == NULL) { // warning delete Req; if (CachedFileName) { delete CachedFileName; CachedFileName = NULL; } if (CachedUrl) { delete CachedUrl; CachedUrl = NULL; } return FALSE; } } #if (!USE_GROUP_PRIORITY) UINT OldPriority = THREAD_PRIORITY_NORMAL; if (m_StreamThread) { OldPriority = m_StreamThread->GetPriority(); m_StreamThread->SetPriority(THREAD_PRIORITY_HIGHEST); } #endif // 锁定缓存文件. if (CachedUrl) LockUrlCache(CachedUrl); // 如果资源已经存在,我们首先调用引用回调. if (pAsset && Req->RefCallback) { (*Req->RefCallback)(RequestUrl, CachedFileName, pAsset, Req->UserData); } else if (Req->RequestLoadedCallback) { pAsset = (*Req->RequestLoadedCallback)(RequestUrl, CachedFileName, Req->UserData); if (pAsset) { // 加入到资源缓存中. if (m_Client) { m_Client->AddResource(CachedFileName->c_str(),pAsset); } } } // 解锁缓存文件 if (CachedUrl) UnlockUrlCache(CachedUrl); if (CachedFileName) { delete CachedFileName; CachedFileName = NULL; } if (CachedUrl) { delete CachedUrl; CachedUrl = NULL; } #if (!USE_GROUP_PRIORITY) if(m_StreamThread) { m_StreamThread->SetPriority(OldPriority); } #endif if( cachefilefp ) { fclose(cachefilefp); } } if (CachedFileName) { delete CachedFileName; CachedFileName = NULL; } if (CachedUrl) { delete CachedUrl; CachedUrl = NULL; } delete Req; return stat; } BOOL LWStreamSystem::LoadCachedFile(String* url, const char *baseUrl, String** pCacheFileName, String** pCacheUrl, ILWResource **ppAsset, StreamRequest *pRequest) { char *fullname = (char *) malloc(url->length() + strlen(baseUrl) + 1); URLUtility::ComputeFullName(url->c_str(), baseUrl, fullname); char *statusbuf = (char*) malloc(strlen(fullname) + strlen(STREAM_LOADING_MSG) + 1); sprintf(statusbuf, STREAM_LOADING_MSG, fullname); // 需要优化, 避免不必要的new delete BOOL urlLoaded = UrlLoaded(fullname, pCacheFileName, pCacheUrl); ILWResource* pAsset = NULL; if (urlLoaded && m_Client) { pAsset = m_Client->FindResource((*pCacheFileName)->c_str()); //pAsset = LookupAssetInCache(*pCacheFileName); } if (pAsset) { // 如果资源已经创建,直接返回. if (ppAsset) *ppAsset = pAsset; } else if (urlLoaded && URLUtility::IsLocalFile(fullname)) { // 如果文件已经在本机,且是本机文件,直接读取. if (m_Client) { m_Client->OnSetStatusText(statusbuf); m_Client->OnSetStatusText("完成"); } } else { // If not, download it LWBindStatusCallback bsc(this, pRequest); String* cachedFileName = new String; cachedFileName->resize(2048,0); if (m_Client) { m_Client->OnSetStatusText(statusbuf); } if ( FAILED(URLDownloadToCacheFile(NULL, fullname, &(*cachedFileName)[0], 2048-1, 0,&bsc)) ) { // 文件没有找到. if (m_Client) { m_Client->OnSetStatusText("未找到文件."); } free(fullname); free(statusbuf); if (*pCacheFileName) { delete *pCacheFileName; *pCacheFileName = NULL; } if (*pCacheUrl) { delete *pCacheUrl; *pCacheUrl = NULL; } return FALSE; } if (*pCacheFileName) { delete *pCacheFileName; } if (*pCacheUrl) { delete *pCacheUrl; } *pCacheFileName = cachedFileName; if (pCacheUrl) *pCacheUrl = new String(fullname); if (m_Client) { m_Client->OnSetStatusText("完成"); } } free(fullname); free(statusbuf); return TRUE; } // 辅助函数,规范URL路径 static char *CanonicalizeUrl(const char* url) { DWORD buflen = 1; DWORD flags = ICU_DECODE | ICU_NO_ENCODE; char dummy; BOOL stat = InternetCanonicalizeUrl(url, &dummy, &buflen, flags); char *buf = new char[buflen+1]; stat = InternetCanonicalizeUrl(url, buf, &buflen, flags); if (stat) return buf; else { delete buf; return NULL; } } // 存于本地磁盘或者缓存目录(离线文件)都被当作已经下载. BOOL LWStreamSystem::UrlLoaded(const char* url, String** pCacheFileName, String** pCacheUrl) { String* cachedFileName = NULL; if (URLUtility::IsLocalFile(url)) { char *canonical = CanonicalizeUrl(url); // URL 不规范. if (canonical == NULL) return FALSE; char *fname = canonical; if (!strncmp(fname, "file://", 7)) { fname = fname + 7; if (*fname == '/') // 有可能是 file:/// fname++; } // 是否存在本地文件 if (_access(fname, 0) == -1) { return FALSE; } else { cachedFileName = new String(fname); } delete canonical; } else if (UrlInCache(url, &cachedFileName)) { if (pCacheUrl) *pCacheUrl = new String(url); } else { return FALSE; } *pCacheFileName = cachedFileName; return TRUE; } // TODO 我们或许需要自己实现Cache 函数. 要能支持用户自定义缓存目录. // 但是现在我们使用浏览器(IE | FIREFOX)缓存目录. BOOL LWStreamSystem::UrlInCache(const char *url, String** pCacheFileName) { INTERNET_CACHE_ENTRY_INFO *pIci = NULL; DWORD bufsize = 0L; // 获得文件缓存信息. BOOL stat = GetUrlCacheEntryInfo(url, pIci, &bufsize); if (!stat) { DWORD err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) { return FALSE; } // 处于缓存目录. else if (err == ERROR_INSUFFICIENT_BUFFER) { BYTE* Buffer = new BYTE[bufsize]; pIci = (INTERNET_CACHE_ENTRY_INFO *)Buffer; memset(pIci, 0, bufsize); pIci->dwStructSize = bufsize; stat = GetUrlCacheEntryInfo(url, pIci, &bufsize); if (stat) { // 简单判断资源是否安全... LONG filetimecompare = CompareFileTime(&pIci->LastModifiedTime, &pIci->LastAccessTime); if (filetimecompare < 1) { *pCacheFileName = new String(pIci->lpszLocalFileName); if (pIci) { delete[] pIci; } return TRUE; } else { if (pIci) { delete[] pIci; } return FALSE; } } } else { } } if (pIci) { delete [] pIci; } return FALSE; } BOOL LWStreamSystem::LockUrlCache(String* url) { assert(url); INTERNET_CACHE_ENTRY_INFO *pIci = NULL; DWORD bufsize = 0L; BOOL stat = RetrieveUrlCacheEntryFile(url->c_str(), pIci, &bufsize, 0L); if (!stat) { DWORD err = GetLastError(); if (err == ERROR_FILE_NOT_FOUND) { return FALSE; } else if (err == ERROR_INSUFFICIENT_BUFFER) { BYTE* Buffer = new BYTE[bufsize]; pIci = (INTERNET_CACHE_ENTRY_INFO *)Buffer; memset(pIci, 0, bufsize); pIci->dwStructSize = bufsize; stat = RetrieveUrlCacheEntryFile(url->c_str(), pIci, &bufsize, 0L); if (stat) { if (pIci) { delete [] pIci; } return TRUE; } } else { // ??? } } if (pIci) { delete [] pIci; } return FALSE; } BOOL LWStreamSystem::UnlockUrlCache(String* url) { assert(url); INTERNET_CACHE_ENTRY_INFO *pIci = NULL; return UnlockUrlCacheEntryFile(url->c_str(), 0L); } void LWStreamSystem::NotifyProgress(float progress, void *userData) { StreamRequest *Req = (StreamRequest*)userData; if (Req->RequestProgressCallback) { (*Req->RequestProgressCallback)(progress,Req->UserData); } }