#if !defined(ONLINE_UPDATER) #define ONLINE_UPDATER #if _MSC_VER > 1000 #pragma once #endif #include <Wininet.h> #define LOCATION_UPDATE_FILE_CHECK _T("update.txt") class OnlineUpdater { public: OnlineUpdater(); virtual ~OnlineUpdater(); enum ErrorType { Success, InternetConnectFailure, InternetSessionFailure, ConfigDownloadFailure, FileDownloadFailure, NoExecutableVersion, UpdateNotRequired, UpdateNotComplete }; ErrorType CheckForUpdate(LPCTSTR UpdateServerURL); HINTERNET GetSession(CString &URL); bool InternetOkay(); bool DownloadConfig(HINTERNET hSession, BYTE *pBuf, DWORD bufSize); bool DownloadFile(HINTERNET hSession, LPCTSTR localFile); CString GetFileVersion(LPCTSTR file); int CompareVersions(CString ver1, CString ver2); bool IsDigits(CString text); CString GetExecutable(); bool Switch(CString executable, CString update, bool WaitForReboot); private: HINTERNET hInternet; }; #endif // !defined(ONLINE_UPDATER) #include "stdafx.h" #include "OnlineUpdater.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #define TRANSFER_SIZE 4096 OnlineUpdater::OnlineUpdater() { hInternet = InternetOpen("AutoUpdateAgent", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); } OnlineUpdater::~OnlineUpdater() { if (hInternet) { InternetCloseHandle(hInternet); } } OnlineUpdater::ErrorType OnlineUpdater::CheckForUpdate(LPCTSTR UpdateServerURL) { if (!InternetOkay()) { return InternetConnectFailure; } bool bTransferSuccess = false; CString URL = UpdateServerURL + CString(LOCATION_UPDATE_FILE_CHECK); HINTERNET hSession = GetSession(URL); if (!hSession) { return InternetSessionFailure; } BYTE pBuf[TRANSFER_SIZE]; memset(pBuf, NULL, sizeof(pBuf)); bTransferSuccess = DownloadConfig(hSession, pBuf, TRANSFER_SIZE); InternetCloseHandle(hSession); if (!bTransferSuccess) { return ConfigDownloadFailure; } CString executable = GetExecutable(); CString fileVersion = GetFileVersion(executable); if (fileVersion.IsEmpty()) { return NoExecutableVersion; } CString updateVersion = (char *) pBuf; if (CompareVersions(updateVersion, fileVersion) != 1) { return UpdateNotRequired; } TCHAR path[MAX_PATH]; GetTempPath(MAX_PATH, path); CString exeName = executable.Mid(1 + executable.ReverseFind(_T('\\'))); CString directory = path; URL = UpdateServerURL + exeName; hSession = GetSession(URL); if (!hSession) { return InternetSessionFailure; } CString msg; msg.Format(_T("An update of %s is now available. Proceed with the update?"), exeName); if (IDNO == MessageBox(GetActiveWindow(), msg, _T("Update is available"), MB_YESNO | MB_ICONQUESTION)) { return UpdateNotComplete; } CString updateFileLocation = directory + exeName; bTransferSuccess = DownloadFile(hSession, updateFileLocation); InternetCloseHandle(hSession); if (!bTransferSuccess) { return FileDownloadFailure; } if (!Switch(executable, updateFileLocation, false)) { return UpdateNotComplete; } return Success; } bool OnlineUpdater::InternetOkay() { if (hInternet == NULL) { return false; } DWORD dwType; if (!InternetGetConnectedState(&dwType, 0)) { return false; } return true; } HINTERNET OnlineUpdater::GetSession(CString &URL) { TCHAR canonicalURL[1024]; DWORD nSize = 1024; InternetCanonicalizeUrl(URL, canonicalURL, &nSize, ICU_BROWSER_MODE); DWORD options = INTERNET_FLAG_NEED_FILE | INTERNET_FLAG_HYPERLINK | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD; HINTERNET hSession = InternetOpenUrl(hInternet, canonicalURL, NULL, NULL, options, 0); URL = canonicalURL; return hSession; } bool OnlineUpdater::DownloadConfig(HINTERNET hSession, BYTE *pBuf, DWORD bufSize) { DWORD dwReadSizeOut; InternetReadFile(hSession, pBuf, bufSize, &dwReadSizeOut); if (dwReadSizeOut <= 0) { return false; } return true; } bool OnlineUpdater::DownloadFile(HINTERNET hSession, LPCTSTR localFile) { HANDLE hFile; BYTE pBuf[TRANSFER_SIZE]; DWORD dwReadSizeOut, dwTotalReadSize = 0; hFile = CreateFile(localFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) return false; do { DWORD dwWriteSize, dwNumWritten; BOOL bRead = InternetReadFile(hSession, pBuf, TRANSFER_SIZE, &dwReadSizeOut); dwWriteSize = dwReadSizeOut; if (bRead && dwReadSizeOut > 0) { dwTotalReadSize += dwReadSizeOut; WriteFile(hFile, pBuf, dwWriteSize, &dwNumWritten, NULL); if (dwWriteSize != dwNumWritten) { CloseHandle(hFile); return false; } } else { if (!bRead) { CloseHandle(hFile); return false; } break; } } while (1); CloseHandle(hFile); return true; } CString OnlineUpdater::GetFileVersion(LPCTSTR file) { CString version; VS_FIXEDFILEINFO *pVerInfo = NULL; DWORD dwTemp, dwSize, dwHandle = 0; BYTE *pData = NULL; UINT uLen; try { dwSize = GetFileVersionInfoSize((LPTSTR) file, &dwTemp); if (dwSize == 0) throw 1; pData = new BYTE[dwSize]; if (pData == NULL) throw 1; if (!GetFileVersionInfo((LPTSTR) file, dwHandle, dwSize, pData)) throw 1; if (!VerQueryValue(pData, _T("\\"), (void **) &pVerInfo, &uLen)) throw 1; DWORD verMS = pVerInfo->dwFileVersionMS; DWORD verLS = pVerInfo->dwFileVersionLS; int ver[4]; ver[0] = HIWORD(verMS); ver[1] = LOWORD(verMS); ver[2] = HIWORD(verLS); ver[3] = LOWORD(verLS); // Are lo-words used? if (ver[2] != 0 || ver[3] != 0) { version.Format(_T("%d.%d.%d.%d"), ver[0], ver[1], ver[2], ver[3]); } else if (ver[0] != 0 || ver[1] != 0) { version.Format(_T("%d.%d"), ver[0], ver[1]); } delete pData; return version; } catch (...) { return _T(""); } } int OnlineUpdater::CompareVersions(CString ver1, CString ver2) { int wVer1[4], wVer2[4]; int i; TCHAR *pVer1 = ver1.GetBuffer(256); TCHAR *pVer2 = ver2.GetBuffer(256); for (i = 0; i < 4; i++) { wVer1[i] = 0; wVer2[i] = 0; } TCHAR *pToken = strtok(pVer1, _T(".")); if (pToken == NULL) { return -21; } i = 3; while (pToken != NULL) { if (i < 0 || !IsDigits(pToken)) { return -21; } wVer1[i] = atoi(pToken); pToken = strtok(NULL, _T(".")); i--; } ver1.ReleaseBuffer(); pToken = strtok(pVer2, _T(".")); if (pToken == NULL) { return -22; } i = 3; while (pToken != NULL) { if (i < 0 || !IsDigits(pToken)) { return -22; } wVer2[i] = atoi(pToken); pToken = strtok(NULL, _T(".")); i--; } ver2.ReleaseBuffer(); for (i = 3; i >= 0; i--) { if (wVer1[i] > wVer2[i]) { return 1; } else if (wVer1[i] < wVer2[i]) { return -1; } } return 0; } bool OnlineUpdater::IsDigits(CString text) { for (int i = 0; i < text.GetLength(); i++) { TCHAR c = text.GetAt(i); if (c >= _T('0') && c <= _T('9')) { } else { return false; } } return true; } CString OnlineUpdater::GetExecutable() { HMODULE hModule = ::GetModuleHandle(NULL); ASSERT(hModule != 0); TCHAR path[MAX_PATH]; VERIFY(::GetModuleFileName(hModule, path, MAX_PATH)); return path; } bool OnlineUpdater::Switch(CString executable, CString update, bool WaitForReboot) { int type = (WaitForReboot) ? MOVEFILE_DELAY_UNTIL_REBOOT : MOVEFILE_COPY_ALLOWED; const TCHAR *backup = _T("OldExecutable.bak"); CString directory = executable.Left(executable.ReverseFind(_T('\\'))); CString backupFile = directory + _T('\\') + CString(backup); DeleteFile(backupFile); if (!MoveFileEx(executable, backupFile, type)) { return false; } BOOL bMoveOK = (MoveFileEx(update, executable, type) == TRUE); int i = GetLastError(); return bMoveOK; }