You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
592 lines
16 KiB
592 lines
16 KiB
/*++
|
|
|
|
Copyright (c) 1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
asr_app.c
|
|
|
|
Abstract:
|
|
|
|
Sample third party ASR recovery application.
|
|
|
|
Authors:
|
|
|
|
Guhan Suriyanarayanan (guhans) 07-Oct-1999
|
|
|
|
Revision History:
|
|
|
|
07-Oct-2000 guhans
|
|
Initial creation
|
|
|
|
--*/
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <windows.h>
|
|
#include <winasr.h>
|
|
#include <setupapi.h>
|
|
|
|
//
|
|
// Macro Description:
|
|
// If ErrorCondition occurs, it sets the LocalStatus to the ErrorCode
|
|
// passed in, calls SetLastError() to set the Last Error to ErrorCode,
|
|
// and jumps to the EXIT label in the calling function
|
|
//
|
|
// Arguments:
|
|
// ErrorCondition // Expression to be tested
|
|
// LocalStatus // Status variable in the calling function
|
|
// LONG ErrorCode // ErrorCode
|
|
//
|
|
#define pErrExitCode( ErrorCondition, LocalStatus, ErrorCode ) { \
|
|
\
|
|
if ((BOOL) ErrorCondition) { \
|
|
\
|
|
wprintf(L"Error %lu (0x%x), line %lu", ErrorCode, ErrorCode, __LINE__); \
|
|
\
|
|
LocalStatus = (DWORD) ErrorCode; \
|
|
\
|
|
SetLastError((DWORD) ErrorCode); \
|
|
\
|
|
goto EXIT; \
|
|
} \
|
|
}
|
|
|
|
|
|
//
|
|
// Constants local to this module
|
|
//
|
|
const WCHAR BACKUP_OPTION[] = L"/backup";
|
|
const WCHAR RESTORE_OPTION[] = L"/restore";
|
|
const WCHAR REGISTER_OPTION[] = L"/register";
|
|
|
|
const WCHAR SIF_PATH_FORMAT[] = L"/sifpath=%ws";
|
|
const WCHAR ERROR_FILE_PATH[] = L"%systemroot%\\repair\\asr.err";
|
|
|
|
const WCHAR MY_SIF_SECTION[] = L"[ASR_APP.APPDATA]";
|
|
const WCHAR MY_SIF_SECTION_NAME[] = L"ASR_APP.APPDATA";
|
|
|
|
|
|
const WCHAR GENERIC_ERROR_MESSAGE[] = L"asr_app could not complete successfully (error %lu 0x%x)\n\nusage: asr_app {/backup | /restore /sifpath=<path to asr.sif> | /register <path to asr_app>}";
|
|
const WCHAR GENERIC_ERROR_TITLE[] = L"asr_app error";
|
|
|
|
#ifdef _IA64_
|
|
const WCHAR CONTEXT_FORMAT[] = L"/context=%I64u";
|
|
#else
|
|
const WCHAR CONTEXT_FORMAT[] = L"/context=%lu";
|
|
#endif
|
|
|
|
|
|
#define ASR_REG_KEY L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Asr\\Commands"
|
|
#define MY_REG_KEY_VALUE_NAME L"ASR Sample Application"
|
|
|
|
typedef enum _AsrAppOption {
|
|
AsrAppNone = 0,
|
|
AsrAppRegister,
|
|
AsrAppBackup,
|
|
AsrAppRestore
|
|
} AsrAppOption;
|
|
|
|
HANDLE Gbl_hErrorFile = NULL;
|
|
|
|
|
|
PWSTR // must be freed by caller
|
|
ExpandEnvStrings(
|
|
IN CONST PCWSTR OriginalString
|
|
)
|
|
{
|
|
PWSTR expandedString = NULL;
|
|
UINT cchSize = MAX_PATH + 1, // start with a reasonable default
|
|
cchRequiredSize = 0;
|
|
BOOL result = FALSE;
|
|
|
|
DWORD status = ERROR_SUCCESS;
|
|
HANDLE heapHandle = GetProcessHeap();
|
|
|
|
expandedString = (PWSTR) HeapAlloc(heapHandle, HEAP_ZERO_MEMORY, (cchSize * sizeof(WCHAR)));
|
|
if (!expandedString) {
|
|
return NULL;
|
|
}
|
|
|
|
cchRequiredSize = ExpandEnvironmentStringsW(OriginalString,
|
|
expandedString,
|
|
cchSize
|
|
);
|
|
|
|
if (cchRequiredSize > cchSize) {
|
|
//
|
|
// Buffer wasn't big enough; free and re-allocate as needed
|
|
//
|
|
HeapFree(heapHandle, 0L, expandedString);
|
|
cchSize = cchRequiredSize + 1;
|
|
|
|
expandedString = (PWSTR) HeapAlloc(heapHandle, HEAP_ZERO_MEMORY, (cchSize * sizeof(WCHAR)));
|
|
if (!expandedString) {
|
|
return NULL;
|
|
}
|
|
|
|
cchRequiredSize = ExpandEnvironmentStringsW(OriginalString,
|
|
expandedString,
|
|
cchSize
|
|
);
|
|
}
|
|
|
|
if ((0 == cchRequiredSize) || (cchRequiredSize > cchSize)) {
|
|
//
|
|
// Either the function failed, or the buffer wasn't big enough
|
|
// even on the second try
|
|
//
|
|
HeapFree(heapHandle, 0L, expandedString);
|
|
expandedString = NULL;
|
|
}
|
|
|
|
return expandedString;
|
|
}
|
|
|
|
|
|
VOID
|
|
OpenErrorFile()
|
|
{
|
|
PWSTR szErrorFilePath = NULL;
|
|
|
|
//
|
|
// Get full path to the error file (%systemroot%\repair\asr.err)
|
|
//
|
|
szErrorFilePath = ExpandEnvStrings(ERROR_FILE_PATH);
|
|
if (!szErrorFilePath) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Open the error file
|
|
//
|
|
Gbl_hErrorFile = CreateFileW(
|
|
szErrorFilePath, // lpFileName
|
|
GENERIC_WRITE | GENERIC_READ, // dwDesiredAccess
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, // dwShareMode
|
|
NULL, // lpSecurityAttributes
|
|
OPEN_ALWAYS, // dwCreationFlags
|
|
FILE_FLAG_WRITE_THROUGH, // dwFlagsAndAttributes
|
|
NULL // hTemplateFile
|
|
);
|
|
|
|
HeapFree(GetProcessHeap(), 0L, szErrorFilePath);
|
|
szErrorFilePath = NULL;
|
|
|
|
if ((!Gbl_hErrorFile) || (INVALID_HANDLE_VALUE == Gbl_hErrorFile)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Move to the end of file
|
|
//
|
|
SetFilePointer(Gbl_hErrorFile, 0L, NULL, FILE_END);
|
|
}
|
|
|
|
|
|
VOID
|
|
CloseErrorFile(
|
|
VOID
|
|
)
|
|
{
|
|
|
|
if ((Gbl_hErrorFile) && (INVALID_HANDLE_VALUE != Gbl_hErrorFile)) {
|
|
CloseHandle(Gbl_hErrorFile);
|
|
Gbl_hErrorFile = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LogErrorMessage(
|
|
IN CONST PCWSTR Message
|
|
)
|
|
{
|
|
SYSTEMTIME currentTime;
|
|
DWORD bytesWritten = 0;
|
|
WCHAR buffer[4196];
|
|
|
|
if ((!Gbl_hErrorFile) || (INVALID_HANDLE_VALUE == Gbl_hErrorFile)) {
|
|
//
|
|
// We haven't been initialised, or the error file couldn't be
|
|
// created for some reason.
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
// In case someone else wrote to this file since our last write
|
|
//
|
|
SetFilePointer(Gbl_hErrorFile, 0L, NULL, FILE_END);
|
|
|
|
//
|
|
// Create our string, and write it out
|
|
//
|
|
GetLocalTime(¤tTime);
|
|
swprintf(buffer,
|
|
L"\r\n[%04hu/%02hu/%02hu %02hu:%02hu:%02hu ASR_APP] (ERROR) %s\r\n",
|
|
currentTime.wYear,
|
|
currentTime.wMonth,
|
|
currentTime.wDay,
|
|
currentTime.wHour,
|
|
currentTime.wMinute,
|
|
currentTime.wSecond,
|
|
Message
|
|
);
|
|
|
|
WriteFile(Gbl_hErrorFile,
|
|
buffer,
|
|
(wcslen(buffer) * sizeof(WCHAR)),
|
|
&bytesWritten,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupState(
|
|
IN CONST DWORD_PTR AsrContext
|
|
)
|
|
{
|
|
//
|
|
// Gather our state to backup
|
|
//
|
|
HMODULE hSyssetup = NULL;
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
BOOL bResult = FALSE;
|
|
|
|
//
|
|
// BOOL
|
|
// AsrAddSifEntryW(
|
|
// IN DWORD_PTR AsrContext,
|
|
// IN PCWSTR lpSectionName,
|
|
// IN PCWSTR lpSifEntry
|
|
// );
|
|
//
|
|
BOOL (*pfnAddSifEntry)(DWORD_PTR, PCWSTR, PCWSTR);
|
|
|
|
//
|
|
// Load syssetup.dll
|
|
//
|
|
hSyssetup = LoadLibraryW(L"syssetup.dll");
|
|
pErrExitCode(
|
|
(!hSyssetup || INVALID_HANDLE_VALUE == hSyssetup),
|
|
dwStatus,
|
|
GetLastError()
|
|
);
|
|
|
|
//
|
|
// Get the RestoreNonCriticalDisksW API exported by syssetup.dll
|
|
//
|
|
pfnAddSifEntry = (BOOL (*)(DWORD_PTR, PCWSTR, PCWSTR))
|
|
GetProcAddress(hSyssetup, "AsrAddSifEntryW");
|
|
pErrExitCode((!pfnAddSifEntry), dwStatus, GetLastError());
|
|
|
|
|
|
//
|
|
// Add the state to asr.sif
|
|
//
|
|
bResult = pfnAddSifEntry(
|
|
AsrContext,
|
|
MY_SIF_SECTION,
|
|
L"1=\"asr_app sample application data\",100,200,300"
|
|
);
|
|
pErrExitCode(!bResult, dwStatus, GetLastError());
|
|
|
|
//
|
|
// Also add to the commands and installfiles section, so that we get
|
|
// called during the ASR recovery.
|
|
//
|
|
|
|
//
|
|
// INSTALLFILES section entry format:
|
|
// system-key,source-media-label,source-device,
|
|
// source-file-path,destination-file-path,vendor-name
|
|
// system-key must be 1
|
|
//
|
|
bResult = pfnAddSifEntry(
|
|
AsrContext,
|
|
ASR_SIF_SECTION_INSTALLFILES,
|
|
L"1,\"ASR Sample App Disk 1\",\"%FLOPPY%\",\"i386\\asr_app.exe\",\"%temp%\\asr_app.exe\",\"ASR Sample App Company\""
|
|
//L"1,\"Application Disk 1\",\"\\device\\cdrom0\",\"application.exe\",\"%TEMP%\\application.exe\",\"Company Name\""
|
|
);
|
|
pErrExitCode(!bResult, dwStatus, GetLastError());
|
|
|
|
// CString cmd =L"1,\"ASRDisk1\",\"\\Device\\Floppy0\\edmbackup.exe\",\"%TEMP%\\edmbackup.exe\",\"EMC\"";
|
|
|
|
/* bResult = pfnAddSifEntry(AsrContext,
|
|
ASR_SIF_SECTION_INSTALLFILES,
|
|
(LPCTSTR) L"1,\"ASRDisk1\",\"\\Device\\Floppy0\\edmbackup.exe\",\"%TEMP%\\edmbackup.exe\",\"EMC\"" );
|
|
pErrExitCode(!bResult, dwStatus, GetLastError());
|
|
*/
|
|
|
|
|
|
//
|
|
// COMMANDS section entry format:
|
|
// system-key,sequence-number,action-on-completion,"command","parameters"
|
|
// system-key must be 1
|
|
// 1000 <= sequence-number <= 4999
|
|
// 0 <= action-on-completion <= 1
|
|
//
|
|
bResult = pfnAddSifEntry(
|
|
AsrContext,
|
|
ASR_SIF_SECTION_COMMANDS,
|
|
L"1,3500,1,\"%temp%\\asr_app.exe\",\"/restore\""
|
|
);
|
|
pErrExitCode(!bResult, dwStatus, GetLastError());
|
|
|
|
EXIT:
|
|
//
|
|
// Cleanup
|
|
//
|
|
if (hSyssetup) {
|
|
FreeLibrary(hSyssetup);
|
|
hSyssetup = NULL;
|
|
}
|
|
|
|
return (ERROR_SUCCESS == dwStatus);
|
|
}
|
|
|
|
|
|
BOOL
|
|
RestoreState(
|
|
IN CONST PCWSTR szAsrSifPath
|
|
)
|
|
{
|
|
HINF hSif = NULL;
|
|
INFCONTEXT infContext;
|
|
BOOL bResult = FALSE;
|
|
|
|
WCHAR szErrorString[1024];
|
|
|
|
int iValue1 = 0,
|
|
iValue2 = 0,
|
|
iValue3 = 0;
|
|
|
|
WCHAR szBuffer[1024];
|
|
|
|
//
|
|
// Open the asr.sif
|
|
//
|
|
hSif = SetupOpenInfFile(szAsrSifPath, NULL, INF_STYLE_WIN4, NULL);
|
|
if ((!hSif) || (INVALID_HANDLE_VALUE == hSif)) {
|
|
|
|
wsprintf(szErrorString, L"Unable to open the ASR state file at %ws (0x%x)",
|
|
szAsrSifPath,
|
|
GetLastError()
|
|
);
|
|
LogErrorMessage(szErrorString);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Find the section
|
|
//
|
|
bResult = SetupFindFirstLineW(hSif, MY_SIF_SECTION_NAME, NULL, &infContext);
|
|
if (bResult) {
|
|
|
|
//
|
|
// Read in the information. We had one string followed by three numbers.
|
|
//
|
|
bResult = SetupGetStringField(&infContext, 1, szBuffer, 1024, NULL)
|
|
&& SetupGetIntField(&infContext, 2, &iValue1)
|
|
&& SetupGetIntField(&infContext, 3, &iValue2)
|
|
&& SetupGetIntField(&infContext, 4, &iValue3);
|
|
|
|
if (bResult) {
|
|
//
|
|
// Now restore our state. Let's just pretend we're doing something.
|
|
//
|
|
wprintf(L"Values read: %ws %lu %lu %lu\n\n", szBuffer, iValue1, iValue2, iValue3);
|
|
wprintf(L"Restoring sample system state, please wait ... ");
|
|
|
|
Sleep(5000);
|
|
wprintf(L"done\n");
|
|
|
|
}
|
|
else {
|
|
|
|
wsprintf(szErrorString,
|
|
L"Some values in the asr_app section of the ASR state file %ws could not be read (0x%x). "
|
|
L"This may indicate a corrupt or an incompatible version of the ASR state file",
|
|
szAsrSifPath,
|
|
GetLastError()
|
|
);
|
|
LogErrorMessage(szErrorString);
|
|
}
|
|
}
|
|
else {
|
|
|
|
wsprintf(szErrorString,
|
|
L"Unable to locate asr_app section in ASR state file %ws (0x%x). "
|
|
L"This may indicate a corrupt or an incompatible version of the ASR state file",
|
|
szAsrSifPath,
|
|
GetLastError()
|
|
);
|
|
LogErrorMessage(szErrorString);
|
|
}
|
|
|
|
SetupCloseInfFile(hSif);
|
|
return bResult;
|
|
}
|
|
|
|
|
|
DWORD
|
|
RegisterForAsrBackup(
|
|
IN CONST PCWSTR szApplicationName
|
|
)
|
|
{
|
|
|
|
DWORD dwResult = ERROR_SUCCESS;
|
|
HKEY hKeyAsr = NULL;
|
|
|
|
WCHAR szData[1024];
|
|
|
|
if (wcslen(szApplicationName) > 1000) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
wsprintf(szData, L"%ws %ws", szApplicationName, BACKUP_OPTION);
|
|
|
|
//
|
|
// Open the registry key
|
|
//
|
|
dwResult = RegOpenKeyExW(
|
|
HKEY_LOCAL_MACHINE, // hKey
|
|
ASR_REG_KEY, // lpSubKey
|
|
0, // ulOptions--Reserved, must be 0
|
|
MAXIMUM_ALLOWED, // samDesired
|
|
&hKeyAsr // phkbResult
|
|
);
|
|
if (ERROR_SUCCESS != dwResult) {
|
|
return dwResult;
|
|
}
|
|
|
|
dwResult = RegSetValueExW(
|
|
hKeyAsr, // hKey
|
|
MY_REG_KEY_VALUE_NAME, // lpValueName
|
|
0, // dwReserved, must be 0
|
|
REG_SZ, // dwType
|
|
(LPBYTE)szData, // lpData
|
|
((wcslen(szData) + 1)* (sizeof(WCHAR))) // cbData
|
|
);
|
|
|
|
return dwResult;
|
|
}
|
|
|
|
|
|
int
|
|
__cdecl // var arg
|
|
wmain (
|
|
int argc,
|
|
wchar_t *argv[],
|
|
wchar_t *envp[]
|
|
)
|
|
{
|
|
|
|
AsrAppOption option = AsrAppNone;
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
|
|
if (argc >= 3) {
|
|
if (!_wcsicmp(argv[1], BACKUP_OPTION)) {
|
|
//
|
|
// asr_app /backup /context=nnn
|
|
//
|
|
option = AsrAppBackup;
|
|
}
|
|
else if (!_wcsicmp(argv[1], RESTORE_OPTION)) {
|
|
//
|
|
// asr_app /restore /sifpath="c:\winnt\repair\asr.sif"
|
|
//
|
|
option = AsrAppRestore;
|
|
}
|
|
else if (!_wcsicmp(argv[1], REGISTER_OPTION)) {
|
|
//
|
|
// asr_app /register "c:\apps\asr_app\asr_app.exe"
|
|
//
|
|
option = AsrAppRegister;
|
|
}
|
|
}
|
|
|
|
switch (option) {
|
|
|
|
case AsrAppRegister: { // This App is being installed
|
|
|
|
dwStatus = RegisterForAsrBackup(argv[2]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case AsrAppBackup: { // An ASR Backup is in progress
|
|
DWORD_PTR AsrContext = 0;
|
|
|
|
//
|
|
// Extract the asr context from the commandline
|
|
//
|
|
swscanf(argv[2], CONTEXT_FORMAT, &AsrContext);
|
|
|
|
//
|
|
// Create our spooge and write to the asr.sif
|
|
//
|
|
if (!BackupState(AsrContext)) {
|
|
dwStatus = GetLastError();
|
|
}
|
|
// AsrFreeContext(&AsrContext);
|
|
|
|
//
|
|
// And we're done
|
|
//
|
|
break;
|
|
}
|
|
|
|
case AsrAppRestore: { // An ASR Restore is in progress
|
|
WCHAR szAsrFilePath[MAX_PATH +1];
|
|
|
|
//
|
|
// Get the path to the asr.sif
|
|
//
|
|
swscanf(argv[2], SIF_PATH_FORMAT, szAsrFilePath);
|
|
OpenErrorFile();
|
|
|
|
//
|
|
// Read our spooge from asr.sif, and recreate the state. Be sure to
|
|
// write out the error to %systemroot%\repair\asr.err in case of
|
|
// error.
|
|
//
|
|
if (!RestoreState(szAsrFilePath)) {
|
|
dwStatus = GetLastError();
|
|
}
|
|
CloseErrorFile();
|
|
|
|
//
|
|
// And we're done
|
|
//
|
|
break;
|
|
}
|
|
|
|
case AsrAppNone:
|
|
default: {
|
|
|
|
//
|
|
// Command-line parameters were incorrect, display usage message
|
|
//
|
|
dwStatus = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ERROR_SUCCESS != dwStatus) {
|
|
//
|
|
// We hit an error
|
|
//
|
|
WCHAR szErrorMessage[1024];
|
|
|
|
swprintf(szErrorMessage, GENERIC_ERROR_MESSAGE, dwStatus, dwStatus);
|
|
MessageBoxW(NULL, szErrorMessage, GENERIC_ERROR_TITLE, MB_OK | MB_ICONSTOP);
|
|
}
|
|
|
|
SetLastError(dwStatus);
|
|
return (int) dwStatus;
|
|
}
|