65.9K
CodeProject 正在变化。 阅读更多。
Home

自动更新技巧

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (36投票s)

2005年2月11日

4分钟阅读

viewsIcon

148084

downloadIcon

5901

本文提供了一个简单的自动更新类,可以添加到任何应用程序中。

Sample Image

引言

我所在的公司 AutoUpdate+ 创建了用于管理基于 Win32 的软件更新的软件,这对于需要此类解决方案的中型到大型用户来说非常棒,但对于小型开发人员来说有点过剩。 许多小型开发人员只需要一个简单的更新解决方案,并且通常只需要管理主项目可执行文件的更新。 本文将介绍我创建的一个自动更新类,它非常容易添加到项目中,并且运行一个小技巧来更新主项目可执行文件。

使用代码

设计了一个名为 CAutoUpdater 的类,其目标是易于添加到任何应用程序,完全管理更新过程并正确处理任何错误。 要使用此类,只需调用 CheckForUpdate 方法,传入更新服务器的 URL 即可。

void CMFCUpdaterDlg::OnUpdatecheck() 
{
    CAutoUpdater updater;
    if (CAutoUpdater::Success == 
        updater.CheckForUpdate("http://AutoUpdatePlus.com/test/"))
    {
        // ... Notify user of success
    }
}

更新过程包括以下步骤

  1. 确保有可用的互联网连接。
  2. 从更新服务器检索配置文件。
  3. 检查配置文件中的版本。 与现有的应用程序版本进行比较。
  4. 如果需要更新,则从更新服务器下载新的应用程序。
  5. 使用现有版本切换最新的应用程序版本。

第一个,也是经常被忽视的步骤,是确保有可用的互联网连接。 这样做至关重要,因为否则您可能会触发自动拨号上网,这很可能会让您的应用程序的大多数用户感到不高兴。

// Important step - ensure we have an internet
// connection. We don't want to force a dial-up.
DWORD dwType;
if (!InternetGetConnectedState(&dwType, 0))
{
    return false;
}

使用 InternetOpenUrl 从 HTTP 服务器检索文件是一项简单的任务,但如果提供的 URL 未被规范化,则可能会失败。 这仅仅意味着必须将坏字符(如空格)转换为转义字符,这使用 InternetCanonicalizeUrl 完成。 GetSession 方法管理此过程,为给定 URL 返回 HTTP 服务器上文件的句柄。 要检查更新,我们必须在服务器上定义一个更新配置文件,它让我们知道要期望哪个应用程序版本。 在我们的例子中,更新配置文件名为update.txt,包含我们的应用程序更新的版本。 您可以在 这里 看到它。 在获得更新配置文件的会话句柄后,我们可以使用 DownloadConfig 方法将数据检索到缓冲区中。 现在我们有了可供我们使用的更新版本。

HINTERNET CAutoUpdater::GetSession(CString &URL)
{
    // Canonicalization of the URL converts
    // unsafe characters into escape character equivalents
    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 CAutoUpdater::DownloadConfig(HINTERNET hSession, 
                           BYTE *pBuf, DWORD bufSize)
{
    DWORD    dwReadSizeOut;
    InternetReadFile(hSession, pBuf, 
                     bufSize, &dwReadSizeOut);
    if (dwReadSizeOut <= 0)
    {
        return false;
    }

    return true;
}

此时,我们已经检索了更新配置文件,现在必须确定是否需要更新。 更新配置文件中的文本是一个简单的版本标签,在我们的例子中,更新版本为2.0.0.1。 这必须与我们当前正在运行的可执行文件的版本进行比较。 令人惊讶的是,从可执行文件中获取版本号并运行版本号比较并不是一项简单的任务,并且可能会变得有点混乱。 如果事实证明我们现有的应用程序版本已过时,我们应该通知用户,然后继续从更新服务器下载最新的应用程序可执行文件。 请注意,我们的新可执行文件将下载到由 GetTempPath 提供的临时用户目录中。

下载最新可执行版本后,必须以某种方式将其切换以替换现有的过时的应用程序可执行文件。 但是,存在一个问题! 当前的可执行文件被锁定,因为它正在使用中,因此无法复制。 尽管如此,确实存在一个简单的 Windows 技巧。 正在使用的文件仍然可以重命名,为更新可执行文件切换到应用程序目录留出空间。 请注意,此更新技巧可能并非总是可用,因为某些文件甚至可能被锁定而无法重命名。 在这种情况下,可以使用带有 MOVE_DELAY_UNTIL_REBOOT 选项的 MoveFileEx,它会将重命名延迟到重启发生以释放文件。

bool CAutoUpdater::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;
}

关注点

提供的类管理一个简单的应用程序的更新,但有一些缺点

  • 更新检查是一个同步任务,它不会向用户提供有关其进度的任何反馈。
  • 在应用程序内进行更新检查会限制其功能。 最好让更新检查从外部可执行文件运行。
  • 一个看似微不足道的程序很快变得复杂起来。 现在的检查无法管理复杂的情况,例如多个文件更新。

历史

  • 2005 年 2 月 10 日 - 首次发布。

关于

提供的源代码显示了一个非常简单的更新机制。 对于更完善的解决方案,请查看位于 www.AutoUpdatePlus.com 的产品 AutoUpdate+

© . All rights reserved.