MSICA/MSICALib/MSITSCAOp.cpp
Simon Rozman 59e983d8fc Pripravo spiskov opravil za deffered/commit/rollback sem poenotil in poenostavil. Posledično sta se InstallScheduledTasks() in FinalizeScheduledTasks() zlili v eno.
Estetski popravki

Urediti moram še sporočanje napredka, ter obveščanje o napakah med akcijami deffered/commit/rollback.
2012-12-21 14:52:17 +00:00

669 lines
22 KiB
C++

#include "StdAfx.h"
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOp
////////////////////////////////////////////////////////////////////////////
CMSITSCAOp::CMSITSCAOp()
{
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpSingleStringOperation
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpSingleStringOperation::CMSITSCAOpSingleStringOperation(LPCWSTR pszValue) :
m_sValue(pszValue),
CMSITSCAOp()
{
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpDoubleStringOperation
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpSrcDstStringOperation::CMSITSCAOpSrcDstStringOperation(LPCWSTR pszValue1, LPCWSTR pszValue2) :
m_sValue1(pszValue1),
m_sValue2(pszValue2),
CMSITSCAOp()
{
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpBooleanOperation
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpBooleanOperation::CMSITSCAOpBooleanOperation(BOOL bValue) :
m_bValue(bValue)
{
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpEnableRollback
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpEnableRollback::CMSITSCAOpEnableRollback(BOOL bEnable) :
CMSITSCAOpBooleanOperation(bEnable)
{
}
HRESULT CMSITSCAOpEnableRollback::Execute(CMSITSCASession *pSession)
{
assert(pSession);
pSession->m_bRollbackEnabled = m_bValue;
return S_OK;
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpDeleteFile
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpDeleteFile::CMSITSCAOpDeleteFile(LPCWSTR pszFileName) :
CMSITSCAOpSingleStringOperation(pszFileName)
{
}
HRESULT CMSITSCAOpDeleteFile::Execute(CMSITSCASession *pSession)
{
assert(pSession);
if (pSession->m_bRollbackEnabled) {
CStringW sBackupName;
UINT uiCount = 0;
DWORD dwError;
do {
// Rename the file to make a backup.
sBackupName.Format(L"%ls (orig %u)", (LPCWSTR)m_sValue, ++uiCount);
dwError = ::MoveFileW(m_sValue, sBackupName) ? ERROR_SUCCESS : ::GetLastError();
} while (dwError == ERROR_FILE_EXISTS);
if (dwError != ERROR_SUCCESS) return AtlHresultFromWin32(dwError);
// Order rollback action to restore from backup copy.
pSession->m_olRollback.AddHead(new CMSITSCAOpMoveFile(sBackupName, m_sValue));
// Order commit action to delete backup copy.
pSession->m_olCommit.AddTail(new CMSITSCAOpDeleteFile(sBackupName));
return S_OK;
} else {
// Delete the file.
return ::DeleteFileW(m_sValue) ? S_OK : AtlHresultFromLastError();
}
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpMoveFile
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpMoveFile::CMSITSCAOpMoveFile(LPCWSTR pszFileSrc, LPCWSTR pszFileDst) :
CMSITSCAOpSrcDstStringOperation(pszFileSrc, pszFileDst)
{
}
HRESULT CMSITSCAOpMoveFile::Execute(CMSITSCASession *pSession)
{
assert(pSession);
if (pSession->m_bRollbackEnabled) {
// Move the file.
if (::MoveFileW(m_sValue1, m_sValue2)) {
// Order rollback action to move it back.
pSession->m_olRollback.AddHead(new CMSITSCAOpMoveFile(m_sValue2, m_sValue1));
return S_OK;
} else
return AtlHresultFromLastError();
} else {
// Move the file.
return ::MoveFileW(m_sValue1, m_sValue2) ? S_OK : AtlHresultFromLastError();
}
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpCreateTask
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpCreateTask::CMSITSCAOpCreateTask(LPCWSTR pszTaskName) :
m_bForce(FALSE),
m_dwFlags(0),
m_dwPriority(NORMAL_PRIORITY_CLASS),
m_wIdleMinutes(0),
m_wDeadlineMinutes(0),
m_dwMaxRuntimeMS(INFINITE),
CMSITSCAOpSingleStringOperation(pszTaskName)
{
}
CMSITSCAOpCreateTask::~CMSITSCAOpCreateTask()
{
// Clear the password in memory.
int iLength = m_sPassword.GetLength();
::SecureZeroMemory(m_sPassword.GetBuffer(iLength), sizeof(WCHAR) * iLength);
m_sPassword.ReleaseBuffer(0);
}
HRESULT CMSITSCAOpCreateTask::Execute(CMSITSCASession *pSession)
{
HRESULT hr;
CComPtr<ITask> pTask;
POSITION pos;
// Load the task to see if it exists.
hr = pSession->m_pTaskScheduler->Activate(m_sValue, IID_ITask, (IUnknown**)&pTask);
if (SUCCEEDED(hr)) {
// The task exists. Release it prematurely before proceeding.
pTask.Detach()->Release();
if (m_bForce) {
// We're supposed to overwrite it. Delete the existing task first.
// Since task deleting is a complicated job (when rollback/commit support is required), and we do have an operation for just that, we use it.
CMSITSCAOpDeleteTask opDeleteTask(m_sValue);
hr = opDeleteTask.Execute(pSession);
if (FAILED(hr)) return hr;
} else {
// The task exists, and we're happy with that.
return S_OK;
}
}
// Create the new task.
hr = pSession->m_pTaskScheduler->NewWorkItem(m_sValue, CLSID_CTask, IID_ITask, (IUnknown**)&pTask);
if (pSession->m_bRollbackEnabled) {
// Order rollback action to delete the task. ITask::NewWorkItem() might made a blank task already.
pSession->m_olRollback.AddHead(new CMSITSCAOpDeleteTask(m_sValue));
}
if (FAILED(hr)) return hr;
// Set its properties.
hr = pTask->SetApplicationName (m_sApplicationName ); if (FAILED(hr)) return hr;
hr = pTask->SetComment (m_sComment ); if (FAILED(hr)) return hr;
hr = pTask->SetParameters (m_sParameters ); if (FAILED(hr)) return hr;
hr = pTask->SetWorkingDirectory(m_sWorkingDirectory); if (FAILED(hr)) return hr;
if (pSession->m_bRollbackEnabled && (m_dwFlags & TASK_FLAG_DISABLED) == 0) {
// The task is supposed to be enabled.
// However, we shall enable it at commit to prevent it from accidentally starting in the very installation phase.
pSession->m_olCommit.AddTail(new CMSITSCAOpEnableTask(m_sValue, TRUE));
m_dwFlags |= TASK_FLAG_DISABLED;
}
hr = pTask->SetFlags (m_dwFlags ); if (FAILED(hr)) return hr;
hr = pTask->SetPriority (m_dwPriority ); if (FAILED(hr)) return hr;
if (!m_sAccountName.IsEmpty()) {
hr = pTask->SetAccountInformation(m_sAccountName, m_sPassword.IsEmpty() ? NULL : m_sPassword);
if (FAILED(hr)) return hr;
}
hr = pTask->SetIdleWait (m_wIdleMinutes, m_wDeadlineMinutes); if (FAILED(hr)) return hr;
hr = pTask->SetMaxRunTime(m_dwMaxRuntimeMS ); if (FAILED(hr)) return hr;
// Add triggers.
for (pos = m_lTriggers.GetHeadPosition(); pos;) {
WORD wTriggerIdx;
CComPtr<ITaskTrigger> pTrigger;
TASK_TRIGGER &ttData = m_lTriggers.GetNext(pos);
hr = pTask->CreateTrigger(&wTriggerIdx, &pTrigger);
if (FAILED(hr)) return hr;
hr = pTrigger->SetTrigger(&ttData);
if (FAILED(hr)) return hr;
}
// Save the task.
CComQIPtr<IPersistFile> pTaskFile(pTask);
hr = pTaskFile->Save(NULL, TRUE);
return S_OK;
}
UINT CMSITSCAOpCreateTask::SetFromRecord(MSIHANDLE hInstall, MSIHANDLE hRecord)
{
UINT uiResult;
int iValue;
uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 3, m_sApplicationName);
if (uiResult != ERROR_SUCCESS) return uiResult;
uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 4, m_sParameters);
if (uiResult != ERROR_SUCCESS) return uiResult;
uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 5, m_sWorkingDirectory);
if (uiResult != ERROR_SUCCESS) return uiResult;
uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 10, m_sComment);
if (uiResult != ERROR_SUCCESS) return uiResult;
m_dwFlags = ::MsiRecordGetInteger(hRecord, 6);
if (m_dwFlags == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
m_dwPriority = ::MsiRecordGetInteger(hRecord, 7);
if (m_dwPriority == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 8, m_sAccountName);
if (uiResult != ERROR_SUCCESS) return uiResult;
uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 9, m_sPassword);
if (uiResult != ERROR_SUCCESS) return uiResult;
iValue = ::MsiRecordGetInteger(hRecord, 11);
m_wIdleMinutes = iValue != MSI_NULL_INTEGER ? (WORD)iValue : 0;
iValue = ::MsiRecordGetInteger(hRecord, 12);
m_wDeadlineMinutes = iValue != MSI_NULL_INTEGER ? (WORD)iValue : 0;
m_dwMaxRuntimeMS = ::MsiRecordGetInteger(hRecord, 13);
if (m_dwMaxRuntimeMS == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
return S_OK;
}
UINT CMSITSCAOpCreateTask::SetTriggersFromView(MSIHANDLE hView)
{
for (;;) {
UINT uiResult;
PMSIHANDLE hRecord;
TASK_TRIGGER ttData;
ULONGLONG ullValue;
FILETIME ftValue;
SYSTEMTIME stValue;
int iValue, iStartTime, iStartTimeRand;
// Fetch one record from the view.
uiResult = ::MsiViewFetch(hView, &hRecord);
if (uiResult == ERROR_NO_MORE_ITEMS) return ERROR_SUCCESS;
else if (uiResult != ERROR_SUCCESS) return uiResult;
ZeroMemory(&ttData, sizeof(TASK_TRIGGER));
ttData.cbTriggerSize = sizeof(TASK_TRIGGER);
// Get StartDate.
iValue = ::MsiRecordGetInteger(hRecord, 2);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ullValue = ((ULONGLONG)iValue + 138426) * 864000000000;
ftValue.dwHighDateTime = ullValue >> 32;
ftValue.dwLowDateTime = ullValue & 0xffffffff;
if (!::FileTimeToSystemTime(&ftValue, &stValue))
return ::GetLastError();
ttData.wBeginYear = stValue.wYear;
ttData.wBeginMonth = stValue.wMonth;
ttData.wBeginDay = stValue.wDay;
// Get EndDate.
iValue = ::MsiRecordGetInteger(hRecord, 3);
if (iValue != MSI_NULL_INTEGER) {
ullValue = ((ULONGLONG)iValue + 138426) * 864000000000;
ftValue.dwHighDateTime = ullValue >> 32;
ftValue.dwLowDateTime = ullValue & 0xffffffff;
if (!::FileTimeToSystemTime(&ftValue, &stValue))
return ::GetLastError();
ttData.wEndYear = stValue.wYear;
ttData.wEndMonth = stValue.wMonth;
ttData.wEndDay = stValue.wDay;
ttData.rgFlags |= TASK_TRIGGER_FLAG_HAS_END_DATE;
}
// Get StartTime and StartTimeRand.
iStartTime = ::MsiRecordGetInteger(hRecord, 4);
if (iStartTime == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
iStartTimeRand = ::MsiRecordGetInteger(hRecord, 5);
if (iStartTimeRand != MSI_NULL_INTEGER) {
// Add random delay to StartTime.
iStartTime += ::MulDiv(rand(), iStartTimeRand, RAND_MAX);
}
ttData.wStartHour = (WORD)(iStartTime / 60);
ttData.wStartMinute = (WORD)(iStartTime % 60);
// Get MinutesDuration.
iValue = ::MsiRecordGetInteger(hRecord, 6);
ttData.MinutesDuration = iValue != MSI_NULL_INTEGER ? iValue : 0;
// Get MinutesInterval.
iValue = ::MsiRecordGetInteger(hRecord, 7);
ttData.MinutesInterval = iValue != MSI_NULL_INTEGER ? iValue : 0;
// Get Flags.
iValue = ::MsiRecordGetInteger(hRecord, 8);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.rgFlags |= iValue & ~TASK_TRIGGER_FLAG_HAS_END_DATE;
// Get Type.
iValue = ::MsiRecordGetInteger(hRecord, 9);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.TriggerType = (TASK_TRIGGER_TYPE)iValue;
switch (ttData.TriggerType) {
case TASK_TIME_TRIGGER_DAILY:
// Get DaysInterval.
iValue = ::MsiRecordGetInteger(hRecord, 10);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.Type.Daily.DaysInterval = (WORD)iValue;
break;
case TASK_TIME_TRIGGER_WEEKLY:
// Get WeeksInterval.
iValue = ::MsiRecordGetInteger(hRecord, 11);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.Type.Weekly.WeeksInterval = (WORD)iValue;
// Get DaysOfTheWeek.
iValue = ::MsiRecordGetInteger(hRecord, 12);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.Type.Weekly.rgfDaysOfTheWeek = (WORD)iValue;
break;
case TASK_TIME_TRIGGER_MONTHLYDATE:
// Get DaysOfMonth.
iValue = ::MsiRecordGetInteger(hRecord, 13);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.Type.MonthlyDate.rgfDays = (WORD)iValue;
// Get MonthsOfYear.
iValue = ::MsiRecordGetInteger(hRecord, 15);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.Type.MonthlyDate.rgfMonths = (WORD)iValue;
break;
case TASK_TIME_TRIGGER_MONTHLYDOW:
// Get WeekOfMonth.
iValue = ::MsiRecordGetInteger(hRecord, 14);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.Type.MonthlyDOW.wWhichWeek = (WORD)iValue;
// Get DaysOfTheWeek.
iValue = ::MsiRecordGetInteger(hRecord, 12);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.Type.MonthlyDOW.rgfDaysOfTheWeek = (WORD)iValue;
// Get MonthsOfYear.
iValue = ::MsiRecordGetInteger(hRecord, 15);
if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
ttData.Type.MonthlyDOW.rgfMonths = (WORD)iValue;
break;
}
m_lTriggers.AddTail(ttData);
}
return S_OK;
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpDeleteTask
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpDeleteTask::CMSITSCAOpDeleteTask(LPCWSTR pszTaskName) :
CMSITSCAOpSingleStringOperation(pszTaskName)
{
}
HRESULT CMSITSCAOpDeleteTask::Execute(CMSITSCASession *pSession)
{
assert(pSession);
if (pSession->m_bRollbackEnabled) {
HRESULT hr;
CComPtr<ITask> pTask;
DWORD dwFlags;
CString sDisplayNameOrig;
UINT uiCount = 0;
// Load the task.
hr = pSession->m_pTaskScheduler->Activate(m_sValue, IID_ITask, (IUnknown**)&pTask);
if (FAILED(hr)) return hr;
// Disable the task.
hr = pTask->GetFlags(&dwFlags);
if (FAILED(hr)) return hr;
if ((dwFlags & TASK_FLAG_DISABLED) == 0) {
// The task is enabled.
// In case the task disabling fails halfway, try to re-enable it anyway on rollback.
pSession->m_olRollback.AddHead(new CMSITSCAOpEnableTask(m_sValue, TRUE));
// Disable it.
dwFlags |= TASK_FLAG_DISABLED;
hr = pTask->SetFlags(dwFlags);
if (FAILED(hr)) return hr;
}
// Prepare a backup copy of task.
do {
sDisplayNameOrig.Format(L"%ls (orig %u)", (LPCWSTR)m_sValue, ++uiCount);
hr = pSession->m_pTaskScheduler->AddWorkItem(sDisplayNameOrig, pTask);
} while (hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS));
// In case the backup copy creation failed halfway, try to delete its remains anyway on rollback.
pSession->m_olRollback.AddHead(new CMSITSCAOpDeleteTask(sDisplayNameOrig));
if (FAILED(hr)) return hr;
// Save the backup copy.
CComQIPtr<IPersistFile> pTaskFile(pTask);
hr = pTaskFile->Save(NULL, TRUE);
if (FAILED(hr)) return hr;
// Order rollback action to restore from backup copy.
pSession->m_olRollback.AddHead(new CMSITSCAOpCopyTask(sDisplayNameOrig, m_sValue));
// Delete it.
hr = pSession->m_pTaskScheduler->Delete(m_sValue);
if (FAILED(hr)) return hr;
// Order commit action to delete backup copy.
pSession->m_olCommit.AddTail(new CMSITSCAOpDeleteTask(sDisplayNameOrig));
return S_OK;
} else {
// Delete the task.
return pSession->m_pTaskScheduler->Delete(m_sValue);
}
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpEnableTask
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpEnableTask::CMSITSCAOpEnableTask(LPCWSTR pszTaskName, BOOL bEnable) :
m_bEnable(bEnable),
CMSITSCAOpSingleStringOperation(pszTaskName)
{
}
HRESULT CMSITSCAOpEnableTask::Execute(CMSITSCASession *pSession)
{
assert(pSession);
HRESULT hr;
CComPtr<ITask> pTask;
// Load the task.
hr = pSession->m_pTaskScheduler->Activate(m_sValue, IID_ITask, (IUnknown**)&pTask);
if (SUCCEEDED(hr)) {
DWORD dwFlags;
// Get task's current flags.
hr = pTask->GetFlags(&dwFlags);
if (SUCCEEDED(hr)) {
// Modify flags.
if (m_bEnable) {
if (pSession->m_bRollbackEnabled && (dwFlags & TASK_FLAG_DISABLED)) {
// The task is disabled and supposed to be enabled.
// However, we shall enable it at commit to prevent it from accidentally starting in the very installation phase.
pSession->m_olCommit.AddTail(new CMSITSCAOpEnableTask(m_sValue, TRUE));
return S_OK;
} else
dwFlags &= ~TASK_FLAG_DISABLED;
} else {
if (pSession->m_bRollbackEnabled && !(dwFlags & TASK_FLAG_DISABLED)) {
// The task is enabled and we will disable it now.
// Order rollback to re-enable it.
pSession->m_olRollback.AddHead(new CMSITSCAOpEnableTask(m_sValue, TRUE));
}
dwFlags |= TASK_FLAG_DISABLED;
}
// Set flags.
hr = pTask->SetFlags(dwFlags);
if (SUCCEEDED(hr)) {
// Save the task.
CComQIPtr<IPersistFile> pTaskFile(pTask);
hr = pTaskFile->Save(NULL, TRUE);
}
}
}
return hr;
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpCopyTask
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpCopyTask::CMSITSCAOpCopyTask(LPCWSTR pszTaskSrc, LPCWSTR pszTaskDst) :
CMSITSCAOpSrcDstStringOperation(pszTaskSrc, pszTaskDst)
{
}
HRESULT CMSITSCAOpCopyTask::Execute(CMSITSCASession *pSession)
{
assert(pSession);
HRESULT hr;
CComPtr<ITask> pTask;
// Load the source task.
hr = pSession->m_pTaskScheduler->Activate(m_sValue1, IID_ITask, (IUnknown**)&pTask);
if (FAILED(hr)) return hr;
// Add with different name.
hr = pSession->m_pTaskScheduler->AddWorkItem(m_sValue2, pTask);
if (pSession->m_bRollbackEnabled) {
// Order rollback action to delete the new copy. ITask::AddWorkItem() might made a blank task already.
pSession->m_olRollback.AddHead(new CMSITSCAOpDeleteTask(m_sValue2));
}
if (FAILED(hr)) return hr;
// Save the task.
CComQIPtr<IPersistFile> pTaskFile(pTask);
hr = pTaskFile->Save(NULL, TRUE);
if (FAILED(hr)) return hr;
return S_OK;
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpList
////////////////////////////////////////////////////////////////////////////
CMSITSCAOpList::CMSITSCAOpList() :
CAtlList<CMSITSCAOp*>(sizeof(CMSITSCAOp*))
{
}
void CMSITSCAOpList::Free()
{
POSITION pos;
for (pos = GetHeadPosition(); pos;) {
CMSITSCAOp *pOp = GetNext(pos);
CMSITSCAOpList *pOpList = dynamic_cast<CMSITSCAOpList*>(pOp);
if (pOpList) {
// Recursivelly free sublists.
pOpList->Free();
}
delete pOp;
}
RemoveAll();
}
HRESULT CMSITSCAOpList::LoadFromFile(LPCTSTR pszFileName)
{
assert(pszFileName);
HRESULT hr;
CAtlFile fSequence;
hr = fSequence.Create(pszFileName, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN);
if (FAILED(hr)) return hr;
// Load operation sequence.
return fSequence >> *this;
}
HRESULT CMSITSCAOpList::SaveToFile(LPCTSTR pszFileName) const
{
assert(pszFileName);
HRESULT hr;
CAtlFile fSequence;
hr = fSequence.Create(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN);
if (FAILED(hr)) return hr;
// Save execute sequence to file.
hr = fSequence << *this;
fSequence.Close();
if (FAILED(hr)) ::DeleteFile(pszFileName);
return hr;
}
HRESULT CMSITSCAOpList::Execute(CMSITSCASession *pSession)
{
assert(pSession);
POSITION pos;
HRESULT hr;
for (pos = GetHeadPosition(); pos;) {
hr = GetNext(pos)->Execute(pSession);
if (!pSession->m_bContinueOnError && FAILED(hr)) return hr;
}
return S_OK;
}
////////////////////////////////////////////////////////////////////////////
// CMSITSCASession
////////////////////////////////////////////////////////////////////////////
CMSITSCASession::CMSITSCASession() :
m_bContinueOnError(FALSE),
m_bRollbackEnabled(FALSE)
{
}
CMSITSCASession::~CMSITSCASession()
{
}
HRESULT CMSITSCASession::Initialize()
{
return m_pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL);
}