Click here to Skip to main content
15,038,237 members
Articles / Desktop Programming / Win32
Posted 11 Sep 2017


36 bookmarked

An efficient way for automatic updating

Rate me:
Please Sign up or sign in to vote.
5.00/5 (20 votes)
11 Sep 2017CPOL7 min read
A simple way to provide silent automatic updates with no server side code

 CodeProject Best C++ Article of September 2017 First Prize.


Many software developers need to update their software and apply such update to all current users. This article provides a method we developed which allows a fully transparant automatic updates with no action needed from the user (such as start the new version, etc.). It is also unique because it doesn't reuqire any server side code. 

The Problems

I have looked for a way to have our software download updates only when the version  on our web site is newer, and yet, to avoid having to run a server side component. Our goal was to have our AutoUpdate class determine if the version on the web site is indeed newer without downloading it first. We also wanted a clean and smooth solution which won't require installing a new version but will cause a product (i.e software.exe) to replace itself with a new version (also software.exe) transparantly. I learned that there are quite a few steps that has to be taken and scenarios to be addressed but the result is a solid solution and covers all scenarios. 

The Building Blocks

Obtaining the version of a given executable. We use GetFileVersionInfo() to obtain the information. We have developed our own function to convert an executable path into our own version information structure which is:

typedef struct
    int Major;
    int Minor;
    int Revision;
    int SubRevision;
} SG_Version;

so SG_GetVersion goes as follow:

BOOL AutoUpdate::SG_GetVersion(LPWSTR ExeFile, SG_Version *ver)
    BOOL result = FALSE;
    DWORD dwDummy;
    DWORD dwFVISize = GetFileVersionInfoSize(ExeFile, &dwDummy);
    LPBYTE lpVersionInfo = new BYTE[dwFVISize];
    GetFileVersionInfo(ExeFile, 0, dwFVISize, lpVersionInfo);
    UINT uLen;
    VerQueryValue(lpVersionInfo, _T("\\"), (LPVOID *)&lpFfi, &uLen);
    if (lpFfi && uLen)
        DWORD dwFileVersionMS = lpFfi->dwFileVersionMS;
        DWORD dwFileVersionLS = lpFfi->dwFileVersionLS;
        delete[] lpVersionInfo;
        ver->Major = HIWORD(dwFileVersionMS);
        ver->Minor = LOWORD(dwFileVersionMS);
        ver->Revision = HIWORD(dwFileVersionLS);
        ver->SubRevision = LOWORD(dwFileVersionLS);
        result = TRUE;
    return result;

Adding the next version to the file name. Next we need a function that will generate the file name which will contain the next version. For example, if our software is "program.exe" and the next version is, then this function will generate the following name and store it in our class's member variable m_NextVersion "program.".

void AutoUpdate::AddNextVersionToFileName(CString& ExeFile, SG_Version ver)
    CString strVer;
    ver.SubRevision += 1;    // For the time being we just promote the subrevision in one but of course
                            // we should build a mechanism to promote the major, minor and revision
    ExeFile = GetSelfFullPath();
    ExeFile = ExeFile.Left(ExeFile.GetLength() - 4);
    ExeFile += L"."+strVer;
    ExeFile += L".exe";
    m_NextVersion = ExeFile;

Downloading the newer version from the web site

After examining several ways to download a file from a URL, here is the best method we have found. Please note that we are using DeleteUrlCacheEntry(). If your software is looking for updates every 2 hours, and only after 6 hours, an update is placed, unless you delete the cache, your browser might not download the newer file as it will have the name of an existing file. That is also important during the QA phase when you try different scenarios.

DeleteUrlCacheEntry(URL); // we need to delete the cache so we always download the real file
HRESULT hr = 0;
hr = URLDownloadToFile(
    NULL,   // A pointer to the controlling IUnknown interface (not needed here)
    ExeName,0,              // Reserved. Must be set to 0.
    &pCallback);           //  A callback function is used to ensure asynchronous download

Then the callback function goes like that:

We define a class named MyCallback. Before calling URLDownloadToFile() we define a local member of this function:

MyCallback pCallback;

The class is define as follow:

using namespace std;

class MyCallback : public IBindStatusCallback
    MyCallback() {}

    ~MyCallback() { }

    // This one is called by URLDownloadToFile
    STDMETHOD(OnProgress)(/* [in] */ ULONG ulProgress, /* [in] */ ULONG ulProgressMax, /* [in] */ ULONG ulStatusCode, /* [in] */ LPCWSTR wszStatusText)
        // You can use your own logging function here
        // Log(L"Downloaded %d of %d. Status code", ulProgress, ulProgressMax, ulStatusCode);
        return S_OK;

    STDMETHOD(OnStartBinding)(/* [in] */ DWORD dwReserved, /* [in] */ IBinding __RPC_FAR *pib)
        return E_NOTIMPL;

    STDMETHOD(GetPriority)(/* [out] */ LONG __RPC_FAR *pnPriority)
        return E_NOTIMPL;

    STDMETHOD(OnLowResource)(/* [in] */ DWORD reserved)
        return E_NOTIMPL;

    STDMETHOD(OnStopBinding)(/* [in] */ HRESULT hresult, /* [unique][in] */ LPCWSTR szError)
        return E_NOTIMPL;

    STDMETHOD(GetBindInfo)(/* [out] */ DWORD __RPC_FAR *grfBINDF, /* [unique][out][in] */ BINDINFO __RPC_FAR *pbindinfo)
        return E_NOTIMPL;

    STDMETHOD(OnDataAvailable)(/* [in] */ DWORD grfBSCF, /* [in] */ DWORD dwSize, /* [in] */ FORMATETC __RPC_FAR *pformatetc, /* [in] */ STGMEDIUM __RPC_FAR *pstgmed)
        return E_NOTIMPL;

    STDMETHOD(OnObjectAvailable)(/* [in] */ REFIID riid, /* [iid_is][in] */ IUnknown __RPC_FAR *punk)
        return E_NOTIMPL;

    // IUnknown stuff
        return 0;

    STDMETHOD_(ULONG, Release)()
        return 0;

    STDMETHOD(QueryInterface)(/* [in] */ REFIID riid, /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
        return E_NOTIMPL;

The SG_Run function

The SG_Run function is the optimal way we have found to start a new process. There are various ways such as ShellExecute() but that would be the most efficient one:

    wprintf(L"Called SG_Run '%s'", FileName);
    PROCESS_INFORMATION ProcessInfo; //This is what we get as an [out] parameter

    STARTUPINFO StartupInfo; //This is an [in] parameter

    ZeroMemory(&StartupInfo, sizeof(StartupInfo));
    StartupInfo.cb = sizeof StartupInfo; //Only compulsory field

    if (CreateProcess(FileName, NULL,
        NULL, NULL, FALSE, 0, NULL,
        NULL, &StartupInfo, &ProcessInfo))
        //WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
        return TRUE;
        return FALSE;


The Solution

Before you start coding, remember to make sure your project has a version string. To add a version string, right click the project name in the Solution Explorer and select Add -> Resource. Image 1Image 2

The flow

The solution can be described with the following steps:

Given that the current version of a product (software.exe) is, we would like it to download the replace itself with a newer version if this version is We name version of software.exe software. We place this version in our web site where it can be downloaded freely with no need to interact with the server or to log in. For example:
  1. Software.exe should faetch its own version and periodically try to download its next version if that is possible. If there is no newer version nothing will be downloaded.
  2. When there is a newer version it will be downloaded to a temporary file (for example _software.exe). 
  3. _software.exe will be examined to assure that its version string matches the original file name on the server, (i.e., if not, it will be deleted and ignored.
  4. software.exe will now start _software.exe as a new process.
  5. software.exe quits.
  6. _software.exe copies itself to software.exe, which is possible since software.exe quitted.
  7. _software.exe starts software.exe as a new process.
  8. _sotware.exe quits.
  9. software.exe deletes _software.exe.


We need to link our software with the following libraries:

urlmon.lib and version.lib.

Remember to set the Language of your code snippet using the Language dropdown.

Use the "var" button to to wrap Variable or class names in <code> tags like this.

The Magic

I have placed an identical copy of this program in my server, with one change: the version string in the code that is included in this article is and the one on my server is If you download the source code and compile it, you will get SG_AutoUpdate.exe. If you run it, it should automatically update itself within seconds.

The original executable looks like this:

Image 3

After running it, you will see the same file but in fact it is a different one. Its the newer version:

Image 4

You can of course use your own web site. 

Right now, the download link is composed using this constant:

CString m_DownloadLink = _T("");

AutoUpdate.h line 43.

You can replace it to any other address. Then here are the instructions for testing this mechanism: 

  1. Build the source code and keep SG_AutoUpdate.exe somewhere (or rename it temporarily).
  2. Go to the project's resources, find the version string, and change it to
  3. Build the source code again.
  4. You will get the more recent version under the name SG_AutoUpdate.exe. Upload it to your server and rename it SG_AutoUpdate.
  5. All names are case sensitive.
  6. Now, go to the backup you have made, delete the file you have just uploaded and rename the backup back to SG_AutoUpdate.exe. Delete all other files. You should have one file named SG_AutoUpdate.exe and if you hoover the mouse over it, you should see the "BEFORE" image, maning the version should show:
  7. Double click it and after a few seconds you will find out it was updated to You can check the version string to verify that. 

But let's make sure the new version isn't downloaded again and won't cause endless "false positive" updates.

Now run the current SG_AutoUpdate.exe again. You will find that it reports that there isn't an update in the server. We are done.

I made a small video showing the entire process. Please take a close look at the "Program Version" column in the File Explorer.


Update (Supports UAC)

Following a questions by David Zaccanti, this method of automatic updating supports applications that require elevation, i.e. prompting the user to approve Administration Privileges. I have updated the source code and the new version on the server (which the program updates to) so they both require Administration privileges. When you compile the source code and running version it will display the UAC Prompt, however after silently updating to version, no elevation prompt will appear, as an elevated process can start another elevated process using CreateProcess() with no elevation prompt.

Left to be done...

There are several enhancements which aren't part of this code but can be developed:

- Adding a Log() function to allow you to redirect all "wprintf" calls into one log file shared by all variants of the program during the auto update process. 

- Allowing automatic update to a version that comes after the next one, for example going from to 

- Adding an automatic version updater to the post-build section of the project, which will allow an automatic promotion of the current version.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Michael Haephrati
CEO Secured Globe, Inc.
United States United States
Michael Haephrati, Musician and CEO and co-founder of Secured Globe, Inc. Worked on many ventures starting from HarmonySoft, designing Rashumon, the first Graphical Multi-lingual word processor for Amiga computer. During 1995-1996 he worked as a Contractor with Apple at Cupertino.

You can also Buy me coffee.

Comments and Discussions

Questionhow about updating not just exe file, include dlls and more ? Pin
Member 143605199-Nov-19 15:06
MemberMember 143605199-Nov-19 15:06 
QuestionSelf renaming reduces two SG_Run calls to one call Pin
ehaerim25-Mar-19 13:42
Memberehaerim25-Mar-19 13:42 
QuestionBug scenario found when result2 = CopyFile(m_SelfFileName, m_SelfFileName.Mid(3), FALSE); returns FALSE Pin
ehaerim23-Mar-19 23:30
Memberehaerim23-Mar-19 23:30 
Questionexit is more graceful than _exit Pin
ehaerim22-Mar-19 21:28
Memberehaerim22-Mar-19 21:28 
QuestionWill this method work when multiple instances running at the same time? Pin
ehaerim21-Mar-19 21:44
Memberehaerim21-Mar-19 21:44 
QuestionDo we really need to try to DeleteFile the normal or temp version 5 times, not just once and for all? Pin
ehaerim21-Mar-19 21:42
Memberehaerim21-Mar-19 21:42 
Questiontwo non-critical bugs Pin
ehaerim21-Mar-19 7:44
Memberehaerim21-Mar-19 7:44 
AnswerRe: two non-critical bugs Pin
Michael Haephrati21-Mar-19 17:12
professionalMichael Haephrati21-Mar-19 17:12 
PraiseThanks you Pin
Huzifa Terkawi26-Oct-17 14:33
MemberHuzifa Terkawi26-Oct-17 14:33 
AnswerRe: Thanks you Pin
Michael Haephrati30-Oct-17 6:36
professionalMichael Haephrati30-Oct-17 6:36 
QuestionNot so efficient Pin
Theo Buys21-Sep-17 22:15
MemberTheo Buys21-Sep-17 22:15 
AnswerRe: Not so efficient Pin
Michael Haephrati21-Sep-17 7:56
professionalMichael Haephrati21-Sep-17 7:56 
GeneralRe: Not so efficient Pin
Theo Buys21-Sep-17 20:55
MemberTheo Buys21-Sep-17 20:55 
AnswerRe: Not so efficient Pin
Michael Haephrati22-Sep-17 7:39
professionalMichael Haephrati22-Sep-17 7:39 
QuestionNice solution! Pin
Theo Buys18-Sep-17 23:43
MemberTheo Buys18-Sep-17 23:43 
AnswerRe: Nice solution! Pin
Michael Haephrati19-Sep-17 0:16
professionalMichael Haephrati19-Sep-17 0:16 
GeneralRe: Nice solution! Pin
Theo Buys19-Sep-17 0:55
MemberTheo Buys19-Sep-17 0:55 
GeneralRe: Nice solution! Pin
Michael Haephrati19-Sep-17 0:59
professionalMichael Haephrati19-Sep-17 0:59 
GeneralRe: Nice solution! Pin
Theo Buys19-Sep-17 1:26
MemberTheo Buys19-Sep-17 1:26 
PraiseRe: Nice solution! Pin
Michael Haephrati19-Sep-17 1:33
professionalMichael Haephrati19-Sep-17 1:33 
GeneralRe: Nice solution! Pin
Theo Buys19-Sep-17 1:55
MemberTheo Buys19-Sep-17 1:55 
GeneralRe: Nice solution! Pin
Michael Haephrati19-Sep-17 2:39
professionalMichael Haephrati19-Sep-17 2:39 
GeneralRe: Nice solution! Pin
Theo Buys19-Sep-17 3:08
MemberTheo Buys19-Sep-17 3:08 
GeneralRe: Nice solution! Pin
Michael Haephrati19-Sep-17 6:53
professionalMichael Haephrati19-Sep-17 6:53 
GeneralRe: Nice solution! Pin
Theo Buys20-Sep-17 22:54
MemberTheo Buys20-Sep-17 22:54 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.