技术开发 频道

在Visual Studio 2010中实现灾难恢复

  【IT168 专稿】程序员:“人世间最痛苦的事情,莫过于加班;比加班更痛苦的,莫过于天天加班;比天天加班更痛苦的,莫过于天天加班却没有加班费!”

  软件用户:“人世间最痛苦的事情,莫过于软件崩溃;比软件崩溃更痛苦的,莫过于软件经常崩溃;比软件经常崩溃更痛苦的,莫过于软件经常崩溃却无法进行灾难恢复!” 

  第一篇:Visual Studio 2010 下一个Visual Studio 6.0

  第二篇:Visual Studio 2010 的自定义开始页

  第三篇:Visual Studio 2010中的多显示器支持

  第四篇:Visual Studio 2010中的调用继承树

  第五篇:C# 4.0中的动态类型和动态编程

  第六篇:使用Visual Studio 2010特性支持TDD

  第七篇:Visual Studio 2010特性支持Office

  第八篇:Visual Studio 2010中的Quick Search

  第十篇:Visual Studio 2010中的C++ IDE增强

  第十一篇:Visual C++ 2010创建Ribbon界面(上)

  第十二篇:Visual C++ 2010创建Ribbon界面(下)

  第十三篇:Visual Studio 2010与VS2008全面比较

  作为软件的最终用户,在使用各种各样的软件的时候,特别是在使用软件进行工作的时候,最害怕的事情就是软件突然崩溃,自己的工作成果化为乌有。

  为了提高用户体验,Microsoft在Windows Vista系统中首先引入了重启管理器(Restart Manager)。它可以帮助应用程序维护其当前运行状态,当软件更新后需要重新启动,或者是遇到非常严重的问题崩溃后,可以重新启动软件并且恢复到软件的当前工作状态。更重要的是,它还可以恢复自动保存的软件数据状态,尽量保证用户数据的安全。有了重启管理器,软件就可以很快地从灾难中恢复过来,实现快速“灾后重建”。



图1 Visual Studio 2010的重启管理


  重启管理器主要应用在下面两个方面:

  •软件更新
  很多时候,软件或者操作系统升级后,需要重新启动才可以生效。在这种情况下,我们就可以使用重启管理器自动关闭真正运行的软件,然后进行更新,更新完成后自动重新启动软件,并且恢复到软件当前的工作状态。这将使得软件的更新更加流畅和智能。

  •软件灾难恢复
  当软件遇到严重错误,进程崩溃的时候,可以使用重启管理器重新启动软件,恢复软件自动保持的数据,让软件可以快速地从灾难中恢复过来。

  为了支持重启管理器,微软提供了一套Restart Manassas API函数来完成这些工作。这些函数定义在<restartmanager.h>头文件中,如果你的应用程序想使用这套API,需要引用rstrtmgr.lib和rstrtmgr.dll。其中,我们常用的函数有:

  •RMStartSession
  创建一个新的重启任务。

  •RMGetList
  这个函数可供安装程序使用,它可以得到所有被影响的应用程序及其当前状态。

  •RMRegisterResources
  注册重启任务的资源,例如文件名,服务或者是RM_UNIQUE_PROCESS结构体。

  •RMRestart
  重新启动被RmShutdown关闭的应用程序或者服务,当然,这些应用程序或者服务都需要通过RegisterApplicationRestart事先进行注册。

  •RMShutDown
  关闭应用程序或者服务。

  •RMEndSession
  结束重启任务。

  添加对重启管理器的支持

  虽然Windows Vista系统本身提供了对重启管理器的支持,但是对于应用程序本身,也同样需要一些额外的工作,以完成对重启管理器的支持。

  对于新创建的MFC应用程序,我们可以简单地在“MFC应用程序向导”中设置是否需要支持重启管理器。


图2 MFC应用程序向导

  在“MFC应用程序向导”的高级特性选项卡中有关于重启管理器的选项。其中,如果仅仅选中“Support Restart Manager”选项,表示你的应用程序将仅仅支持重新启动。换句话说,你的应用程序可以在升级或者崩溃之后重新启动,但是无法自动打开未关闭的文档,无法对数据进行恢复。

  如果同时选中“Reopen previously open documents”选项,它表示你的应用程序可以在重启之后重新打开之前打开的文档,也就是自动恢复到当前的工作状态。

  如果选中了“Support application recover”选项,它表示你的应用程序在重新启动后,不仅可以重新打开之前打开的文档,还会尝试恢复自动保存的文档。它将弹出一个任务对话框(Unicode版本)或者消息框(非Unicode版本),询问用户是否需要恢复自动保持的文档。如果用户选择“Yes”,那么自动保持的文档将被打开作为当前文档。如果用户选择“No”,那么用户最后保存的文档将被打开作为当前文档,同时自动保存的文档将被删除。

  这里需要注意的是,只有文档视图类型的应用程序才支持“Reopen previously open documents”选项和“Support application recover”选项,对话框类型的应用程序只支持“Support Restart Manager”选项。

  对于Visual Studio 2010中新创建的MFC应用程序,可以在应用程序向导中进行设置,添加对重启管理器的支持。那么对于很多已有的MFC应用程序,如果同样想获得重启管理器的支持,应该怎么办呢?实际上,对于已有的MFC应用程序,要想获得重启管理器的支持很简单。在新版本的MFC中,CWinApp类增加了一个新的成员变量,用于控制应用程序对重启管理器的支持,我们只需要在应用程序的构造函数中,添加几行代码,按照我们的需求对其合理的初始化就可以了。

  跟我们在上面所介绍的“MFC应用程序向导”中的选项相一致,如果你选择的是“Support Restart Manager”,你可以在初始化函数中添加如下的代码: 

m_dwRestartManagerSupportFlags =
       AFX_RESTART_MANAGER_SUPPORT_RESTART;

  如果你想选择“Reopen previously open documents”,可以添加如下的代码:

m_dwRestartManagerSupportFlags =
AFX_RESTART_MANAGER_SUPPORT_RESTART_ASPECTS;

  如果你想选择“Support application recover”,可以添加下面这行代码:

m_dwRestartManagerSupportFlags =
        AFX_RESTART_MANAGER_SUPPORT_ALL_ASPECTS;

  例如,我只想支持重启管理器,让软件可以在更新后重新启动,则可以这样进行应用程序的初始化:

CRestartManagerDemoApp::CRestartManagerDemoApp()
{
m_bHiColorIcons
= TRUE;
// support Restart Manager
m_dwRestartManagerSupportFlags
= AFX_RESTART_MANAGER_SUPPORT_RESTART;
// TODO: add construction code here,
// Place all significant initialization in InitInstance
}

  实例:创建支持重启管理器的MFC应用程序

  下面我们以一个实际的例子,来看看如何在我们的MFC应用程序中添加对重启管理器的支持。

  首先,启动Visual Studio 2010 CTP,创建一个单文档的应用程序RestartManagerDemo。按照我们前面的介绍,在“MFC应用程序向导”中选择“Support Restart Manager”和“Reopen previously open documents”选项,以支持应用程序的重新启动和文档的重新打开。

  为了验证重启管理器重新打开文档的功能,我们在文档中添加一些数据,这些数据将在程序重新启动后自动被加载进来。

// 泡泡类,用于在视图中显示圆圈泡泡
class CBubble
{
public:
     CBubble(CPoint cp,
double fR)
{
          m_nCenterPoint
= cp;
          m_fR
= fR;
};
     CBubble()
{};
// 圆心
CPoint m_nCenterPoint;
// 半径    
double m_fR;
};

class CRestartManagerDemoDoc : public CDocument
{
protected: // create from serialization only
CRestartManagerDemoDoc();
DECLARE_DYNCREATE(CRestartManagerDemoDoc)

// Attributes
public:
// 保存数据的数组
    CArray
<CBubble,CBubble&> m_Array;
// Operations
public:
CArray
<CBubble,CBubble&>& GetBubbleArray()
{
                        
return m_Array;
};
//
};

  然后,我们需要实现文档的序列化函数,使得我们的文档数据能够保存和重新加载:

// CRestartManagerDemoDoc serialization

void CRestartManagerDemoDoc::Serialize(CArchive
& ar)
{
// 保存数据
if (ar.IsStoring())
{
  
// TODO: add storing code here
          
int nSize = m_Array.GetSize();
          ar
<<nSize;
          
for(int nIndex = 0; nIndex < nSize; ++nIndex )
  {
   CBubble tempBubble
= m_Array.GetAt( nIndex );
               ar
<<tempBubble.m_nCenterPoint;
               ar
<<tempBubble.m_fR;
  }
}
else // 加载数据
{
  
// TODO: add loading code here
  
int nSize = 0;
          ar
>>nSize;
  
for(int nIndex = 0; nIndex < nSize; ++nIndex )
  {
  
//CBubble tempBubble = m_Array.GetAt( nIndex );
               CPoint tempPoint;
              
double tempR;
              ar
>>tempPoint;
               ar
>>tempR;
   m_Array.Add( CBubble( tempPoint, tempR) );
            
  }
}
}

  完成文档类的工作后,我们就有了保存数据的容器,现在我们需要对数据进行修改和显示。在视图类中,我们通过鼠标点击,修改文档中的数据,向其中添加CBubble对象。

void CRestartManagerDemoView::OnLButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
     CRestartManagerDemoDoc
* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
  
return;
CArray
<CBubble,CBubble&>& m_Array = pDoc->GetBubbleArray();
// 以当前鼠标点击点为圆心,随机半径构造一个CBubble对象,并添加到文档中
m_Array.Add( CBubble( point, rand()%
30 ));

// 更新视图显示
     Invalidate();

CView::OnLButtonDown(nFlags, point);
}

  然后,我们将这些数据在视图中显示出来:

void CRestartManagerDemoView::OnDraw(CDC* pDC)
{
CRestartManagerDemoDoc
* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
  
return;

// TODO: add draw code for native data here
// 从文档中得到数据
     CArray
<CBubble,CBubble&>& m_Array = pDoc->GetBubbleArray();

// 显示数据
for(int nIndex = 0; nIndex < m_Array.GetSize(); ++nIndex )
{
  CBubble tempBubble
= m_Array.GetAt( nIndex );
  pDC
->Ellipse(tempBubble.m_nCenterPoint.x - tempBubble.m_fR,
               tempBubble.m_nCenterPoint.y
- tempBubble.m_fR,
               tempBubble.m_nCenterPoint.x
+ tempBubble.m_fR,
               tempBubble.m_nCenterPoint.y
+ tempBubble.m_fR);
}
}

  这样,我们就实现了一个简单的支持重启管理器的文档视图类型的MFC应用程序。这个程序可以通过鼠标在视图中点击向文档中添加数据,然后这些数据可以保存和重新打开。void CRestartManagerDemoView::OnLButtonDown(UINT nFlags, CPoint point)

{
// TODO: 在此添加消息处理程序代码和/或调用默认值
     CRestartManagerDemoDoc
* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
  
return;
CArray
<CBubble,CBubble&>& m_Array = pDoc->GetBubbleArray();
// 以当前鼠标点击点为圆心,随机半径构造一个CBubble对象,并添加到文档中
m_Array.Add( CBubble( point, rand()%
30 ));

// 更新视图显示
     Invalidate();

CView::OnLButtonDown(nFlags, point);
}

  然后,我们将这些数据在视图中显示出来:
void CRestartManagerDemoView::OnDraw(CDC
* pDC)
{
CRestartManagerDemoDoc
* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
  
return;

// TODO: add draw code for native data here
// 从文档中得到数据
     CArray
<CBubble,CBubble&>& m_Array = pDoc->GetBubbleArray();

// 显示数据
for(int nIndex = 0; nIndex < m_Array.GetSize(); ++nIndex )
{
  CBubble tempBubble
= m_Array.GetAt( nIndex );
  pDC
->Ellipse(tempBubble.m_nCenterPoint.x - tempBubble.m_fR,
               tempBubble.m_nCenterPoint.y
- tempBubble.m_fR,
               tempBubble.m_nCenterPoint.x
+ tempBubble.m_fR,
               tempBubble.m_nCenterPoint.y
+ tempBubble.m_fR);
}
}

  这样,我们就实现了一个简单的支持重启管理器的文档视图类型的MFC应用程序。这个程序可以通过鼠标在视图中点击向文档中添加数据,然后这些数据可以保存和重新打开。

  使用Restart Manassas API测试重启管理器

  接下来,我们可以编写一个测试程序,使用Restart Manassas API模拟软件的更新后重启,以验证其重启管理器是否正常工作。
用Visual Studio 2010 CTP创建一个控制台应用程序TestRM,然后将其实现如下:

// TestRM.cpp : Defines the entry point for the console application.
//

#include
"stdafx.h"
#include
<windows.h>
#include
<restartmanager.h>

int _tmain(int argc, _TCHAR* argv[])
{
     DWORD dwSessionHandle
= 0;
WCHAR wszSessionKey[CCH_RM_SESSION_KEY
+1];

// 设定需要重启的资源
  LPCWSTR pwzResourcesToRestart[] =
  {L
"C:\\Users\\TFSSETUP\\Documents\\Visual Studio 10\\Projects\\
  RestartManagerDemo\\Debug\\RestartManagerDemo.exe" };

// 创建一个重启任务
if (RmStartSession(&dwSessionHandle, 0, wszSessionKey) == ERROR_SUCCESS)
{
  
// 注册资源
  if (RmRegisterResources(dwSessionHandle, 1,
   pwzResourcesToRestart,
0, NULL, 0, NULL) == ERROR_SUCCESS)
  {
  
// 关闭应用程序
   if (RmShutdown(dwSessionHandle,
    RmShutdownOnlyRegistered, NULL)
== ERROR_SUCCESS)
   {
    
// 重新启动应用程序
    if (RmRestart(dwSessionHandle, 0, NULL) == ERROR_SUCCESS)
    {
    
return 0;
    }
   }
  }
}
return 0;
}

  我们首先运行RestartManagerDemo,在视图中用鼠标点击向文档中添加数据,然后保持文档为demo.bub。


图3 支持重启管理器的MFC应用程序

  现在,我们就可以运行TestRM重启这个应用程序了。运行TestRM后,我们会看到RestartManagerDemo会被关闭然后重新打开。同时,我们之前打开的文档demo.bub也被重新加载,整个应用程序很快恢复到了我们之前的工作状态。

  “自从有了重启管理器,更新也简单了,崩溃也不怕了,连加班都不用了!”
 

0
相关文章