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.
This commit is contained in:
Simon Rozman 2012-12-21 14:52:17 +00:00
parent 174c96ee60
commit 59e983d8fc
5 changed files with 1166 additions and 808 deletions

View File

@ -66,7 +66,7 @@ Vse :: "$(JEZIK).$(CFG).$(PLAT).ActionText-2.idt"
Action Description Template Action Description Template
s$(MSI_TIP_ID) L0 L0 s$(MSI_TIP_ID) L0 L0
1250 ActionText Action 1250 ActionText Action
caInstallScheduledTasks Registracija razporejenih opravil Opravilo: [1] InstallScheduledTasks Registracija razporejenih opravil Opravilo: [1]
<<NOKEEP <<NOKEEP
"De.$(CFG).$(PLAT).ActionText-2.idt" : "Sl.$(CFG).$(PLAT).ActionText-2.idtx" "..\res\de_DE.po" "De.$(CFG).$(PLAT).ActionText-2.idt" : "Sl.$(CFG).$(PLAT).ActionText-2.idtx" "..\res\de_DE.po"
@ -113,10 +113,10 @@ Vse :: "$(JEZIK).$(CFG).$(PLAT).CustomAction-1.idt"
Action Type Source Target Action Type Source Target
s$(MSI_TIP_ID) i2 S$(MSI_TIP_ID) S255 s$(MSI_TIP_ID) i2 S$(MSI_TIP_ID) S255
CustomAction Action CustomAction Action
caEvaluateScheduledTasks 1 binMSITSCA.dll EvaluateScheduledTasks EvaluateScheduledTasks 1 binMSITSCA.dll EvaluateScheduledTasks
caInstallScheduledTasks 1025 binMSITSCA.dll InstallScheduledTasks InstallScheduledTasks 1025 binMSITSCA.dll InstallScheduledTasks
caRollbackScheduledTasks 1281 binMSITSCA.dll FinalizeScheduledTasks RollbackScheduledTasks 1281 binMSITSCA.dll InstallScheduledTasks
caCommitScheduledTasks 1537 binMSITSCA.dll FinalizeScheduledTasks CommitScheduledTasks 1537 binMSITSCA.dll InstallScheduledTasks
<<NOKEEP <<NOKEEP
@ -131,6 +131,10 @@ Vse :: "$(JEZIK).$(CFG).$(PLAT).Error-2.idt"
Error Message Error Message
i2 L0 i2 L0
1250 Error Error 1250 Error Error
2550 Pri odpiranju namestitvenega paketa je prišlo do napake.
2551 Pri pripravi seznama razvršèenih opravil je prišlo do napake [2].
2552 Pri pisanju v datoteko seznama razporejenih opravil »[2]« je prišlo do napake [3].
2553 Pri nastavljanju parametra »[2]« je prišlo do napake [3].
<<NOKEEP <<NOKEEP
"De.$(CFG).$(PLAT).Error-2.idt" : "Sl.$(CFG).$(PLAT).Error-2.idtx" "..\res\de_DE.po" "De.$(CFG).$(PLAT).Error-2.idt" : "Sl.$(CFG).$(PLAT).Error-2.idtx" "..\res\de_DE.po"
@ -157,10 +161,10 @@ Vse :: "$(JEZIK).$(CFG).$(PLAT).InstallExecuteSequence-1.idt"
Action Condition Sequence Action Condition Sequence
s$(MSI_TIP_ID) S255 I2 s$(MSI_TIP_ID) S255 I2
InstallExecuteSequence Action InstallExecuteSequence Action
caEvaluateScheduledTasks 6580 EvaluateScheduledTasks 6580
caInstallScheduledTasks 6581 InstallScheduledTasks 6581
caRollbackScheduledTasks 6582 RollbackScheduledTasks 6582
caCommitScheduledTasks 6583 CommitScheduledTasks 6583
<<NOKEEP <<NOKEEP

View File

@ -8,15 +8,7 @@
// Local constants // Local constants
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
#define MSITSCA_TASK_TICK_SIZE 2048 #define MSITSCA_TASK_TICK_SIZE (10*1024*1024)
////////////////////////////////////////////////////////////////////////////
// Local function declarations
////////////////////////////////////////////////////////////////////////////
inline UINT MsiFormatAndStoreString(MSIHANDLE hInstall, MSIHANDLE hRecord, unsigned int iField, CAtlFile &fSequence, CStringW &sValue);
inline UINT MsiStoreInteger(MSIHANDLE hRecord, unsigned int iField, BOOL bIsNullable, CAtlFile &fSequence, int *piValue = NULL);
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@ -49,489 +41,276 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpRes
UINT MSITSCA_API EvaluateScheduledTasks(MSIHANDLE hInstall) UINT MSITSCA_API EvaluateScheduledTasks(MSIHANDLE hInstall)
{ {
UNREFERENCED_PARAMETER(hInstall); UINT uiResult;
HRESULT hr; HRESULT hr;
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL)); BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
CString sSequenceFilename, sComponent; CMSITSCAOpList olExecute;
CStringW sValue;
CAtlFile fSequence;
PMSIHANDLE hDatabase, hViewST;
PMSIHANDLE hRecordProg = ::MsiCreateRecord(2);
BOOL bRollbackEnabled; BOOL bRollbackEnabled;
PMSIHANDLE
hDatabase,
hRecordProg = ::MsiCreateRecord(3);
CString sValue;
assert(hRecordProg); assert(hRecordProg);
assert(0); // Attach debugger here, or press "Ignore"! assert(0); // Attach debugger here, or press "Ignore"!
// The amount of tick space to add for each task to progress indicator. // Check and add the rollback enabled state.
verify(::MsiRecordSetInteger(hRecordProg, 1, 3) == ERROR_SUCCESS); uiResult = ::MsiGetProperty(hInstall, _T("RollbackDisabled"), sValue);
verify(::MsiRecordSetInteger(hRecordProg, 2, MSITSCA_TASK_TICK_SIZE) == ERROR_SUCCESS); bRollbackEnabled = uiResult == ERROR_SUCCESS ?
// Prepare our own sequence script.
{
LPTSTR szBuffer = sSequenceFilename.GetBuffer(MAX_PATH);
assert(szBuffer);
::GetTempPath(MAX_PATH, szBuffer);
::GetTempFileName(szBuffer, _T("TS"), 0, szBuffer);
sSequenceFilename.ReleaseBuffer();
}
hr = fSequence.Create(sSequenceFilename, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN);
if (FAILED(hr)) goto error1;
// Save the rollback enabled state.
hr = HRESULT_FROM_WIN32(::MsiGetProperty(hInstall, _T("RollbackDisabled"), sValue));
bRollbackEnabled = SUCCEEDED(hr) ?
_wtoi(sValue) || !sValue.IsEmpty() && towlower(sValue.GetAt(0)) == L'y' ? FALSE : TRUE : _wtoi(sValue) || !sValue.IsEmpty() && towlower(sValue.GetAt(0)) == L'y' ? FALSE : TRUE :
TRUE; TRUE;
hr = fSequence << (int)bRollbackEnabled; olExecute.AddTail(new CMSITSCAOpEnableRollback(bRollbackEnabled));
// Open MSI database.
hDatabase = ::MsiGetActiveDatabase(hInstall); hDatabase = ::MsiGetActiveDatabase(hInstall);
if (!hDatabase) { hr = E_INVALIDARG; goto error2; } if (hDatabase) {
// Check if ScheduledTask table exists. If it doesn't exist, there's nothing to do.
MSICONDITION condition = ::MsiDatabaseIsTablePersistent(hDatabase, _T("ScheduledTask"));
if (condition == MSICONDITION_FALSE || condition == MSICONDITION_TRUE) {
PMSIHANDLE hViewST;
// Execute a query to get tasks. // Prepare a query to get a list/view of tasks.
hr = HRESULT_FROM_WIN32(::MsiDatabaseOpenView(hDatabase, _T("SELECT Task,DisplayName,Application,Parameters,WorkingDir,Flags,Priority,User,Password,Description,IdleMin,IdleDeadline,MaxRuntime,Condition,Component_ FROM ScheduledTask"), &hViewST)); uiResult = ::MsiDatabaseOpenView(hDatabase, _T("SELECT Task,DisplayName,Application,Parameters,WorkingDir,Flags,Priority,User,Password,Description,IdleMin,IdleDeadline,MaxRuntime,Condition,Component_ FROM ScheduledTask"), &hViewST);
if (FAILED(hr)) goto error2; if (uiResult == ERROR_SUCCESS) {
hr = HRESULT_FROM_WIN32(::MsiViewExecute(hViewST, NULL)); // Execute query!
if (FAILED(hr)) goto error2; uiResult = ::MsiViewExecute(hViewST, NULL);
if (uiResult == ERROR_SUCCESS) {
//CString sComponent;
CStringW sDisplayName;
for (;;) { for (;;) {
PMSIHANDLE hRecord, hViewTT; PMSIHANDLE hRecord;
INSTALLSTATE iInstalled, iAction; INSTALLSTATE iInstalled, iAction;
MSICONDITION condition;
ULONGLONG posTriggerCount, posEOF;
UINT nTriggers = 0;
// Fetch one record from the view. // Fetch one record from the view.
hr = HRESULT_FROM_WIN32(::MsiViewFetch(hViewST, &hRecord)); uiResult = ::MsiViewFetch(hViewST, &hRecord);
if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) { if (uiResult == ERROR_NO_MORE_ITEMS) {
hr = S_OK; uiResult = ERROR_SUCCESS;
break; break;
} else if (FAILED(hr)) } else if (uiResult != ERROR_SUCCESS)
break; break;
// Read and evaluate condition. // Read and evaluate task's condition.
hr = HRESULT_FROM_WIN32(::MsiRecordGetString(hRecord, 14, sValue)); uiResult = ::MsiRecordGetString(hRecord, 14, sValue);
if (FAILED(hr)) break; if (uiResult != ERROR_SUCCESS) break; // TODO: If condition is empty string ommit evaluation.
condition = ::MsiEvaluateCondition(hInstall, sValue); condition = ::MsiEvaluateCondition(hInstall, sValue);
if (condition == MSICONDITION_FALSE) if (condition == MSICONDITION_FALSE)
continue; continue;
else if (condition == MSICONDITION_ERROR) { else if (condition == MSICONDITION_ERROR) {
hr = HRESULT_FROM_WIN32(ERROR_INVALID_FIELD); uiResult = ERROR_INVALID_FIELD;
break; break;
} }
// Read Component ID. // Read task's Component ID.
hr = HRESULT_FROM_WIN32(::MsiRecordGetString(hRecord, 15, sComponent)); uiResult = ::MsiRecordGetString(hRecord, 15, sValue);
if (FAILED(hr)) break; if (uiResult != ERROR_SUCCESS) break;
// Get component state and save for deferred processing. // Get the component state.
hr = HRESULT_FROM_WIN32(::MsiGetComponentState(hInstall, sComponent, &iInstalled, &iAction)); uiResult = ::MsiGetComponentState(hInstall, sValue, &iInstalled, &iAction);
if (FAILED(hr)) break; if (uiResult != ERROR_SUCCESS) break;
hr = fSequence << (int)iInstalled;
if (FAILED(hr)) break;
hr = fSequence << (int)iAction;
if (FAILED(hr)) break;
// Read and store task's data to script file. // Get task's DisplayName.
hr = HRESULT_FROM_WIN32(::MsiFormatAndStoreString(hInstall, hRecord, 2, fSequence, sValue)); // DisplayName uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 2, sDisplayName);
if (FAILED(hr)) break;
if (iAction >= INSTALLSTATE_LOCAL) {
hr = HRESULT_FROM_WIN32(::MsiFormatAndStoreString(hInstall, hRecord, 3, fSequence, sValue)); // Application
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiFormatAndStoreString(hInstall, hRecord, 4, fSequence, sValue)); // Parameters
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiFormatAndStoreString(hInstall, hRecord, 5, fSequence, sValue)); // WorkingDir
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger( hRecord, 6, FALSE, fSequence )); // Flags
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger( hRecord, 7, FALSE, fSequence )); // Priority
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiFormatAndStoreString(hInstall, hRecord, 8, fSequence, sValue)); // User
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiFormatAndStoreString(hInstall, hRecord, 9, fSequence, sValue)); // Password
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiFormatAndStoreString(hInstall, hRecord, 10, fSequence, sValue)); // Description
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger( hRecord, 11, TRUE, fSequence )); // IdleMin
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger( hRecord, 12, TRUE, fSequence )); // IdleDeadline
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger( hRecord, 13, FALSE, fSequence )); // MaxRuntime
if (FAILED(hr)) break;
// Write 0 for the number of triggers and store file position to update this count later. if (iAction >= INSTALLSTATE_LOCAL) {
verify(SUCCEEDED(fSequence.GetPosition(posTriggerCount))); // Installing component. Add the task.
hr = fSequence << (int)nTriggers; PMSIHANDLE hViewTT;
if (FAILED(hr)) break; CMSITSCAOpCreateTask *opCreateTask = new CMSITSCAOpCreateTask(sDisplayName);
assert(opCreateTask);
// Perform another query to get task's triggers. // Populate the operation with task's data.
hr = HRESULT_FROM_WIN32(::MsiDatabaseOpenView(hDatabase, _T("SELECT Trigger,BeginDate,EndDate,StartTime,StartTimeRand,MinutesDuration,MinutesInterval,Flags,Type,DaysInterval,WeeksInterval,DaysOfTheWeek,DaysOfMonth,WeekOfMonth,MonthsOfYear FROM TaskTrigger WHERE Task_=?"), &hViewTT)); opCreateTask->SetFromRecord(hInstall, hRecord);
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiViewExecute(hViewTT, hRecord));
if (FAILED(hr)) break;
for (;; nTriggers++) { // Perform another query to get task's triggers.
PMSIHANDLE hRecord; uiResult = ::MsiDatabaseOpenView(hDatabase, _T("SELECT Trigger,BeginDate,EndDate,StartTime,StartTimeRand,MinutesDuration,MinutesInterval,Flags,Type,DaysInterval,WeeksInterval,DaysOfTheWeek,DaysOfMonth,WeekOfMonth,MonthsOfYear FROM TaskTrigger WHERE Task_=?"), &hViewTT);
int iStartTime, iStartTimeRand; if (uiResult != ERROR_SUCCESS) break;
// Fetch one record from the view. // Execute query!
hr = HRESULT_FROM_WIN32(::MsiViewFetch(hViewTT, &hRecord)); uiResult = ::MsiViewExecute(hViewTT, hRecord);
if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) { if (uiResult == ERROR_SUCCESS) {
hr = S_OK; // Populate trigger list.
break; uiResult = opCreateTask->SetTriggersFromView(hViewTT);
} else if (FAILED(hr)) verify(::MsiViewClose(hViewTT) == ERROR_SUCCESS);
break; if (uiResult != ERROR_SUCCESS) break;
} else
break;
// Read StartTime and StartTimeRand olExecute.AddTail(opCreateTask);
iStartTime = ::MsiRecordGetInteger(hRecord, 4); } else {
if (iStartTime == MSI_NULL_INTEGER) { // Removing component. Remove the task.
hr = HRESULT_FROM_WIN32(ERROR_INVALID_FIELD); olExecute.AddTail(new CMSITSCAOpDeleteTask(sDisplayName));
break; }
// The amount of tick space to add for each task to progress indicator.
verify(::MsiRecordSetInteger(hRecordProg, 1, 3 ) == ERROR_SUCCESS);
verify(::MsiRecordSetInteger(hRecordProg, 2, MSITSCA_TASK_TICK_SIZE) == ERROR_SUCCESS);
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { uiResult = ERROR_INSTALL_USEREXIT; break; }
}
verify(::MsiViewClose(hViewST) == ERROR_SUCCESS);
if (SUCCEEDED(uiResult)) {
CString sSequenceFilename;
CAtlFile fSequence;
// Prepare our own sequence script file.
// The InstallScheduledTasks is a deferred custom action, thus all this information will be unavailable to it.
// Therefore save all required info to file now.
{
LPTSTR szBuffer = sSequenceFilename.GetBuffer(MAX_PATH);
assert(szBuffer);
::GetTempPath(MAX_PATH, szBuffer);
::GetTempFileName(szBuffer, _T("TS"), 0, szBuffer);
sSequenceFilename.ReleaseBuffer();
}
// Save execute sequence to file.
hr = olExecute.SaveToFile(sSequenceFilename);
if (SUCCEEDED(hr)) {
// Store sequence script file names to properties for deferred custiom actions.
uiResult = ::MsiSetProperty(hInstall, _T("InstallScheduledTasks"), sSequenceFilename);
if (uiResult == ERROR_SUCCESS) {
LPCTSTR pszExtension = ::PathFindExtension(sSequenceFilename);
CString sSequenceFilename2;
sSequenceFilename2.Format(_T("%.*ls-rb%ls"), pszExtension - (LPCTSTR)sSequenceFilename, (LPCTSTR)sSequenceFilename, pszExtension);
uiResult = ::MsiSetProperty(hInstall, _T("RollbackScheduledTasks"), sSequenceFilename2);
if (uiResult == ERROR_SUCCESS) {
sSequenceFilename2.Format(_T("%.*ls-cm%ls"), pszExtension - (LPCTSTR)sSequenceFilename, (LPCTSTR)sSequenceFilename, pszExtension);
uiResult = ::MsiSetProperty(hInstall, _T("CommitScheduledTasks"), sSequenceFilename2);
if (uiResult != ERROR_SUCCESS) {
verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_SCHEDULED_TASKS_PROPERTY_SET) == ERROR_SUCCESS);
verify(::MsiRecordSetString (hRecordProg, 2, _T("CommitScheduledTasks") ) == ERROR_SUCCESS);
verify(::MsiRecordSetInteger(hRecordProg, 3, uiResult ) == ERROR_SUCCESS);
::MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
}
} else {
verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_SCHEDULED_TASKS_PROPERTY_SET) == ERROR_SUCCESS);
verify(::MsiRecordSetString (hRecordProg, 2, _T("RollbackScheduledTasks") ) == ERROR_SUCCESS);
verify(::MsiRecordSetInteger(hRecordProg, 3, uiResult ) == ERROR_SUCCESS);
::MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
}
} else {
verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_SCHEDULED_TASKS_PROPERTY_SET) == ERROR_SUCCESS);
verify(::MsiRecordSetString (hRecordProg, 2, _T("InstallScheduledTasks") ) == ERROR_SUCCESS);
verify(::MsiRecordSetInteger(hRecordProg, 3, uiResult ) == ERROR_SUCCESS);
::MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
}
if (uiResult != ERROR_SUCCESS) ::DeleteFile(sSequenceFilename);
} else {
uiResult = ERROR_INSTALL_SCHEDULED_TASKS_SCRIPT_WRITE;
verify(::MsiRecordSetInteger(hRecordProg, 1, uiResult ) == ERROR_SUCCESS);
verify(::MsiRecordSetString (hRecordProg, 2, sSequenceFilename) == ERROR_SUCCESS);
verify(::MsiRecordSetInteger(hRecordProg, 3, hr ) == ERROR_SUCCESS);
::MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
}
} else if (uiResult != ERROR_INSTALL_USEREXIT) {
verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_SCHEDULED_TASKS_OPLIST_CREATE) == ERROR_SUCCESS);
verify(::MsiRecordSetInteger(hRecordProg, 2, uiResult ) == ERROR_SUCCESS);
::MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
}
} else {
verify(::MsiRecordSetInteger(hRecordProg, 1, uiResult) == ERROR_SUCCESS);
::MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
} }
iStartTimeRand = ::MsiRecordGetInteger(hRecord, 5); } else {
if (iStartTimeRand != MSI_NULL_INTEGER) { verify(::MsiRecordSetInteger(hRecordProg, 1, uiResult) == ERROR_SUCCESS);
// Add random delay to StartTime. ::MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
iStartTime += ::MulDiv(rand(), iStartTimeRand, RAND_MAX);
}
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 2, FALSE, fSequence)); // BeginDate
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 3, TRUE, fSequence)); // EndDate
if (FAILED(hr)) break;
hr = fSequence << iStartTime; // StartTime
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 6, TRUE, fSequence)); // MinutesDuration
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 7, TRUE, fSequence)); // MinutesInterval
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 8, FALSE, fSequence)); // Flags
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 9, FALSE, fSequence)); // Type
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 10, TRUE, fSequence)); // DaysInterval
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 11, TRUE, fSequence)); // WeeksInterval
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 12, TRUE, fSequence)); // DaysOfTheWeek
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 13, TRUE, fSequence)); // DaysOfMonth
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 14, TRUE, fSequence)); // WeekOfMonth
if (FAILED(hr)) break;
hr = HRESULT_FROM_WIN32(::MsiStoreInteger(hRecord, 15, TRUE, fSequence)); // MonthsOfYear
if (FAILED(hr)) break;
} }
verify(::MsiViewClose(hViewTT) == ERROR_SUCCESS);
if (FAILED(hr)) break;
// Seek back to update actual trigger count.
verify(SUCCEEDED(fSequence.GetPosition(posEOF)));
verify(SUCCEEDED(fSequence.Seek(posTriggerCount, FILE_BEGIN)));
fSequence << nTriggers;
verify(SUCCEEDED(fSequence.Seek(posEOF, FILE_BEGIN)));
} }
} else {
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { hr = E_ABORT; goto error2; } uiResult = ERROR_INSTALL_SCHEDULED_TASKS_DATABASE_OPEN;
verify(::MsiRecordSetInteger(hRecordProg, 1, uiResult) == ERROR_SUCCESS);
::MsiProcessMessage(hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
} }
verify(::MsiViewClose(hViewST) == ERROR_SUCCESS); olExecute.Free();
if (FAILED(hr)) goto error2;
// Store sequence script file names for deferred custiom actions.
hr = HRESULT_FROM_WIN32(::MsiSetProperty(hInstall, _T("caInstallScheduledTasks"), sSequenceFilename));
if (FAILED(hr)) goto error2;
{
LPCTSTR pszExtension = ::PathFindExtension(sSequenceFilename);
CString sSequenceFilename2;
sSequenceFilename2.Format(_T("%.*ls-rb%ls"), pszExtension - (LPCTSTR)sSequenceFilename, (LPCTSTR)sSequenceFilename, pszExtension);
hr = HRESULT_FROM_WIN32(::MsiSetProperty(hInstall, _T("caRollbackScheduledTasks"), sSequenceFilename2));
if (FAILED(hr)) goto error2;
sSequenceFilename2.Format(_T("%.*ls-cm%ls"), pszExtension - (LPCTSTR)sSequenceFilename, (LPCTSTR)sSequenceFilename, pszExtension);
hr = HRESULT_FROM_WIN32(::MsiSetProperty(hInstall, _T("caCommitScheduledTasks"), sSequenceFilename2));
if (FAILED(hr)) goto error2;
}
hr = S_OK;
error2:
fSequence.Close();
error1:
if (FAILED(hr)) ::DeleteFile(sSequenceFilename);
if (bIsCoInitialized) ::CoUninitialize(); if (bIsCoInitialized) ::CoUninitialize();
return SUCCEEDED(hr) ? ERROR_SUCCESS : hr == E_ABORT ? ERROR_INSTALL_USEREXIT : ERROR_INVALID_DATA; return uiResult;
} }
UINT MSITSCA_API InstallScheduledTasks(MSIHANDLE hInstall) UINT MSITSCA_API InstallScheduledTasks(MSIHANDLE hInstall)
{ {
assert(::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED)); UINT uiResult;
HRESULT hr; HRESULT hr;
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL)); BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
CString sSequenceFilename, sSequenceFilenameRB, sSequenceFilenameCM; CString sSequenceFilename;
LPCTSTR pszExtension;
CStringW sDisplayName;
CAtlFile fSequence, fSequence2;
CComPtr<ITaskScheduler> pTaskScheduler;
CMSITSCAOperationList lstOperationsRB, lstOperationsCM;
PMSIHANDLE hRecordAction = ::MsiCreateRecord(3), hRecordProg = ::MsiCreateRecord(3);
BOOL bRollbackEnabled;
assert(hRecordProg);
assert(0); // Attach debugger here, or press "Ignore"! assert(0); // Attach debugger here, or press "Ignore"!
// Tell the installer to use explicit progress messages. uiResult = ::MsiGetProperty(hInstall, _T("CustomActionData"), sSequenceFilename);
verify(::MsiRecordSetInteger(hRecordProg, 1, 1) == ERROR_SUCCESS); if (uiResult == ERROR_SUCCESS) {
verify(::MsiRecordSetInteger(hRecordProg, 2, 1) == ERROR_SUCCESS); CMSITSCAOpList lstOperations;
verify(::MsiRecordSetInteger(hRecordProg, 3, 0) == ERROR_SUCCESS);
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { hr = E_ABORT; goto error1; }
// Specify that an update of the progress bar's position in this case means to move it forward by one increment. // Load operation sequence.
verify(::MsiRecordSetInteger(hRecordProg, 1, 2) == ERROR_SUCCESS); hr = lstOperations.LoadFromFile(sSequenceFilename);
verify(::MsiRecordSetInteger(hRecordProg, 2, MSITSCA_TASK_TICK_SIZE) == ERROR_SUCCESS); if (SUCCEEDED(hr)) {
verify(::MsiRecordSetInteger(hRecordProg, 3, 0) == ERROR_SUCCESS); CMSITSCASession session;
BOOL bIsCleanup = ::MsiGetMode(hInstall, MSIRUNMODE_COMMIT) || ::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK);
// Determine sequence script file names. // In case of commit/rollback, continue sequence on error, to do as much cleanup as possible.
hr = HRESULT_FROM_WIN32(::MsiGetProperty(hInstall, _T("CustomActionData"), sSequenceFilename)); session.m_bContinueOnError = bIsCleanup;
if (FAILED(hr)) goto error1;
pszExtension = ::PathFindExtension(sSequenceFilename);
assert(pszExtension);
sSequenceFilenameRB.Format(_T("%.*ls-rb%ls"), pszExtension - (LPCTSTR)sSequenceFilename, (LPCTSTR)sSequenceFilename, pszExtension);
sSequenceFilenameCM.Format(_T("%.*ls-cm%ls"), pszExtension - (LPCTSTR)sSequenceFilename, (LPCTSTR)sSequenceFilename, pszExtension);
// Open sequence script file. // Get task scheduler object.
hr = fSequence.Create(sSequenceFilename, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN); hr = session.m_pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL);
if (FAILED(hr)) goto error2; if (SUCCEEDED(hr)) {
// Execute the operations.
hr = lstOperations.Execute(&session);
if (SUCCEEDED(hr)) {
if (!bIsCleanup && session.m_bRollbackEnabled) {
// Save cleanup scripts.
LPCTSTR pszExtension = ::PathFindExtension(sSequenceFilename);
CString sSequenceFilenameCM, sSequenceFilenameRB;
// Get rollback-enabled state. sSequenceFilenameRB.Format(_T("%.*ls-rb%ls"), pszExtension - (LPCTSTR)sSequenceFilename, (LPCTSTR)sSequenceFilename, pszExtension);
{ sSequenceFilenameCM.Format(_T("%.*ls-cm%ls"), pszExtension - (LPCTSTR)sSequenceFilename, (LPCTSTR)sSequenceFilename, pszExtension);
int iValue;
hr = fSequence >> iValue;
if (FAILED(hr)) goto error3;
bRollbackEnabled = iValue ? TRUE : FALSE;
}
hr = pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL); // After end of commit, delete rollback file too. After end of rollback, delete commit file too.
if (FAILED(hr)) goto error3; session.m_olCommit.AddTail(new CMSITSCAOpDeleteFile(sSequenceFilenameRB));
session.m_olRollback.AddTail(new CMSITSCAOpDeleteFile(sSequenceFilenameCM));
for (;;) { // Save commit file first.
INSTALLSTATE iInstalled, iAction; hr = session.m_olCommit.SaveToFile(sSequenceFilenameCM);
CComPtr<ITask> pTaskOrig; if (SUCCEEDED(hr)) {
// Save rollback file next.
hr = fSequence >> (int&)iInstalled; hr = session.m_olRollback.SaveToFile(sSequenceFilenameRB);
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) { if (SUCCEEDED(hr)) {
hr = S_OK; uiResult = ERROR_SUCCESS;
break; } else {
} else if (FAILED(hr)) goto error4; // Saving rollback file failed.
hr = fSequence >> (int&)iAction; uiResult = HRESULT_CODE(hr);
if (FAILED(hr)) goto error4; }
hr = fSequence >> sDisplayName; } else {
if (FAILED(hr)) goto error4; // Saving commit file failed.
uiResult = HRESULT_CODE(hr);
verify(::MsiRecordSetString(hRecordAction, 1, sDisplayName) == ERROR_SUCCESS); }
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecordAction) == IDCANCEL) { hr = E_ABORT; goto error1; } } else
uiResult = ERROR_SUCCESS;
// See if the task with this name already exists. } else {
hr = pTaskScheduler->Activate(sDisplayName, IID_ITask, (IUnknown**)&pTaskOrig); // Execution failed.
if (hr != COR_E_FILENOTFOUND && FAILED(hr)) goto error4; uiResult = HRESULT_CODE(hr);
else if (SUCCEEDED(hr)) {
// The task exists.
if (iInstalled < INSTALLSTATE_LOCAL || iAction < INSTALLSTATE_LOCAL) {
// This component either was, or is going to be uninstalled.
// In first case this task shouldn't exist. Perhaps it is from some failed previous install/uninstall attempt.
// In second case we should delete it.
// So delete it anyway!
DWORD dwFlags;
CStringW sDisplayNameOrig;
UINT uiCount = 0;
if (bRollbackEnabled) {
hr = pTaskOrig->GetFlags(&dwFlags);
if (FAILED(hr)) goto error4;
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.
lstOperationsRB.AddHead(new CMSITSCAOpEnableTask(sDisplayName, TRUE));
// Disable it.
dwFlags |= TASK_FLAG_DISABLED;
hr = pTaskOrig->SetFlags(dwFlags);
if (FAILED(hr)) goto error4;
}
// Prepare a backup copy of task.
do {
sDisplayNameOrig.Format(L"%ls (orig %u)", (LPCWSTR)sDisplayName, ++uiCount);
hr = pTaskScheduler->AddWorkItem(sDisplayNameOrig, pTaskOrig);
} while (hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS));
// In case the backup copy creation failed halfway, try to delete its remains anyway on rollback.
lstOperationsRB.AddHead(new CMSITSCAOpDeleteTask(sDisplayNameOrig));
if (FAILED(hr)) goto error4;
// Save the backup copy.
CComQIPtr<IPersistFile> pTaskFile(pTaskOrig);
hr = pTaskFile->Save(NULL, TRUE);
if (FAILED(hr)) goto error4;
// Order rollback action to restore from backup copy.
lstOperationsRB.AddHead(new CMSITSCAOpCopyTask(sDisplayNameOrig, sDisplayName));
} }
// Delete it. if (uiResult != ERROR_SUCCESS && !bIsCleanup) {
hr = pTaskScheduler->Delete(sDisplayName); // Perform the cleanup now, since rollback action might not get called at all (if scheduled later than this action).
if (FAILED(hr)) goto error4; session.m_bContinueOnError = TRUE;
session.m_bRollbackEnabled = FALSE;
if (bRollbackEnabled) { verify(SUCCEEDED(session.m_olRollback.Execute(&session)));
// Order commit action to delete backup copy.
lstOperationsCM.AddTail(new CMSITSCAOpDeleteTask(sDisplayNameOrig));
} }
} } else {
} // Task scheduler creation failed.
uiResult = HRESULT_CODE(hr);
if (iAction >= INSTALLSTATE_LOCAL) {
// The component is being installed.
CComPtr<ITask> pTask;
CComPtr<IPersistFile> pTaskFile;
if (bRollbackEnabled) {
// In case the task creation fails halfway, try to delete its remains anyway on rollback.
lstOperationsRB.AddHead(new CMSITSCAOpDeleteTask(sDisplayName));
}
// Create the new task.
hr = pTaskScheduler->NewWorkItem(sDisplayName, CLSID_CTask, IID_ITask, (IUnknown**)&pTask);
if (FAILED(hr)) goto error4;
// Load the task from sequence file.
hr = fSequence >> pTask;
if (FAILED(hr)) goto error4;
// Save the task.
hr = pTask.QueryInterface<IPersistFile>(&pTaskFile);
if (FAILED(hr)) goto error4;
hr = pTaskFile->Save(NULL, TRUE);
if (FAILED(hr)) goto error4;
}
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { hr = E_ABORT; goto error4; }
}
if (bRollbackEnabled) {
// Save commit script.
hr = fSequence2.Create(sSequenceFilenameCM, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN);
if (SUCCEEDED(hr)) {
// Save rollback filename too, since commit script should delete it.
hr = fSequence2 << sSequenceFilenameRB;
if (FAILED(hr)) goto error4;
// Save commit script.
hr = lstOperationsCM.Save(fSequence2);
if (FAILED(hr)) goto error4;
fSequence2.Close();
}
}
hr = S_OK;
error4:
if (bRollbackEnabled) {
if (SUCCEEDED(hr)) {
// This custom action completed successfully. Save rollback script in case of error.
if (SUCCEEDED(fSequence2.Create(sSequenceFilenameRB, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN))) {
// Save commit filename too, since rollback script should delete it.
if (SUCCEEDED(fSequence2 << sSequenceFilenameRB))
lstOperationsRB.Save(fSequence2);
fSequence2.Close();
} }
} else { } else {
// This very custom action encountered an error. Its rollback won't be called later (if scheduled later than this CA) so do the cleanup immediately. // Sequence loading failed. Don't panic => do nothing.
lstOperationsRB.Execute(pTaskScheduler, TRUE); uiResult = ERROR_SUCCESS;
::DeleteFile(sSequenceFilenameCM);
} }
}
lstOperationsRB.Free();
lstOperationsCM.Free();
error3:
fSequence.Close();
error2:
::DeleteFile(sSequenceFilename);
error1:
if (bIsCoInitialized) ::CoUninitialize();
return SUCCEEDED(hr) ? ERROR_SUCCESS : hr == E_ABORT ? ERROR_INSTALL_USEREXIT : ERROR_INVALID_DATA;
}
UINT MSITSCA_API FinalizeScheduledTasks(MSIHANDLE hInstall)
{
HRESULT hr;
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
CString sSequenceFilename, sSequenceFilename2;
CAtlFile fSequence;
CComPtr<ITaskScheduler> pTaskScheduler;
CMSITSCAOperationList lstOperations;
assert(0); // Attach debugger here, or press "Ignore"!
hr = HRESULT_FROM_WIN32(::MsiGetProperty(hInstall, _T("CustomActionData"), sSequenceFilename));
if (SUCCEEDED(hr)) {
// Open sequence script file.
hr = fSequence.Create(sSequenceFilename, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN);
if (SUCCEEDED(hr)) {
// Read "the other" script filename and delete it.
hr = fSequence >> sSequenceFilename2;
if (SUCCEEDED(hr)) {
::DeleteFile(sSequenceFilename2);
// Get task scheduler object.
hr = pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL);
if (SUCCEEDED(hr)) {
// Load operation sequence.
hr = lstOperations.Load(fSequence);
if (SUCCEEDED(hr)) {
// Execute the cleanup operations.
hr = lstOperations.Execute(pTaskScheduler, TRUE);
}
lstOperations.Free();
}
}
fSequence.Close();
} else
hr = S_OK;
lstOperations.Free();
::DeleteFile(sSequenceFilename); ::DeleteFile(sSequenceFilename);
} else {
// Couldn't get CustomActionData property.
} }
if (bIsCoInitialized) ::CoUninitialize(); if (bIsCoInitialized) ::CoUninitialize();
return SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INVALID_DATA; return uiResult;
}
////////////////////////////////////////////////////////////////////////////
// Local functions
////////////////////////////////////////////////////////////////////////////
inline UINT MsiFormatAndStoreString(MSIHANDLE hInstall, MSIHANDLE hRecord, unsigned int iField, CAtlFile &fSequence, CStringW &sValue)
{
UINT uiResult;
uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, iField, sValue);
if (uiResult != ERROR_SUCCESS) return uiResult;
if (FAILED(fSequence << sValue)) return ERROR_WRITE_FAULT;
return ERROR_SUCCESS;
}
inline UINT MsiStoreInteger(MSIHANDLE hRecord, unsigned int iField, BOOL bIsNullable, CAtlFile &fSequence, int *piValue)
{
int iValue;
iValue = ::MsiRecordGetInteger(hRecord, iField);
if (!bIsNullable && iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
if (FAILED(fSequence << iValue)) return ERROR_WRITE_FAULT;
if (piValue) *piValue = iValue;
return ERROR_SUCCESS;
} }

View File

@ -39,6 +39,11 @@
#define MSITSCA_API #define MSITSCA_API
#endif #endif
#define ERROR_INSTALL_SCHEDULED_TASKS_DATABASE_OPEN 2550L
#define ERROR_INSTALL_SCHEDULED_TASKS_OPLIST_CREATE 2551L
#define ERROR_INSTALL_SCHEDULED_TASKS_SCRIPT_WRITE 2552L
#define ERROR_INSTALL_SCHEDULED_TASKS_PROPERTY_SET 2553L
//////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////
// Exported functions // Exported functions
@ -50,7 +55,6 @@ extern "C" {
UINT MSITSCA_API EvaluateScheduledTasks(MSIHANDLE hInstall); UINT MSITSCA_API EvaluateScheduledTasks(MSIHANDLE hInstall);
UINT MSITSCA_API InstallScheduledTasks(MSIHANDLE hInstall); UINT MSITSCA_API InstallScheduledTasks(MSIHANDLE hInstall);
UINT MSITSCA_API FinalizeScheduledTasks(MSIHANDLE hInstall);
#ifdef __cplusplus #ifdef __cplusplus
} }
@ -364,172 +368,15 @@ inline HRESULT operator >>(CAtlFile &f, CStringW &str)
} }
inline HRESULT operator >>(CAtlFile &f, ITask *pTask) inline HRESULT operator <<(CAtlFile &f, const TASK_TRIGGER &ttData)
{ {
assert(pTask); return f.Write(&ttData, sizeof(TASK_TRIGGER));
}
HRESULT hr;
CStringW sValue, sValue2;
int iValue, iValue2;
UINT nTriggers;
hr = f >> sValue; inline HRESULT operator >>(CAtlFile &f, TASK_TRIGGER &ttData)
if (FAILED(hr)) return hr; {
hr = pTask->SetApplicationName(sValue); return f.Read(&ttData, sizeof(TASK_TRIGGER));
if (FAILED(hr)) return hr;
hr = f >> sValue;
if (FAILED(hr)) return hr;
hr = pTask->SetParameters(sValue);
if (FAILED(hr)) return hr;
hr = f >> sValue;
if (FAILED(hr)) return hr;
hr = pTask->SetWorkingDirectory(sValue);
if (FAILED(hr)) return hr;
hr = f >> iValue;
if (FAILED(hr)) return hr;
hr = pTask->SetFlags(iValue);
if (FAILED(hr)) return hr;
hr = f >> iValue;
if (FAILED(hr)) return hr;
hr = pTask->SetPriority(iValue);
if (FAILED(hr)) return hr;
hr = f >> sValue;
if (FAILED(hr)) return hr;
hr = f >> sValue2;
if (FAILED(hr)) return hr;
hr = !sValue.IsEmpty() ? pTask->SetAccountInformation(sValue, sValue2.IsEmpty() ? NULL : sValue2) : S_OK;
{
// Clear the password in memory before proceeding.
int iLength = sValue2.GetLength();
LPWSTR pszValue2 = sValue2.GetBuffer(iLength);
::SecureZeroMemory(pszValue2, sizeof(WCHAR) * iLength);
sValue2.ReleaseBuffer(0);
}
if (FAILED(hr)) return hr;
hr = f >> sValue;
if (FAILED(hr)) return hr;
hr = pTask->SetComment(sValue);
if (FAILED(hr)) return hr;
hr = f >> iValue;
if (FAILED(hr)) return hr;
hr = f >> iValue2;
if (FAILED(hr)) return hr;
hr = pTask->SetIdleWait((WORD)iValue, (WORD)iValue2);
if (FAILED(hr)) return hr;
hr = f >> iValue;
if (FAILED(hr)) return hr;
hr = pTask->SetMaxRunTime(iValue);
if (FAILED(hr)) return hr;
// Read and add triggers.
hr = f >> (int&)nTriggers;
if (FAILED(hr)) return hr;
while (nTriggers--) {
CComPtr<ITaskTrigger> pTrigger;
TASK_TRIGGER ttData;
WORD wTriggerIdx;
ULONGLONG ullValue;
FILETIME ftValue;
SYSTEMTIME stValue;
int iDaysInterval, iWeeksInterval, iDaysOfTheWeek, iDaysOfMonth, iWeekOfMonth, iMonthsOfYear;
ZeroMemory(&ttData, sizeof(TASK_TRIGGER));
ttData.cbTriggerSize = sizeof(TASK_TRIGGER);
hr = pTask->CreateTrigger(&wTriggerIdx, &pTrigger);
if (FAILED(hr)) return hr;
hr = f >> iValue;
if (FAILED(hr)) return hr;
ullValue = ((ULONGLONG)iValue + 138426) * 864000000000;
ftValue.dwHighDateTime = ullValue >> 32;
ftValue.dwLowDateTime = ullValue & 0xffffffff;
if (!::FileTimeToSystemTime(&ftValue, &stValue))
return AtlHresultFromLastError();
ttData.wBeginYear = stValue.wYear;
ttData.wBeginMonth = stValue.wMonth;
ttData.wBeginDay = stValue.wDay;
hr = f >> iValue;
if (FAILED(hr)) return hr;
if (iValue != MSI_NULL_INTEGER) {
ullValue = ((ULONGLONG)iValue + 138426) * 864000000000;
ftValue.dwHighDateTime = ullValue >> 32;
ftValue.dwLowDateTime = ullValue & 0xffffffff;
if (!::FileTimeToSystemTime(&ftValue, &stValue))
return AtlHresultFromLastError();
ttData.wEndYear = stValue.wYear;
ttData.wEndMonth = stValue.wMonth;
ttData.wEndDay = stValue.wDay;
ttData.rgFlags |= TASK_TRIGGER_FLAG_HAS_END_DATE;
}
hr = f >> iValue;
if (FAILED(hr)) return hr;
ttData.wStartHour = (WORD)(iValue / 60);
ttData.wStartMinute = (WORD)(iValue % 60);
hr = f >> iValue;
if (FAILED(hr)) return hr;
ttData.MinutesDuration = iValue;
hr = f >> iValue;
if (FAILED(hr)) return hr;
ttData.MinutesInterval = iValue;
hr = f >> iValue;
if (FAILED(hr)) return hr;
ttData.rgFlags |= iValue & ~TASK_TRIGGER_FLAG_HAS_END_DATE;
hr = f >> iValue;
if (FAILED(hr)) return hr;
ttData.TriggerType = (TASK_TRIGGER_TYPE)iValue;
hr = f >> iDaysInterval;
if (FAILED(hr)) return hr;
hr = f >> iWeeksInterval;
if (FAILED(hr)) return hr;
hr = f >> iDaysOfTheWeek;
if (FAILED(hr)) return hr;
hr = f >> iDaysOfMonth;
if (FAILED(hr)) return hr;
hr = f >> iWeekOfMonth;
if (FAILED(hr)) return hr;
hr = f >> iMonthsOfYear;
if (FAILED(hr)) return hr;
switch (ttData.TriggerType) {
case TASK_TIME_TRIGGER_DAILY:
ttData.Type.Daily.DaysInterval = (WORD)iDaysInterval;
break;
case TASK_TIME_TRIGGER_WEEKLY:
ttData.Type.Weekly.WeeksInterval = (WORD)iWeeksInterval;
ttData.Type.Weekly.rgfDaysOfTheWeek = (WORD)iDaysOfTheWeek;
break;
case TASK_TIME_TRIGGER_MONTHLYDATE:
ttData.Type.MonthlyDate.rgfDays = iDaysOfMonth;
ttData.Type.MonthlyDate.rgfMonths = (WORD)iMonthsOfYear;
break;
case TASK_TIME_TRIGGER_MONTHLYDOW:
ttData.Type.MonthlyDOW.wWhichWeek = (WORD)iWeekOfMonth;
ttData.Type.MonthlyDOW.rgfDaysOfTheWeek = (WORD)iDaysOfTheWeek;
ttData.Type.MonthlyDOW.rgfMonths = (WORD)iMonthsOfYear;
break;
}
hr = pTrigger->SetTrigger(&ttData);
if (FAILED(hr)) return hr;
}
return S_OK;
} }
#endif // !defined(RC_INVOKED) && !defined(MIDL_PASS) #endif // !defined(RC_INVOKED) && !defined(MIDL_PASS)

View File

@ -11,44 +11,466 @@ CMSITSCAOp::CMSITSCAOp()
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpSingleTaskOperation // CMSITSCAOpSingleStringOperation
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
CMSITSCAOpSingleTaskOperation::CMSITSCAOpSingleTaskOperation(LPCWSTR pszTaskName) : CMSITSCAOpSingleStringOperation::CMSITSCAOpSingleStringOperation(LPCWSTR pszValue) :
m_sTaskName(pszTaskName), m_sValue(pszValue),
CMSITSCAOp() CMSITSCAOp()
{ {
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpsrcDstTaskOperation // CMSITSCAOpDoubleStringOperation
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
CMSITSCAOpSrcDstTaskOperation::CMSITSCAOpSrcDstTaskOperation(LPCWSTR pszSourceTaskName, LPCWSTR pszDestinationTaskName) : CMSITSCAOpSrcDstStringOperation::CMSITSCAOpSrcDstStringOperation(LPCWSTR pszValue1, LPCWSTR pszValue2) :
m_sSourceTaskName(pszSourceTaskName), m_sValue1(pszValue1),
m_sDestinationTaskName(pszDestinationTaskName), m_sValue2(pszValue2),
CMSITSCAOp() 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::CMSITSCAOpDeleteTask(LPCWSTR pszTaskName) : CMSITSCAOpDeleteTask::CMSITSCAOpDeleteTask(LPCWSTR pszTaskName) :
CMSITSCAOpSingleTaskOperation(pszTaskName) CMSITSCAOpSingleStringOperation(pszTaskName)
{ {
} }
HRESULT CMSITSCAOpDeleteTask::Execute(ITaskScheduler *pTaskScheduler) HRESULT CMSITSCAOpDeleteTask::Execute(CMSITSCASession *pSession)
{ {
assert(pTaskScheduler); assert(pSession);
// Delete the task. if (pSession->m_bRollbackEnabled) {
return pTaskScheduler->Delete(m_sTaskName); 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);
}
} }
@ -58,19 +480,19 @@ HRESULT CMSITSCAOpDeleteTask::Execute(ITaskScheduler *pTaskScheduler)
CMSITSCAOpEnableTask::CMSITSCAOpEnableTask(LPCWSTR pszTaskName, BOOL bEnable) : CMSITSCAOpEnableTask::CMSITSCAOpEnableTask(LPCWSTR pszTaskName, BOOL bEnable) :
m_bEnable(bEnable), m_bEnable(bEnable),
CMSITSCAOpSingleTaskOperation(pszTaskName) CMSITSCAOpSingleStringOperation(pszTaskName)
{ {
} }
HRESULT CMSITSCAOpEnableTask::Execute(ITaskScheduler *pTaskScheduler) HRESULT CMSITSCAOpEnableTask::Execute(CMSITSCASession *pSession)
{ {
assert(pTaskScheduler); assert(pSession);
HRESULT hr; HRESULT hr;
CComPtr<ITask> pTask; CComPtr<ITask> pTask;
// Load the task. // Load the task.
hr = pTaskScheduler->Activate(m_sTaskName, IID_ITask, (IUnknown**)&pTask); hr = pSession->m_pTaskScheduler->Activate(m_sValue, IID_ITask, (IUnknown**)&pTask);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
DWORD dwFlags; DWORD dwFlags;
@ -78,10 +500,22 @@ HRESULT CMSITSCAOpEnableTask::Execute(ITaskScheduler *pTaskScheduler)
hr = pTask->GetFlags(&dwFlags); hr = pTask->GetFlags(&dwFlags);
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
// Modify flags. // Modify flags.
if (m_bEnable) if (m_bEnable) {
dwFlags &= ~TASK_FLAG_DISABLED; if (pSession->m_bRollbackEnabled && (dwFlags & TASK_FLAG_DISABLED)) {
else // 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; dwFlags |= TASK_FLAG_DISABLED;
}
// Set flags. // Set flags.
hr = pTask->SetFlags(dwFlags); hr = pTask->SetFlags(dwFlags);
@ -101,126 +535,134 @@ HRESULT CMSITSCAOpEnableTask::Execute(ITaskScheduler *pTaskScheduler)
// CMSITSCAOpCopyTask // CMSITSCAOpCopyTask
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
CMSITSCAOpCopyTask::CMSITSCAOpCopyTask(LPCWSTR pszSourceTaskName, LPCWSTR pszDestinationTaskName) : CMSITSCAOpCopyTask::CMSITSCAOpCopyTask(LPCWSTR pszTaskSrc, LPCWSTR pszTaskDst) :
CMSITSCAOpSrcDstTaskOperation(pszSourceTaskName, pszDestinationTaskName) CMSITSCAOpSrcDstStringOperation(pszTaskSrc, pszTaskDst)
{ {
} }
HRESULT CMSITSCAOpCopyTask::Execute(ITaskScheduler *pTaskScheduler) HRESULT CMSITSCAOpCopyTask::Execute(CMSITSCASession *pSession)
{ {
assert(pTaskScheduler); assert(pSession);
HRESULT hr; HRESULT hr;
CComPtr<ITask> pTask; CComPtr<ITask> pTask;
// Load the source task. // Load the source task.
hr = pTaskScheduler->Activate(m_sSourceTaskName, IID_ITask, (IUnknown**)&pTask); hr = pSession->m_pTaskScheduler->Activate(m_sValue1, IID_ITask, (IUnknown**)&pTask);
if (SUCCEEDED(hr)) { if (FAILED(hr)) return hr;
// Add with different name.
hr = pTaskScheduler->AddWorkItem(m_sDestinationTaskName, pTask);
if (SUCCEEDED(hr)) {
// Save the task.
CComQIPtr<IPersistFile> pTaskFile(pTask);
hr = pTaskFile->Save(NULL, TRUE);
}
}
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;
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// CMSITSCAOperationList // CMSITSCAOpList
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
CMSITSCAOperationList::CMSITSCAOperationList() : CMSITSCAOpList::CMSITSCAOpList() :
CAtlList<CMSITSCAOp*>(sizeof(CMSITSCAOp*)) CAtlList<CMSITSCAOp*>(sizeof(CMSITSCAOp*))
{ {
} }
HRESULT CMSITSCAOperationList::Save(CAtlFile &f) const void CMSITSCAOpList::Free()
{ {
POSITION pos; POSITION pos;
HRESULT hr;
for (pos = GetHeadPosition(); pos;) { for (pos = GetHeadPosition(); pos;) {
const CMSITSCAOp *pOp = GetNext(pos); CMSITSCAOp *pOp = GetNext(pos);
if (dynamic_cast<const CMSITSCAOpDeleteTask*>(pOp)) CMSITSCAOpList *pOpList = dynamic_cast<CMSITSCAOpList*>(pOp);
hr = Save<CMSITSCAOpDeleteTask, OPERATION_DELETE_TASK>(f, pOp);
else if (dynamic_cast<const CMSITSCAOpEnableTask*>(pOp)) if (pOpList) {
hr = Save<CMSITSCAOpEnableTask, OPERATION_ENABLE_TASK>(f, pOp); // Recursivelly free sublists.
else if (dynamic_cast<const CMSITSCAOpCopyTask*>(pOp)) pOpList->Free();
hr = Save<CMSITSCAOpCopyTask, OPERATION_COPY_TASK>(f, pOp);
else {
// Unsupported type of operation.
assert(0);
hr = E_UNEXPECTED;
} }
delete pOp;
if (FAILED(hr)) return hr;
} }
return S_OK;
}
HRESULT CMSITSCAOperationList::Load(CAtlFile &f)
{
int iTemp;
HRESULT hr;
for (;;) {
hr = f >> iTemp;
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) {
hr = S_OK;
break;
} else if (FAILED(hr))
return hr;
switch ((OPERATION)iTemp) {
case OPERATION_DELETE_TASK:
hr = LoadAndAddTail<CMSITSCAOpDeleteTask>(f);
break;
case OPERATION_ENABLE_TASK:
hr = LoadAndAddTail<CMSITSCAOpEnableTask>(f);
break;
case OPERATION_COPY_TASK:
hr = LoadAndAddTail<CMSITSCAOpCopyTask>(f);
break;
default:
// Unsupported type of operation.
assert(0);
hr = E_UNEXPECTED;
}
if (FAILED(hr)) return hr;
}
return S_OK;
}
void CMSITSCAOperationList::Free()
{
POSITION pos;
for (pos = GetHeadPosition(); pos;)
delete GetNext(pos);
RemoveAll(); RemoveAll();
} }
HRESULT CMSITSCAOperationList::Execute(ITaskScheduler *pTaskScheduler, BOOL bContinueOnError) 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; POSITION pos;
HRESULT hr; HRESULT hr;
for (pos = GetHeadPosition(); pos;) { for (pos = GetHeadPosition(); pos;) {
hr = GetNext(pos)->Execute(pTaskScheduler); hr = GetNext(pos)->Execute(pSession);
if (!bContinueOnError && FAILED(hr)) return hr; if (!pSession->m_bContinueOnError && FAILED(hr)) return hr;
} }
return S_OK; 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);
}

View File

@ -2,12 +2,15 @@
#define __MSITSCAOP_H__ #define __MSITSCAOP_H__
#include "MSITSCA.h" #include "MSITSCA.h"
#include <atlbase.h>
#include <atlcoll.h> #include <atlcoll.h>
#include <atlfile.h> #include <atlfile.h>
#include <atlstr.h> #include <atlstr.h>
#include <mstask.h> #include <mstask.h>
#include <windows.h> #include <windows.h>
class CMSITSCASession;
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// CMSITSCAOp // CMSITSCAOp
@ -18,42 +21,131 @@ class CMSITSCAOp
public: public:
CMSITSCAOp(); CMSITSCAOp();
virtual HRESULT Execute(ITaskScheduler *pTaskScheduler) = 0; virtual HRESULT Execute(CMSITSCASession *pSession) = 0;
}; };
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpSingleTaskOperation // CMSITSCAOpSingleStringOperation
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpSingleTaskOperation : public CMSITSCAOp class CMSITSCAOpSingleStringOperation : public CMSITSCAOp
{ {
public: public:
CMSITSCAOpSingleTaskOperation(LPCWSTR pszTaskName = L""); CMSITSCAOpSingleStringOperation(LPCWSTR pszValue = L"");
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSingleTaskOperation &op); friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSingleStringOperation &op);
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSingleTaskOperation &op); friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSingleStringOperation &op);
protected: protected:
CStringW m_sTaskName; CStringW m_sValue;
}; };
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpsrcDstTaskOperation // CMSITSCAOpDoubleStringOperation
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpSrcDstTaskOperation : public CMSITSCAOp class CMSITSCAOpSrcDstStringOperation : public CMSITSCAOp
{ {
public: public:
CMSITSCAOpSrcDstTaskOperation(LPCWSTR pszSourceTaskName = L"", LPCWSTR pszDestinationTaskName = L""); CMSITSCAOpSrcDstStringOperation(LPCWSTR pszValue1 = L"", LPCWSTR pszValue2 = L"");
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSrcDstTaskOperation &op); friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSrcDstStringOperation &op);
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSrcDstTaskOperation &op); friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSrcDstStringOperation &op);
protected: protected:
CStringW m_sSourceTaskName; CStringW m_sValue1;
CStringW m_sDestinationTaskName; CStringW m_sValue2;
};
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpBooleanOperation
////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpBooleanOperation : public CMSITSCAOp
{
public:
CMSITSCAOpBooleanOperation(BOOL bValue = TRUE);
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpBooleanOperation &op);
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpBooleanOperation &op);
protected:
BOOL m_bValue;
};
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpEnableRollback
////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpEnableRollback : public CMSITSCAOpBooleanOperation
{
public:
CMSITSCAOpEnableRollback(BOOL bEnable = TRUE);
virtual HRESULT Execute(CMSITSCASession *pSession);
};
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpDeleteFile
////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpDeleteFile : public CMSITSCAOpSingleStringOperation
{
public:
CMSITSCAOpDeleteFile(LPCWSTR pszFileName = L"");
virtual HRESULT Execute(CMSITSCASession *pSession);
};
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpMoveFile
////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpMoveFile : public CMSITSCAOpSrcDstStringOperation
{
public:
CMSITSCAOpMoveFile(LPCWSTR pszFileSrc = L"", LPCWSTR pszFileDst = L"");
virtual HRESULT Execute(CMSITSCASession *pSession);
};
////////////////////////////////////////////////////////////////////////////
// CMSITSCAOpCreateTask
////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpCreateTask : public CMSITSCAOpSingleStringOperation
{
public:
CMSITSCAOpCreateTask(LPCWSTR pszTaskName = L"");
virtual ~CMSITSCAOpCreateTask();
virtual HRESULT Execute(CMSITSCASession *pSession);
UINT SetFromRecord(MSIHANDLE hInstall, MSIHANDLE hRecord);
UINT SetTriggersFromView(MSIHANDLE hView);
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpCreateTask &op);
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpCreateTask &op);
protected:
BOOL m_bForce;
CStringW m_sApplicationName;
CStringW m_sParameters;
CStringW m_sWorkingDirectory;
CStringW m_sComment;
DWORD m_dwFlags;
DWORD m_dwPriority;
CStringW m_sAccountName;
CStringW m_sPassword;
WORD m_wIdleMinutes;
WORD m_wDeadlineMinutes;
DWORD m_dwMaxRuntimeMS;
CAtlList<TASK_TRIGGER> m_lTriggers;
}; };
@ -61,11 +153,11 @@ protected:
// CMSITSCAOpDeleteTask // CMSITSCAOpDeleteTask
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpDeleteTask : public CMSITSCAOpSingleTaskOperation class CMSITSCAOpDeleteTask : public CMSITSCAOpSingleStringOperation
{ {
public: public:
CMSITSCAOpDeleteTask(LPCWSTR pszTaskName = L""); CMSITSCAOpDeleteTask(LPCWSTR pszTaskName = L"");
virtual HRESULT Execute(ITaskScheduler *pTaskScheduler); virtual HRESULT Execute(CMSITSCASession *pSession);
}; };
@ -73,11 +165,11 @@ public:
// CMSITSCAOpEnableTask // CMSITSCAOpEnableTask
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpEnableTask : public CMSITSCAOpSingleTaskOperation class CMSITSCAOpEnableTask : public CMSITSCAOpSingleStringOperation
{ {
public: public:
CMSITSCAOpEnableTask(LPCWSTR pszTaskName = L"", BOOL bEnable = TRUE); CMSITSCAOpEnableTask(LPCWSTR pszTaskName = L"", BOOL bEnable = TRUE);
virtual HRESULT Execute(ITaskScheduler *pTaskScheduler); virtual HRESULT Execute(CMSITSCASession *pSession);
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpEnableTask &op); friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpEnableTask &op);
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpEnableTask &op); friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpEnableTask &op);
@ -91,34 +183,42 @@ protected:
// CMSITSCAOpCopyTask // CMSITSCAOpCopyTask
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
class CMSITSCAOpCopyTask : public CMSITSCAOpSrcDstTaskOperation class CMSITSCAOpCopyTask : public CMSITSCAOpSrcDstStringOperation
{ {
public: public:
CMSITSCAOpCopyTask(LPCWSTR pszSourceTaskName = L"", LPCWSTR pszDestinationTaskName = L""); CMSITSCAOpCopyTask(LPCWSTR pszTaskSrc = L"", LPCWSTR pszTaskDst = L"");
virtual HRESULT Execute(ITaskScheduler *pTaskScheduler); virtual HRESULT Execute(CMSITSCASession *pSession);
}; };
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// CMSITSCAOperationList // CMSITSCAOpList
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
class CMSITSCAOperationList : public CAtlList<CMSITSCAOp*> class CMSITSCAOpList : public CMSITSCAOp, public CAtlList<CMSITSCAOp*>
{ {
public: public:
CMSITSCAOperationList(); CMSITSCAOpList();
HRESULT Save(CAtlFile &f) const;
HRESULT Load(CAtlFile &f);
void Free(); void Free();
HRESULT LoadFromFile(LPCTSTR pszFileName);
HRESULT SaveToFile(LPCTSTR pszFileName) const;
HRESULT Execute(ITaskScheduler *pTaskScheduler, BOOL bContinueOnError); virtual HRESULT Execute(CMSITSCASession *pSession);
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpList &op);
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpList &op);
protected: protected:
enum OPERATION { enum OPERATION {
OPERATION_DELETE_TASK = 1, OPERATION_ENABLE_ROLLBACK = 1,
OPERATION_DELETE_FILE,
OPERATION_MOVE_FILE,
OPERATION_CREATE_TASK,
OPERATION_DELETE_TASK,
OPERATION_ENABLE_TASK, OPERATION_ENABLE_TASK,
OPERATION_COPY_TASK OPERATION_COPY_TASK,
OPERATION_SUBLIST
}; };
protected: protected:
@ -127,11 +227,262 @@ protected:
}; };
////////////////////////////////////////////////////////////////////////////
// CMSITSCASession
////////////////////////////////////////////////////////////////////////////
class CMSITSCASession
{
public:
CMSITSCASession();
virtual ~CMSITSCASession();
HRESULT Initialize();
CComPtr<ITaskScheduler> m_pTaskScheduler; // Task scheduler interface
BOOL m_bContinueOnError; // Continue execution on operation error?
BOOL m_bRollbackEnabled; // Is rollback enabled?
CMSITSCAOpList m_olRollback; // Rollback operation list
CMSITSCAOpList m_olCommit; // Commit operation list
};
////////////////////////////////////////////////////////////////////////////
// Inline operators
////////////////////////////////////////////////////////////////////////////
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSingleStringOperation &op)
{
return f << op.m_sValue;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSingleStringOperation &op)
{
return f >> op.m_sValue;
}
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSrcDstStringOperation &op)
{
HRESULT hr;
hr = f << op.m_sValue1;
if (FAILED(hr)) return hr;
return f << op.m_sValue2;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSrcDstStringOperation &op)
{
HRESULT hr;
hr = f >> op.m_sValue1;
if (FAILED(hr)) return hr;
return f >> op.m_sValue2;
}
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpBooleanOperation &op)
{
return f << (int)op.m_bValue;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpBooleanOperation &op)
{
int iValue;
HRESULT hr;
hr = f >> iValue;
if (FAILED(hr)) return hr;
op.m_bValue = iValue ? TRUE : FALSE;
return S_OK;
}
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpCreateTask &op)
{
HRESULT hr;
POSITION pos;
hr = f << (const CMSITSCAOpSingleStringOperation&)op; if (FAILED(hr)) return hr;
hr = f << (int)op.m_bForce; if (FAILED(hr)) return hr;
hr = f << op.m_sApplicationName; if (FAILED(hr)) return hr;
hr = f << op.m_sParameters; if (FAILED(hr)) return hr;
hr = f << op.m_sWorkingDirectory; if (FAILED(hr)) return hr;
hr = f << op.m_sComment; if (FAILED(hr)) return hr;
hr = f << (int)op.m_dwFlags; if (FAILED(hr)) return hr;
hr = f << (int)op.m_dwPriority; if (FAILED(hr)) return hr;
hr = f << op.m_sAccountName; if (FAILED(hr)) return hr;
hr = f << op.m_sPassword; if (FAILED(hr)) return hr;
hr = f << (int)MAKELONG(op.m_wDeadlineMinutes, op.m_wIdleMinutes); if (FAILED(hr)) return hr;
hr = f << (int)op.m_dwMaxRuntimeMS; if (FAILED(hr)) return hr;
hr = f << (int)op.m_lTriggers.GetCount(); if (FAILED(hr)) return hr;
for (pos = op.m_lTriggers.GetHeadPosition(); pos;) {
hr = f << op.m_lTriggers.GetNext(pos);
if (FAILED(hr)) return hr;
}
return S_OK;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpCreateTask &op)
{
HRESULT hr;
DWORD dwValue;
hr = f >> (CMSITSCAOpSingleStringOperation&)op; if (FAILED(hr)) return hr;
hr = f >> (int&)dwValue; if (FAILED(hr)) return hr; op.m_bForce = dwValue ? TRUE : FALSE;
hr = f >> op.m_sApplicationName; if (FAILED(hr)) return hr;
hr = f >> op.m_sParameters; if (FAILED(hr)) return hr;
hr = f >> op.m_sWorkingDirectory; if (FAILED(hr)) return hr;
hr = f >> op.m_sComment; if (FAILED(hr)) return hr;
hr = f >> (int&)op.m_dwFlags; if (FAILED(hr)) return hr;
hr = f >> (int&)op.m_dwPriority; if (FAILED(hr)) return hr;
hr = f >> op.m_sAccountName; if (FAILED(hr)) return hr;
hr = f >> op.m_sPassword; if (FAILED(hr)) return hr;
hr = f >> (int&)dwValue; if (FAILED(hr)) return hr; op.m_wIdleMinutes = HIWORD(dwValue); op.m_wDeadlineMinutes = LOWORD(dwValue);
hr = f >> (int&)op.m_dwMaxRuntimeMS; if (FAILED(hr)) return hr;
hr = f >> (int&)dwValue; if (FAILED(hr)) return hr;
while (dwValue--) {
TASK_TRIGGER ttData;
hr = f >> ttData;
if (FAILED(hr)) return hr;
op.m_lTriggers.AddTail(ttData);
}
return S_OK;
}
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpEnableTask &op)
{
HRESULT hr;
hr = f << (const CMSITSCAOpSingleStringOperation&)op;
if (FAILED(hr)) return hr;
return f << (int)op.m_bEnable;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpEnableTask &op)
{
HRESULT hr;
int iTemp;
hr = f >> (CMSITSCAOpSingleStringOperation&)op;
if (FAILED(hr)) return hr;
hr = f >> iTemp;
if (FAILED(hr)) return hr;
op.m_bEnable = iTemp ? TRUE : FALSE;
return S_OK;
}
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpList &list)
{
POSITION pos;
HRESULT hr;
hr = f << (int)list.GetCount();
if (FAILED(hr)) return hr;
for (pos = list.GetHeadPosition(); pos;) {
const CMSITSCAOp *pOp = list.GetNext(pos);
if (dynamic_cast<const CMSITSCAOpEnableRollback*>(pOp))
hr = list.Save<CMSITSCAOpEnableRollback, CMSITSCAOpList::OPERATION_ENABLE_ROLLBACK>(f, pOp);
else if (dynamic_cast<const CMSITSCAOpDeleteFile*>(pOp))
hr = list.Save<CMSITSCAOpDeleteFile, CMSITSCAOpList::OPERATION_DELETE_FILE>(f, pOp);
else if (dynamic_cast<const CMSITSCAOpMoveFile*>(pOp))
hr = list.Save<CMSITSCAOpMoveFile, CMSITSCAOpList::OPERATION_MOVE_FILE>(f, pOp);
else if (dynamic_cast<const CMSITSCAOpCreateTask*>(pOp))
hr = list.Save<CMSITSCAOpCreateTask, CMSITSCAOpList::OPERATION_CREATE_TASK>(f, pOp);
else if (dynamic_cast<const CMSITSCAOpDeleteTask*>(pOp))
hr = list.Save<CMSITSCAOpDeleteTask, CMSITSCAOpList::OPERATION_DELETE_TASK>(f, pOp);
else if (dynamic_cast<const CMSITSCAOpEnableTask*>(pOp))
hr = list.Save<CMSITSCAOpEnableTask, CMSITSCAOpList::OPERATION_ENABLE_TASK>(f, pOp);
else if (dynamic_cast<const CMSITSCAOpCopyTask*>(pOp))
hr = list.Save<CMSITSCAOpCopyTask, CMSITSCAOpList::OPERATION_COPY_TASK>(f, pOp);
else if (dynamic_cast<const CMSITSCAOpList*>(pOp))
hr = list.Save<CMSITSCAOpList, CMSITSCAOpList::OPERATION_SUBLIST>(f, pOp);
else {
// Unsupported type of operation.
assert(0);
hr = E_UNEXPECTED;
}
if (FAILED(hr)) return hr;
}
return S_OK;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpList &list)
{
HRESULT hr;
DWORD dwCount;
hr = f >> (int&)dwCount;
if (FAILED(hr)) return hr;
while (dwCount--) {
int iTemp;
hr = f >> iTemp;
if (FAILED(hr)) return hr;
switch ((CMSITSCAOpList::OPERATION)iTemp) {
case CMSITSCAOpList::OPERATION_ENABLE_ROLLBACK:
hr = list.LoadAndAddTail<CMSITSCAOpEnableRollback>(f);
break;
case CMSITSCAOpList::OPERATION_DELETE_FILE:
hr = list.LoadAndAddTail<CMSITSCAOpDeleteFile>(f);
break;
case CMSITSCAOpList::OPERATION_MOVE_FILE:
hr = list.LoadAndAddTail<CMSITSCAOpMoveFile>(f);
break;
case CMSITSCAOpList::OPERATION_CREATE_TASK:
hr = list.LoadAndAddTail<CMSITSCAOpCreateTask>(f);
break;
case CMSITSCAOpList::OPERATION_DELETE_TASK:
hr = list.LoadAndAddTail<CMSITSCAOpDeleteTask>(f);
break;
case CMSITSCAOpList::OPERATION_ENABLE_TASK:
hr = list.LoadAndAddTail<CMSITSCAOpEnableTask>(f);
break;
case CMSITSCAOpList::OPERATION_COPY_TASK:
hr = list.LoadAndAddTail<CMSITSCAOpCopyTask>(f);
break;
case CMSITSCAOpList::OPERATION_SUBLIST:
hr = list.LoadAndAddTail<CMSITSCAOpList>(f);
break;
default:
// Unsupported type of operation.
assert(0);
hr = E_UNEXPECTED;
}
if (FAILED(hr)) return hr;
}
return S_OK;
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// Inline methods // Inline methods
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
template <class T, int ID> inline static HRESULT CMSITSCAOperationList::Save(CAtlFile &f, const CMSITSCAOp *p) template <class T, int ID> inline static HRESULT CMSITSCAOpList::Save(CAtlFile &f, const CMSITSCAOp *p)
{ {
assert(p); assert(p);
HRESULT hr; HRESULT hr;
@ -145,7 +496,7 @@ template <class T, int ID> inline static HRESULT CMSITSCAOperationList::Save(CAt
} }
template <class T> inline HRESULT CMSITSCAOperationList::LoadAndAddTail(CAtlFile &f) template <class T> inline HRESULT CMSITSCAOpList::LoadAndAddTail(CAtlFile &f)
{ {
HRESULT hr; HRESULT hr;
@ -165,69 +516,4 @@ template <class T> inline HRESULT CMSITSCAOperationList::LoadAndAddTail(CAtlFile
return S_OK; return S_OK;
} }
////////////////////////////////////////////////////////////////////////////
// Inline operators
////////////////////////////////////////////////////////////////////////////
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSingleTaskOperation &op)
{
return f << op.m_sTaskName;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSingleTaskOperation &op)
{
return f >> op.m_sTaskName;
}
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSrcDstTaskOperation &op)
{
HRESULT hr;
hr = f << op.m_sSourceTaskName;
if (FAILED(hr)) return hr;
return f << op.m_sDestinationTaskName;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSrcDstTaskOperation &op)
{
HRESULT hr;
hr = f >> op.m_sSourceTaskName;
if (FAILED(hr)) return hr;
return f >> op.m_sDestinationTaskName;
}
inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpEnableTask &op)
{
HRESULT hr;
hr = f << (const CMSITSCAOpSingleTaskOperation&)op;
if (FAILED(hr)) return hr;
return f << (int)op.m_bEnable;
}
inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpEnableTask &op)
{
HRESULT hr;
int iTemp;
hr = f >> (CMSITSCAOpSingleTaskOperation&)op;
if (FAILED(hr)) return hr;
hr = f >> iTemp;
if (FAILED(hr)) return hr;
op.m_bEnable = iTemp ? TRUE : FALSE;
return S_OK;
}
#endif // __MSITSCAOP_H__ #endif // __MSITSCAOP_H__