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:
parent
174c96ee60
commit
59e983d8fc
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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__
|
||||||
|
Loading…
x
Reference in New Issue
Block a user