//--------------------------------------------------------------------------- #include #include #include #include #include #include #include "UpgradeThread.h" #pragma hdrstop #pragma package(smart_init) #pragma comment(lib, "wininet") //--------------------------------------------------------------------------- #define UPDATE_APP_NAME "AppUpgrade.exe" #define MD5_FILE_NAME "filemd5.lst" //#define GLOBAL_VAR_FILE "glbvrf.cnf" //#define DEFAULT_SERVER "23.236.66.46:88" //"127.0.0.1" // #define GLOBAL_VAR_VERSION 0x03 extern String MAIN_APP_NAME; typedef struct tGVFHeader{ char head[4]; WORD idSoftware; WORD iUpdate; tGVFHeader(){ head[0] = 'G'; head[1] = 'V'; head[2] = 'F'; head[3] = GLOBAL_VAR_VERSION; iUpdate = 0; } } TGVFHEADER; typedef struct tGetUrlParam { char server[128]; int idSoft; int errCode; HINTERNET hSession, hRequest; int sizeOut; char *dataOut; tGetUrlParam(){ memset( this, 0, sizeof(tGetUrlParam) ); }; } TGetUrlParam; typedef struct tDownloadParam { char url[256]; char filename[256]; HWND hWnd; int errCode; HINTERNET hSession, hRequest; tDownloadParam(){ memset( this, 0, sizeof(tDownloadParam) ); }; } TDownloadParam; using namespace std; //--------------------------------------------------------------------------- DWORD WINAPI GetUrlProc(LPVOID args) { TGetUrlParam* param = (TGetUrlParam*)args; param->hSession = InternetOpen( L"RookIE/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 ); if( param->hSession ){ String api = L"http://" + (String)param->server + L"/Api/getUpgradeUrl?sid=" + IntToStr(param->idSoft); param->hRequest = InternetOpenUrl( param->hSession, api.c_str(), NULL, 0, INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_RELOAD|INTERNET_FLAG_PRAGMA_NOCACHE, 0 ); if( param->hRequest ){ int pos = 0; DWORD dwByteRead = 0; char szBuffer[1024]; ZeroMemory(szBuffer, sizeof(szBuffer)); ZeroMemory(param->dataOut, param->sizeOut); // 循环读取缓冲区内容直到结束 while( InternetReadFile(param->hRequest, szBuffer, sizeof(szBuffer), &dwByteRead) && dwByteRead > 0){ if( pos + dwByteRead > param->sizeOut ){ param->errCode = ERROR_SIZE; break; } // 复制输出数据 memcpy( param->dataOut+pos, szBuffer, dwByteRead ); pos += dwByteRead; // 清空缓冲区以备下一次读取 ZeroMemory(szBuffer, sizeof(szBuffer)); } param->dataOut[pos] = 0; param->sizeOut = pos; } else param->errCode = ERROR_INTERNET_REQ; } else param->errCode = ERROR_INTERNET_OPEN; return 0; } DWORD WINAPI DownloadProc(LPVOID args) { TDownloadParam* param = (TDownloadParam*)args; param->hSession = InternetOpen( L"RookIE/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 ); if( param->hSession ){ param->hRequest = InternetOpenUrl( param->hSession, ((String)param->url).c_str(), NULL, 0, INTERNET_FLAG_DONT_CACHE|INTERNET_FLAG_RELOAD|INTERNET_FLAG_PRAGMA_NOCACHE, 0 ); if( param->hRequest ){ int pos = 0; DWORD dwAllBytes = 0, dwSize = 4; if( param->hWnd ){ HttpQueryInfo( param->hRequest, HTTP_QUERY_CONTENT_LENGTH|HTTP_QUERY_FLAG_NUMBER, &dwAllBytes, &dwSize, NULL ); } FILE* fp = fopen( param->filename, "wb" ); if( fp ){ char szBuffer[2048] = "\0"; DWORD dwByteRead = 0, dwReceived = 0; ZeroMemory(szBuffer, sizeof(szBuffer)); // 循环读取缓冲区内容直到结束 while (InternetReadFile(param->hRequest, szBuffer, sizeof(szBuffer), &dwByteRead) && dwByteRead > 0){ fwrite( szBuffer, 1, dwByteRead, fp ); if( param->hWnd ){ //显示进度 dwReceived += dwByteRead; int pos = (int)( dwReceived * 100.0 / dwAllBytes + 0.5 ); SendMessage( param->hWnd, UM_PROGRESS, 1, pos ); } // 清空缓冲区以备下一次读取 ZeroMemory(szBuffer, sizeof(szBuffer)); } fclose( fp ); if( dwReceived==dwAllBytes ){ SendMessage( param->hWnd, UM_PROGRESS, 1, 100 ); param->errCode = 0; } else param->errCode = ERROR_FILE_SIZE; } else param->errCode = ERROR_OPEN_FILE; } else param->errCode = ERROR_INTERNET_REQ; } else param->errCode = ERROR_INTERNET_OPEN; return 0; } //--------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////////////// // __fastcall TUpgradeThread::TUpgradeThread(bool CreateSuspended, int mode, int sid, String sVar, HWND hWnd) : TThread(CreateSuspended) { m_iMode = mode; m_idSoft = sid; m_hWnd = hWnd; m_lstFile = NULL; m_bUpdateSelf = false; m_AppPath = TPath::GetFullPath(ExtractFilePath(Application->ExeName)); m_PathName = m_AppPath + MAIN_APP_NAME; // m_Server = DEFAULT_SERVER; m_hSema = CreateSemaphore( NULL, 0, 1, NULL ); m_GlobalFile = m_AppPath + sVar;// GLOBAL_VAR_FILE; } //--------------------------------------------------------------------------- void __fastcall TUpgradeThread::Cancel() { Terminate(); ReleaseSemaphore( m_hSema, 1, NULL ); } //--------------------------------------------------------------------------- void __fastcall TUpgradeThread::Execute() { int ret = 0; //获取列表文件 //对比文件MD5码=>更新列表 //下载更新列表中的文件 if( m_iMode==3 ){ Sleep(1000); //等待app完全退出 if( UpdateFiles() ){ RegUpdateFlag( 0 ); RestartApp(); } } else if( m_iMode==1 ){ MakeMD5File(); } else{ String strText, strProg1, strProg2; //清空目录 ClearUpdateDir(); Randomize(); if( m_hWnd ){ strText = "正在获取更新列表"; SendMessage( m_hWnd, UM_SHOWTEXT, 0, (LPARAM)strText.c_str() ); strProg1 = L"——"; SendMessage( m_hWnd, UM_SHOWTEXT, 1, (LPARAM)strProg1.c_str() ); strProg2 = L"正在下载列表文件"; SendMessage( m_hWnd, UM_SHOWTEXT, 2, (LPARAM)strProg2.c_str() ); } String umdfile = GetUpdateMD5File(); if( umdfile.Length()>0 && !Terminated ){ if( m_hWnd ){ strText = "正在检查更新列表"; SendMessage( m_hWnd, UM_SHOWTEXT, 0, (LPARAM)strText.c_str() ); } TStringList* ufnLst = new TStringList; int ret = GetUpdateFileList( umdfile, ufnLst ); if( ret==0 && !Terminated ){ int count = ufnLst->Count; if( count > 0 ){ if( m_hWnd ){ strText = "正在下载更新文件"; SendMessage( m_hWnd, UM_SHOWTEXT, 0, (LPARAM)strText.c_str() ); } for( int i=0; iCount && !Terminated; i++ ){ if( m_hWnd ){ strProg1 = L"文件数 " + IntToStr( i ) + L"/" + IntToStr( m_lstFile->Count ); SendMessage( m_hWnd, UM_SHOWTEXT, 1, (LPARAM)strProg1.c_str() ); int pos = (int)( i*100.0/m_lstFile->Count + 0.5 ); SendMessage( m_hWnd, UM_PROGRESS, 0, pos ); strProg2 = L"当前文件 " + (*m_lstFile)[i]; SendMessage( m_hWnd, UM_SHOWTEXT, 2, (LPARAM)strProg2.c_str() ); } String md5; if( !GenerateFileMD5( (*m_lstFile)[i], md5 ) ) break; if( m_hWnd ) SendMessage( m_hWnd, UM_PROGRESS, 1, 100 ); char text[256]; sprintf( text, "%s\t%s\n", ((AnsiString)(*m_lstFile)[i]).c_str(), ((AnsiString)md5).c_str() ); fwrite( text, 1, strlen(text), fp ); } if( !Terminated && m_hWnd ) SendMessage( m_hWnd, UM_PROGRESS, 0, 100 ); fclose( fp ); } } delete m_lstFile; delete m_lstMD5; m_lstFile = NULL; return !Terminated; } //--------------------------------------------------------------------------- bool __fastcall TUpgradeThread::GenerateFileMD5( String fname, String& md5 ) { String strName = m_AppPath + fname; THashMD5* pMD5 = new THashMD5(); TFileStream* fs = new TFileStream( strName, fmOpenRead | fmShareDenyNone ); fs->Position = 0; int sizeBuf = 1024 * 1024; char* buf = (char*)malloc( sizeBuf ); while( !Terminated && fs->Position < fs->Size ){ if( fs->Position + sizeBuf > fs->Size ) sizeBuf = fs->Size - fs->Position; fs->ReadBuffer( buf, sizeBuf ); pMD5->Update( buf, sizeBuf ); if( m_hWnd ){ //显示进度 int pos = (int)( fs->Position * 100.0 / fs->Size + 0.5 ); SendMessage( m_hWnd, UM_PROGRESS, 1, pos ); } } if( !Terminated ){ md5 = pMD5->HashAsString(); } free( buf ); delete fs; delete pMD5; return !Terminated; } //--------------------------------------------------------------------------- int __fastcall TUpgradeThread::DownloadFile( String url, String filename ) { int ret = -1; String path = m_AppPath + L"upgrade\\"; if(!DirectoryExists(path, false)) ForceDirectories(path); DeleteUrlCacheEntry(url.c_str()); path += filename; for( int i=0; i<3; i++ ){ //尝试3次 TDownloadParam param; param.hWnd = m_hWnd; strcpy( param.url, ((AnsiString)url).c_str() ); strcpy( param.filename, ((AnsiString)path).c_str() ); DWORD id; HANDLE hThread = CreateThread( NULL, 0, DownloadProc, ¶m, 0, &id ); if( hThread ){ HANDLE aHandle[2] = { m_hSema, hThread }; DWORD dwRet = WaitForMultipleObjects( 2, aHandle, FALSE, INFINITE ); if( param.hRequest ) InternetCloseHandle( param.hRequest ); if( param.hSession ) InternetCloseHandle( param.hSession ); WaitForSingleObject( hThread, 200 ); CloseHandle( hThread ); if( dwRet==WAIT_OBJECT_0 ) //用户退出 break; else if( dwRet==WAIT_OBJECT_0+1 ){ //线程正常退出 if( param.errCode==0 ){ ret = 0; break; } else if( param.errCode==ERROR_OPEN_FILE ) break; } } } return ret; } //--------------------------------------------------------------------------- bool __fastcall TUpgradeThread::GetFileList( TStringList* pList ) { TSearchRec sr; int iAttr = 0; String strPath = m_AppPath; if( FindFirst(strPath + L"*.*", iAttr, sr)==0 ){ do{ if( !(iAttr & faDirectory) /*iAttr & faNormal*/ //&& !sr.Name.Pos(".log") //&& !sr.Name.Pos(".txt") //&& !sr.Name.Pos(".ico") //&& !sr.Name.Pos(".cnf") //&& !sr.Name.Pos("agcdstdt") && !sr.Name.Pos(".lst")){ pList->Add( sr.Name ); } } while( !Terminated && FindNext(sr)==0 ); FindClose(sr); } return !Terminated; } //--------------------------------------------------------------------------- String __fastcall TUpgradeThread::GetUpdateMD5File() { String locFile; m_UpdateUrl = GetUpdateURL(); if( m_UpdateUrl.Length()>0 ){ int ran = Random( 1000000 ); String url = m_UpdateUrl + MD5_FILE_NAME; url += "?" + IntToStr( ran ); int ret = DownloadFile( url, MD5_FILE_NAME ); if( ret==0 ){ locFile = m_AppPath + L"upgrade/" + MD5_FILE_NAME; } } return locFile; } //--------------------------------------------------------------------------- int __fastcall TUpgradeThread::GetServers( TStringList* pList ) { char szBuffer[2048] = ""; // bool bSave = false; FILE* fp = NULL; //读取文件配置 fp = fopen( ((AnsiString)m_GlobalFile).c_str(), "rb" ); if( fp ){ TGVFHEADER header; header.idSoftware = m_idSoft; int ret = fread( (void*)szBuffer, 1, sizeof(header), fp ); if( ret==sizeof(header) && !memcmp(szBuffer, header.head, sizeof(header.head)) ){ fseek( fp, 0, SEEK_END ); int size = ftell( fp ) - sizeof(header); if( size>2048 ) size = 2048; fseek( fp, sizeof(header), SEEK_SET ); fread( szBuffer, 1, size, fp ); char srvs[1024] = ""; char *tokenPtr=strtok(szBuffer,"\n"); while( tokenPtr ){ if( strstr(tokenPtr, "srv=") ){ strcpy( srvs, tokenPtr+4 ); break; } tokenPtr=strtok(NULL,"\n"); } if( strlen(srvs) ){ char *tkn = strtok( srvs, ";" ); while( tkn!=NULL ){ pList->Add( tkn ); tkn = strtok(NULL, ";"); } } } fclose( fp ); } // else{ // pList->Add( m_Server ); // } return 0; } //--------------------------------------------------------------------------- String __fastcall TUpgradeThread::GetUpdateURL() { String url; TStringList* pServers = new TStringList; GetServers( pServers ); for( int i=0; iCount; i++ ){ int sizeOut = 2048; char dataOut[2048]; TGetUrlParam param; param.idSoft = m_idSoft; param.sizeOut = sizeOut; param.dataOut = dataOut; strcpy( param.server, ((AnsiString)(*pServers)[i]).c_str() ); DWORD id; HANDLE hThread = CreateThread( NULL, 0, GetUrlProc, ¶m, 0, &id ); if( hThread ){ HANDLE aHandle[2] = { m_hSema, hThread }; DWORD dwRet = WaitForMultipleObjects( 2, aHandle, FALSE, 10000 ); if( param.hRequest ) InternetCloseHandle( param.hRequest ); if( param.hSession ) InternetCloseHandle( param.hSession ); WaitForSingleObject( hThread, 200 ); CloseHandle( hThread ); if( dwRet==WAIT_OBJECT_0 ) //用户退出 break; else if( dwRet==WAIT_OBJECT_0+1 && param.errCode==0 ){ //线程正常退出 if(dataOut[0]=='\"' && dataOut[param.sizeOut-1]=='\"') dataOut[param.sizeOut-1] = 0; char* p = strstr( dataOut, "url:" ); if( p ){ m_Server = (*pServers)[i]; char* str = p+4; url = L"http://" + m_Server + L"/" + str; break; } } } } delete pServers; return url; } //--------------------------------------------------------------------------- int __fastcall TUpgradeThread::GetUpdateFileList( String ufile, TStringList* ufnLst ) { int err = 0; String lfname = m_AppPath + MD5_FILE_NAME; FILE *ufp = fopen( ((AnsiString)ufile).c_str(), "rb" ), *lfp = fopen( ((AnsiString)lfname).c_str(), "rb" ); if( !ufp ){ err = -1; goto exit_line; } else if( !lfp ){ if( !MakeMD5File() || !(lfp=fopen( ((AnsiString)lfname).c_str(), "rb" )) ){ err = -2; goto exit_line; } } { char lbuf[4096] = "", ubuf[4096] = ""; fseek( lfp, 0, SEEK_END ); fseek( ufp, 0, SEEK_END ); int lsize = ftell( lfp ); int usize = ftell( ufp ); if(usize==0) goto exit_line; fseek( lfp, 0, SEEK_SET ); fseek( ufp, 0, SEEK_SET ); fread( lbuf, 1, lsize, lfp ); fread( ubuf, 1, usize, ufp ); fclose( lfp ); fclose( ufp ); lfp = ufp = NULL; TStringList *lfnLst = new TStringList, *lmdLst = new TStringList, *umdLst = new TStringList; char *tokenPtr=strtok(lbuf,"\n"); while( tokenPtr ){ char* pos = strstr( tokenPtr, "\t" ); if( pos ){ char buf[256]; memcpy( buf, tokenPtr, pos-tokenPtr ); buf[pos-tokenPtr] = 0; lfnLst->Add( buf ); lmdLst->Add( pos+1 ); } tokenPtr=strtok(NULL,"\n"); } tokenPtr=strtok(ubuf,"\n"); while( tokenPtr ){ char* pos = strstr( tokenPtr, "\t" ); if( pos ){ char buf[256]; memcpy( buf, tokenPtr, pos-tokenPtr ); buf[pos-tokenPtr] = 0; ufnLst->Add( buf ); umdLst->Add( pos+1 ); } tokenPtr=strtok(NULL,"\n"); } String strProg1, strProg2; int count = ufnLst->Count; for( int i=ufnLst->Count-1; i>=0; i-- ){ if( m_hWnd ){ strProg1 = L"文件数 " + IntToStr( count-i-1 ) + L"/" + IntToStr(count); SendMessage( m_hWnd, UM_SHOWTEXT, 1, (LPARAM)strProg1.c_str() ); int pos = (int)( (count-i-1)*100.0/count + 0.5 ); SendMessage( m_hWnd, UM_PROGRESS, 0, pos ); strProg2 = L"当前比对文件 " + (*ufnLst)[i]; SendMessage( m_hWnd, UM_SHOWTEXT, 2, (LPARAM)strProg2.c_str() ); } int count2 = lfnLst->Count; for( int j=lfnLst->Count-1; j>=0; j-- ){ if( m_hWnd ){ int pos = (int)( (count2-j)*100.0/count2 + 0.5 ); SendMessage( m_hWnd, UM_PROGRESS, 1, pos ); } // String ufn = (*ufnLst)[i], // lfn = (*lfnLst)[j]; if( !(*ufnLst)[i].CompareIC( (*lfnLst)[j] ) ){ // String umd = (*umdLst)[i], // lmd = (*lmdLst)[j]; if( !(*umdLst)[i].CompareIC( (*lmdLst)[j] ) ){ ufnLst->Delete( i ); umdLst->Delete( i ); } lfnLst->Delete( j ); lmdLst->Delete( j ); break; } } } delete lfnLst; delete lmdLst; delete umdLst; } exit_line: if( ufp ) fclose( ufp ); if( lfp ) fclose( lfp ); return err; } //--------------------------------------------------------------------------- int __fastcall TUpgradeThread::RegUpdateFlag( int iUpdate ) { FILE* fp = fopen( ((AnsiString)m_GlobalFile).c_str(), "r+b" ); if( fp ){ TGVFHEADER header; header.idSoftware = m_idSoft; header.iUpdate = iUpdate; fseek( fp, 0, SEEK_SET ); fwrite( (void*)&header, sizeof(header), 1, fp ); fclose( fp ); } return 0; } //--------------------------------------------------------------------------- int __fastcall TUpgradeThread::ClearUpdateDir() { TSearchRec sr; int iAttr = 0; String strPath = m_AppPath + L"upgrade\\"; if( FindFirst(strPath + L"*.*", iAttr, sr)==0 ){ do{ if( !(iAttr & faDirectory) ){ DeleteFile( strPath + sr.Name ); } } while( FindNext(sr)==0 ); FindClose(sr); } return 0; } //--------------------------------------------------------------------------- bool __fastcall TUpgradeThread::UpdateFiles() { bool bSuccess = true; TSearchRec sr; int iAttr = 0; String strPath = m_AppPath + L"upgrade\\"; //先关闭主程序 StopApp(); Sleep(1000); //等待app完全退出 if( FindFirst(strPath + L"*.*", iAttr, sr)==0 ){ do{ if( !(iAttr & faDirectory) && sr.Name.CompareIC(UPDATE_APP_NAME) ){ String fOld = strPath + sr.Name, fNew = m_AppPath + sr.Name; if( !MoveFileEx( fOld.c_str(), fNew.c_str(), MOVEFILE_REPLACE_EXISTING|MOVEFILE_WRITE_THROUGH ) ){ bSuccess = false; break; } } } while( FindNext(sr)==0 ); FindClose(sr); } return bSuccess; } //--------------------------------------------------------------------------- int __fastcall TUpgradeThread::RestartApp() { //启动主程序 HANDLE hToken = NULL, hTokenDup = NULL; if(OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hToken)){ if(DuplicateTokenEx(hToken, TOKEN_ALL_ACCESS,NULL, SecurityIdentification, TokenPrimary, &hTokenDup)){ STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si,sizeof(STARTUPINFO)); ZeroMemory(&pi,sizeof(PROCESS_INFORMATION)); si.cb = sizeof(STARTUPINFO); si.lpDesktop = NULL; si.lpReserved = NULL; si.lpTitle = NULL; si.cbReserved2 = NULL; si.lpReserved2 = NULL; si.dwFlags = STARTF_USESHOWWINDOW; si.wShowWindow = SW_SHOWNORMAL; String command = "\"" + m_AppPath + MAIN_APP_NAME + "\""; CreateProcessAsUser(hTokenDup,NULL,command.c_str(),NULL,NULL,FALSE,0,NULL,NULL,&si,&pi); } } return 0; } //--------------------------------------------------------------------------- bool __fastcall TUpgradeThread::StopApp() { HANDLE hSnapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS,0 ); //获取进程快照 if( hSnapshot ){ DWORD idProcess = 0; PROCESSENTRY32 pe32; pe32.dwSize = sizeof( PROCESSENTRY32 ); if(!Process32First( hSnapshot, &pe32 )){ //指向第一个进程 CloseHandle(hSnapshot); return false; } do{ if( lstrcmpi( pe32.szExeFile, MAIN_APP_NAME.c_str() )==0 ){ //查找进程 if( SameModulePath( pe32.th32ProcessID )){ idProcess = pe32.th32ProcessID; break; } } } while( Process32Next(hSnapshot, &pe32) ); // 循环直到取不到进程 if( idProcess ){ HANDLE hApp= OpenProcess( PROCESS_ALL_ACCESS, FALSE, idProcess ); //根据进程ID获取程序的句柄 if( hApp ){ if( WaitForSingleObject( hApp, 2000 )==WAIT_TIMEOUT )// 等待程序关闭2秒 TerminateProcess( hApp, 0 ); CloseHandle( hApp ); } } CloseHandle( hSnapshot ); } return true; } //--------------------------------------------------------------------------- #define PSAPI_VERSION 1 #include #pragma comment(lib,"psapi") bool __fastcall TUpgradeThread::SameModulePath(DWORD dwPID) { TCHAR sFilename[MAX_PATH]; HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID); if (hProcess == NULL) return false; bool bSame = false; HMODULE hModule; DWORD cbNeeded; if (EnumProcessModules(hProcess, &hModule, sizeof(hModule), &cbNeeded)){ if (GetModuleFileNameEx(hProcess, hModule, sFilename, MAX_PATH)){ if(wcslen(sFilename)>0){ String sFullName = TPath::GetFullPath(sFilename); if( !m_PathName.CompareIC( sFilename ) ) bSame = true; } } } else{ DWORD size = MAX_PATH; if (QueryFullProcessImageName(hProcess, 0, sFilename, &size)){ if( !m_PathName.CompareIC( sFilename ) ) bSame = true; } } CloseHandle(hProcess); return bSame; } //---------------------------------------------------------------------------