Silent Application Update
by Zuoliu Ding


Listing One
localhost/myupdate/
                   updateinfo.xml
                   supdate2.exe
                   sconfig.xml
                   image/
                         supdate.ico
                         maplewood.bmp


Listing Two
<!-- updateinfo.xml -->
<SilentUpdate>
  <supdate.exe>2.0.0.0|110592|http://localhost/myupdate/supdate2.exe
     </supdate.exe>
  <supdate.ico>1.2.0.0|1078|http://localhost/myupdate/image/supdate.ico
     </supdate.ico>
  <sconfig.xml>1.5.0.0|307|http://localhost/myupdate/sconfig.xml
     </sconfig.xml>
  <background.bmp>1.5.0.0|49206|http://localhost/myupdate/image/
                                      maplewood.bmp</background.bmp>
</SilentUpdate>


Listing Three
/*--------------------------------------------------------------
Function:    ThreadDownloadFiles
Description: Monitor new version and download files
Parameter:   p: CMainDlg object
Author:      Zuoliu Ding
--------------------------------------------------------------*/
DWORD WINAPI ThreadDownloadFiles(void *p)
{
   CMainDlg* pMainDlg = (CMainDlg*)p;
   if (pMainDlg->m_nUpdateStatus!=_DOWNLOAD_NOSTART) return 0;

   CString sInfo = StreamDownload(pMainDlg->m_sUpdateUrl);
   if (sInfo.IsEmpty()) Return(_DOWNLOAD_ERROR);

   CAppInfo AppInfo;
   if (!AppInfo.LoadUpdateXml(sInfo)) Return(_DOWNLOAD_ERROR);

   int nRet =_DOWNLOAD_CURRENT, nDownloadSize, nServerFileSize;
   CString sInfoName, sFile, sTmpReg;
   for (int i=0; i<APPINFO_COUNT; i++)
   {
      sInfoName = szFiles[i];
      if (AppInfo.IsFileNew(sInfoName))
      {
         sFile = GetModulePath() +AppInfo.GetFileName(sInfoName) +_NEW;
         nDownloadSize = FileDownload(AppInfo.GetDLUrl(sInfoName), sFile);
         nServerFileSize = AppInfo.GetSize(sInfoName);
         if (nDownloadSize == nServerFileSize)
         {   
            // e.g.: 2.0.0.0|bluesky.bmp
            sTmpReg = AppInfo.GetVersion(sInfoName) +"|"
                      +AppInfo.GetFileName(sInfoName);
            SaveRegKey(sInfoName +_NEW, sTmpReg);
            nRet =_DOWNLOAD_SUCCESS;
         }
         else DeleteFile(sFile);
      }
   }
   Return(nRet);
}

/*--------------------------------------------------------------
Function:    OnMouseMove - member function, meggage handler
Description: Check update once in an application session
--------------------------------------------------------------*/
LRESULT CMainDlg::OnMouseMove(UINT, WPARAM, LPARAM, BOOL&)
{
   if (m_nUpdateStatus ==_DOWNLOAD_NOSTART &&
       !m_sUpdateUrl.IsEmpty())
   {
      DWORD dwThreadId;
      HANDLE h = ::CreateThread(NULL, 0, ThreadDownloadFiles,
                                (LPVOID)this, 0, &dwThreadId);
      // for 5 minutes
      if (WaitForSingleObject(h, 300000) == WAIT_OBJECT_0)
      {
         if (m_nUpdateStatus ==_DOWNLOAD_SUCCESS)
         {
            SetDlgItemText(IDC_STATIC_TEXT,
            "Some New Update will be Available in Next Session...");
         }
      }   
      else m_nUpdateStatus =_DOWNLOAD_ERROR;
 
      CloseHandle(h);
   }

   return 0;
}


Listing Four
/*--------------------------------------------------------------
supdate.cpp: Main source file for supdate.exe
Author:      Zuoliu Ding
--------------------------------------------------------------*/
#include "stdafx.h"
#include "hlpfuncs.h"

BOOL IsExeFileUpdated()
{
   CString sInfoName, sFile;
   BOOL bRet = FALSE;
   CString sNewVersion = ReadRegKey(_EXE_FILE +_NEW);
   int nPos = sNewVersion.Find('|'); //e.g. 2.0.0.0|bluesky.bmp

   if (!sNewVersion.IsEmpty() && -1 !=nPos)
   {
      sFile = GetModulePath() +sNewVersion.Mid(nPos+1);
      sNewVersion = sNewVersion.Left(nPos);
      WIN32_FIND_DATA FindFileData;
      HANDLE hFindFile = FindFirstFile(sFile +_NEW, &FindFileData);

      if (hFindFile != INVALID_HANDLE_VALUE)
      {
         CString sFileOld = sFile +_OLD;
         DeleteFile(sFileOld);      // if any

         // 95/98/ME: MoveFile/MoveFileEx here is Unsupported.
         if (MoveFileEx(sFile, sFileOld, NULL))
         {  
            CopyFile(sFile +_NEW, sFile, FALSE);
            DeleteFile(sFile +_NEW);   
            SaveRegKey(_EXE_FILE, sNewVersion);
            DeleteName(_EXE_FILE +_NEW);
            bRet = TRUE;
         }
      }

      if (hFindFile != INVALID_HANDLE_VALUE)
         CloseHandle(hFindFile);
   }

   // Simply copy other files
   for (int i=1; i<APPINFO_COUNT; i++)
   {
      sInfoName = szFiles[i];
      sNewVersion = ReadRegKey(sInfoName +_NEW);
      int nPos = sNewVersion.Find('|');
      if (!sNewVersion.IsEmpty() && -1 !=nPos)
      {
         sFile = GetModulePath() +sNewVersion.Mid(nPos+1);
         sNewVersion = sNewVersion.Left(nPos);
         CopyFile(sFile +_NEW, sFile, FALSE);
         DeleteFile(sFile +_NEW);   
         SaveRegKey(sInfoName, sNewVersion);
         DeleteName(sInfoName +_NEW);
      }
   }

   return bRet;
}

// Main Module and WinMain
CAppModule _Module;
static const GUID libId =
{0x1980abfa,0xaf4f,0x45e8,{0xb7,0xbc,0xf1,0x9e,0x63,0x6e,0x26,0x8e}};

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPreInst,
                     LPTSTR lpstrCmdLine, int nCmdShow)
{
   HRESULT hRes = ::CoInitialize(NULL);
   ATLASSERT(SUCCEEDED(hRes));
   ::DefWindowProc(NULL, 0, 0, 0L);
   AtlInitCommonControls(ICC_BAR_CLASSES);   
   hRes = _Module.Init(NULL, hInstance, &libId);
   ATLASSERT(SUCCEEDED(hRes));
   AtlAxWinInit();
   int nRet = 0;

   HWND hWnd = ::FindWindow(NULL, _APP_TITLE);
   if (hWnd)
   {
       if (IsIconic(hWnd))
         ShowWindow(hWnd, SW_RESTORE);
       else
         SetForegroundWindow(hWnd);
   }
   else
   {
      if (IsExeFileUpdated())
         ShellExecute(NULL, "open", GetModulePath() +_EXE_FILE,
                      NULL, NULL, SW_SHOWNORMAL);
      else
         nRet = Run(lpstrCmdLine, nCmdShow);
   }

   _Module.Term();
   ::CoUninitialize();
   return nRet;
}


Listing Five
HKEY_LOCAL_MACHINE\
  SOFTWARE\
    Microsoft\
      Windows\
        CurrentVersion\
          RunOnce\
            sUpdate c:\...\sUpdate.bat







