다시 시작할 애플리케이션을 등록하려면 RegisterApplicationRestart 함수를 호출합니다. WER(Windows 오류 보고) 응답하지 않거나 처리되지 않은 예외가 발생하기 전에 60초 이상 실행된 경우 애플리케이션을 다시 시작합니다.
또한 WER이 애플리케이션을 다시 시작할 때 유용할 수 있는 데이터 및 상태 정보를 저장할 수 있는 복구 등록하는고려해야 합니다. 복구를 위해 등록하는 경우 복구 프로세스가 완료된 후 WER이 애플리케이션을 다시 시작합니다.
복구 프로세스가 완료되면 WER은 애플리케이션을 종료한 다음 다시 시작합니다. 콘솔 애플리케이션의 경우 애플리케이션이 종료될 때 닫는 별도의 콘솔 창에서 애플리케이션이 시작됩니다.
응용 프로그램 설치 관리자 작성자 주의: 응용 프로그램 다시 시작을 위한 등록은 컴퓨터가 소프트웨어 업데이트로 인해 다시 시작된 경우, Windows가 자동으로 응용 프로그램을 다시 시작하게 합니다. 이렇게 하려면 애플리케이션의 설치 관리자가 EWX_RESTARTAPPS 플래그 집합을 사용하여 ExitWindowsEx 함수를 호출하거나 SHUTDOWN_RESTARTAPPS 플래그가 설정된 InitiateShutdown 함수를 호출해야 합니다.
다음 예제에서는 WER이 애플리케이션을 다시 시작하도록 등록하는 방법을 보여 줍니다. 이 예제에서는 애플리케이션 다시 시작을 등록한 후 액세스 위반이 발생합니다. 액세스 위반은 Windows 오류 보고에서 감지되어, 애플리케이션이 다시 시작되는 것과 같은 사용자 오류 보고 환경을 보여 줍니다. 명령줄 인수가 없는 콘솔 창에서 실행해야 합니다.
#include <windows.h>
#include <stdio.h>
#include <strsafe.h>
#define RESTART_SWITCH L"/restart"
void GetRestartInfo(int argc, wchar_t* argv[], BOOL* pfIsRestart, DWORD* pdwRecordId);
DWORD InitApplication(BOOL fIsRestart, DWORD* pdwRecordId);
BOOL WINAPI CtrlHandler(DWORD dwControlType);
DWORD WINAPI Recover(PVOID pContext);
// A simple example to show how to use application recovery. For simplicity,
// the state information (record ID) is passed as part of the command line.
void wmain(int argc, wchar_t* argv[])
{
HRESULT hr = S_OK;
DWORD dwRecordId = 0;
BOOL fIsRestart = FALSE;
GetRestartInfo(argc, argv, &fIsRestart, &dwRecordId);
if (FAILED(hr = InitApplication(fIsRestart, &dwRecordId)))
{
wprintf(L"Failed to initialize the application.\n");
goto cleanup;
}
// Do work. If you have a lot of state information, you should
// periodically persist the state information to save time
// in the recovery process.
// The application must exist for at least 60 seconds for the
// application to be restarted. Use Sleep() to mimic 60 seconds
// worth of work.
wprintf(L"Sleeping...\n");
Sleep(62 * 1000);
// Set the record ID to verify restart value.
dwRecordId = 10;
// Generate an access violation to force a restart. If we've already
// restarted just exit because the example already demonstrated
// the restart feature.
if (FALSE == fIsRestart)
{
wprintf(L"Causing access violation...\n");
int* p = NULL;
*p = 5;
}
cleanup:
wprintf(L"Exiting...\n");
}
// Get the restart info from the command line. The command line is
// of the form, /restart -r <RecordId>. The application
// is being restarted if the first argument is /restart.
void GetRestartInfo(int argc, wchar_t* argv[], BOOL* pfIsRestart, DWORD* pdwRecordId)
{
if (argc > 1)
{
if (!wcsncmp(RESTART_SWITCH, argv[1], sizeof(RESTART_SWITCH)))
{
*pfIsRestart = TRUE;
*pdwRecordId = _wtoi(argv[3]);
}
}
}
// Initialize the application. If this is a restart, use the state
// information to reset the state of the application. This example
// does not fail the initialization process if the process of
// registering for application recovery and restart failed.
DWORD InitApplication(BOOL fIsRestart, DWORD* pdwRecordId)
{
DWORD status = ERROR_SUCCESS; // Set only if the initializing the application fails,
HRESULT hr = S_OK; // not if registering for recovery and restart fails.
WCHAR wsCommandLine[RESTART_MAX_CMD_LINE];
wprintf(L"Entering InitApplication...\n");
if (fIsRestart)
{
// TODO: Use the record ID to initialize the application.
wprintf(L"Restart record ID is %lu\n", *pdwRecordId);
}
else
{
// This is not a restart, so initialize the application accordingly.
}
// Register for restart. The command line is updated in the recovery callback.
RtlZeroMemory(wsCommandLine, sizeof(wsCommandLine));
StringCchPrintf(wsCommandLine, sizeof(wsCommandLine), L"/restart -r %lu", *pdwRecordId);
hr = RegisterApplicationRestart(wsCommandLine, RESTART_NO_PATCH | RESTART_NO_REBOOT);
if (FAILED(hr))
{
// Not failing because the registration failed.
wprintf(L"RegisterApplicationRestart failed with ox%x.\n", hr);
goto cleanup;
}
// Register the callback that handles the control event notifications.
// Used for recovery when an installer is updating a component of the
// application.
if (!SetConsoleCtrlHandler(CtrlHandler, TRUE))
{
// Not failing initialization because the registration failed.
// Consider calling UnregisterApplicationRestart if you must
// have the latest state information.
wprintf(L"SetConsoleCtrlHandler failed.\n");
goto cleanup;
}
// Register the callback that handles recovery when the application
// encounters an unhandled exception or becomes unresponsive.
hr = RegisterApplicationRecoveryCallback(Recover, pdwRecordId, RECOVERY_DEFAULT_PING_INTERVAL, 0);
if (FAILED(hr))
{
// Not failing initialization because the registration failed.
// Consider calling UnregisterApplicationRestart if you must
// have the latest state information.
wprintf(L"RegisterApplicationRecoveryCallback failed with ox%x.\n", hr);
goto cleanup;
}
cleanup:
return hr;
}
// Implement the callback for handling control character events.
// You'd implement this callback if an installer could update a
// component of your application. The system sends a CTRL_C_EVENT
// notification when an installer needs to shutdown your application
// or restart the computer in order to complete the installation.
// You can use the CTRL_C_EVENT to save final state information or
// data before exiting.
BOOL WINAPI CtrlHandler(DWORD dwControlType)
{
wprintf(L"Entering CtrlHandler...\n");
switch (dwControlType)
{
case CTRL_C_EVENT:
wprintf(L"Handling CTRL_C_EVENT\n");
return FALSE;
// Other cases go here.
default:
wprintf(L"Other, %ul\n", dwControlType);
return FALSE;
}
}
// Implement the recovery callback. This callback lets the application
// save state information or data in the event that the application
// encounters an unhandled exception or becomes unresponsive.
DWORD WINAPI Recover(PVOID pContext)
{
HRESULT hr = S_OK;
BOOL bCanceled = FALSE;
DWORD dwRecordId = *(DWORD*)pContext;
WCHAR wsCommandLine[RESTART_MAX_CMD_LINE];
wprintf(L"Entering Recover callback...\n");
// Do recovery work.
// Update the restart command line.
RtlZeroMemory(wsCommandLine, sizeof(wsCommandLine));
StringCchPrintf(wsCommandLine, sizeof(wsCommandLine), L"/restart -r %lu", dwRecordId);
hr = RegisterApplicationRestart(wsCommandLine, RESTART_NO_PATCH | RESTART_NO_REBOOT);
if (FAILED(hr))
{
// Not failing because the registration failed.
wprintf(L"RegisterApplicationRestart failed with ox%x.\n", hr);
}
// You must call the ApplicationRecoveryInProgress function within
// the specified ping interval or the recovery callback exits.
// Typically, you would do a block of work, call the function, and repeat.
hr = ApplicationRecoveryInProgress(&bCanceled);
if (bCanceled)
{
wprintf(L"Recovery was canceled by the user.\n");
goto cleanup;
}
// Do more recovery work.
// You could also call the RegisterApplicationRestart function to
// update the command line used for the restart.
cleanup:
// Save the state file.
wprintf(L"Leaving Recover callback...\n");
ApplicationRecoveryFinished((bCanceled) ? FALSE: TRUE);
return 0;
}