Začetna verzija nameščanja razvrščenih opravil je končana. Usposobiti moram še nastavljanje kazalca napredka in obveščanje o postopku ter napakah.
This commit is contained in:
parent
e4711e98f0
commit
174c96ee60
2
.gitattributes
vendored
2
.gitattributes
vendored
@ -4,6 +4,8 @@ MSICALib/MSITSCA.cpp -text
|
|||||||
MSICALib/MSITSCA.h -text
|
MSICALib/MSITSCA.h -text
|
||||||
MSICALib/MSITSCA.vcxproj -text svneol=unset#text/xml
|
MSICALib/MSITSCA.vcxproj -text svneol=unset#text/xml
|
||||||
MSICALib/MSITSCA.vcxproj.filters -text svneol=unset#text/xml
|
MSICALib/MSITSCA.vcxproj.filters -text svneol=unset#text/xml
|
||||||
|
MSICALib/MSITSCAOp.cpp -text
|
||||||
|
MSICALib/MSITSCAOp.h -text
|
||||||
MSICALib/StdAfx.cpp -text
|
MSICALib/StdAfx.cpp -text
|
||||||
MSICALib/StdAfx.h -text
|
MSICALib/StdAfx.h -text
|
||||||
MSICALib/res/MSITSCA.rc -text svneol=unset#unset
|
MSICALib/res/MSITSCA.rc -text svneol=unset#unset
|
||||||
|
@ -28,32 +28,60 @@ ScheduledTask Parameters Y Formatted Task command line parameters.
|
|||||||
ScheduledTask WorkingDir N Formatted Task working directory. If the working directory is set to "", when the application is run, the current directory will be the directory in which the task scheduler service executable, Mstask.exe, resides.
|
ScheduledTask WorkingDir N Formatted Task working directory. If the working directory is set to "", when the application is run, the current directory will be the directory in which the task scheduler service executable, Mstask.exe, resides.
|
||||||
ScheduledTask Flags N DoubleInteger Task's flags to pass in IScheduledWorkItem::SetFlags() call.
|
ScheduledTask Flags N DoubleInteger Task's flags to pass in IScheduledWorkItem::SetFlags() call.
|
||||||
ScheduledTask Priority N DoubleInteger 32;64;128;256 Task's priority to pass in ITask::SetPriority() call.
|
ScheduledTask Priority N DoubleInteger 32;64;128;256 Task's priority to pass in ITask::SetPriority() call.
|
||||||
ScheduledTask User Y Text Account name to run task as. Use empty string for system account.
|
ScheduledTask User Y Formatted Account name to run task as. Use empty string for system account.
|
||||||
ScheduledTask Password Y Text Account password to run task as. Use NULL for system account.
|
ScheduledTask Password Y Formatted Account password to run task as. Use NULL for system account.
|
||||||
ScheduledTask Description Y Text Task description.
|
ScheduledTask Description Y Formatted Task description.
|
||||||
ScheduledTask IdleMin Y Integer A value that specifies how long, in minutes, the system must remain idle before the work item can run.
|
ScheduledTask IdleMin Y Integer A value that specifies how long, in minutes, the system must remain idle before the work item can run.
|
||||||
ScheduledTask IdleDeadline Y Integer A value that specifies the maximum number of minutes that the Task Scheduler will wait for the idle-time period.
|
ScheduledTask IdleDeadline Y Integer A value that specifies the maximum number of minutes that the Task Scheduler will wait for the idle-time period.
|
||||||
ScheduledTask MaxRuntime N DoubleInteger Specifies the maximum run time (in milliseconds), for the task. This parameter may be set to -1 to specify an unlimited time.
|
ScheduledTask MaxRuntime N DoubleInteger Specifies the maximum run time (in milliseconds), for the task. This parameter may be set to -1 to specify an unlimited time.
|
||||||
ScheduledTask Condition Y Condition Optional expression which skips the task if evaluates to expFalse. If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.
|
ScheduledTask Condition Y Condition Optional expression which skips the task if evaluates to expFalse. If the expression syntax is invalid, the engine will terminate, returning iesBadActionData.
|
||||||
ScheduledTask Component_ N Component 1 Identifier Component the task is part of.
|
ScheduledTask Component_ N Component 1 Identifier Component the task is part of.
|
||||||
TaskTrigger Trigger N Identifier Primary key, non-localized token.
|
TaskTrigger Trigger N Identifier Primary key, non-localized token.
|
||||||
TaskTrigger BeginDate N Date/Time Date that the task trigger activates. The beginning date must be specified when setting a task.
|
TaskTrigger BeginDate N DoubleInteger Date that the task trigger activates (counted in days from January 1st, 1980). The beginning date must be specified when setting a task.
|
||||||
TaskTrigger EndDate Y Date/Time Date that the task trigger deactivates.
|
TaskTrigger EndDate Y DoubleInteger Date that the task trigger deactivates (counted in days from January 1st, 1980).
|
||||||
TaskTrigger StartTime N Date/Time Time of the day the task runs.
|
TaskTrigger StartTime N DoubleInteger Time of the day the task runs (counted in minutes since midnight).
|
||||||
|
TaskTrigger StartTimeRand Y Integer Maximum random number of minutes to add to start time.
|
||||||
TaskTrigger MinutesDuration Y DoubleInteger Number of minutes after the task starts that the trigger will remain active. The number of minutes specified here must be greater than or equal to the MinutesInterval column.
|
TaskTrigger MinutesDuration Y DoubleInteger Number of minutes after the task starts that the trigger will remain active. The number of minutes specified here must be greater than or equal to the MinutesInterval column.
|
||||||
TaskTrigger MinutesInterval Y DoubleInteger Number of minutes between consecutive task executions. This number is counted from the start of the previous scheduled task. The number of minutes specified here must be less than the MinutesDuration column.
|
TaskTrigger MinutesInterval Y DoubleInteger Number of minutes between consecutive task executions. This number is counted from the start of the previous scheduled task. The number of minutes specified here must be less than the MinutesDuration column.
|
||||||
TaskTrigger Flags N DoubleInteger Value that describes the behavior of the trigger. This value is a combination of the flags in TASK_TRIGGER::rgFlags.
|
TaskTrigger Flags N DoubleInteger Value that describes the behavior of the trigger. This value is a combination of the flags in TASK_TRIGGER::rgFlags.
|
||||||
TaskTrigger Type N 0 7 Integer A TASK_TRIGGER_TYPE enumerated value that specifies the type of trigger. This member is used with Type. The type of trigger specified here determines which fields of the TRIGGER_TYPE_UNION specified in Type member will be used. Trigger type is based on when the trigger will run the task.
|
TaskTrigger Type N 0 7 Integer A TASK_TRIGGER_TYPE enumerated value that specifies the type of trigger. This member is used with Type. The type of trigger specified here determines which fields of the TRIGGER_TYPE_UNION specified in Type member will be used. Trigger type is based on when the trigger will run the task.
|
||||||
TaskTrigger DaysInterval Y Integer Specifies the number of days between task runs.
|
TaskTrigger DaysInterval Y Integer Specifies the number of days between task runs.
|
||||||
TaskTrigger WeeksInterval Y Integer Number of weeks between invocations of a task.
|
TaskTrigger WeeksInterval Y Integer Number of weeks between invocations of a task.
|
||||||
TaskTrigger DaysOfTheWeek Y 0 127 Integer Value that describes the days of the week the task runs. This value is a bitfield and is a combination of the following flags: 1=Sun, 2=Mon, 4=Tue, 8=Wed, 16=Thu, 32=Fri, 64=Sat.
|
TaskTrigger DaysOfTheWeek Y 0 127 Integer Value that describes the days of the week the task runs. This value is a bitfield and is a combination of the following values: 1=Sun, 2=Mon, 4=Tue, 8=Wed, 16=Thu, 32=Fri, 64=Sat.
|
||||||
TaskTrigger DaysOfMonth Y DoubleInteger Specifies the day of the month a task runs. This value is a bitfield that specifies the day(s) the task will run. Bit 0 corresponds to the first of the month, bit 1 to the second, and so forth.
|
TaskTrigger DaysOfMonth Y DoubleInteger Specifies the day of the month a task runs. This value is a bitfield that specifies the day(s) the task will run. Bit 0 corresponds to the first of the month, bit 1 to the second, and so forth.
|
||||||
TaskTrigger WeekOfMonth Y Integer 1;2;3;4;5 Specifies the week of the month when the task runs. This value is exclusive and is one of the following flags: 1=The task will run between the first and seventh day of the month, 2=The task will run between the eighth and 14th day of the month, 3=The task will run between the 15th and 21st day of the month, 4=The task will run between the 22nd and 28th of the month, 5=The task will run between the last seven days of the month.
|
TaskTrigger WeekOfMonth Y Integer 1;2;3;4;5 Specifies the week of the month when the task runs. This value is exclusive and is one of the following: 1=The task will run between the first and seventh day of the month, 2=The task will run between the eighth and 14th day of the month, 3=The task will run between the 15th and 21st day of the month, 4=The task will run between the 22nd and 28th of the month, 5=The task will run between the last seven days of the month.
|
||||||
TaskTrigger MonthsOfYear Y 0 4095 Integer Specifies the month(s) when the task runs. This value is a combination of the following flags: 1=Jan, 2=Feb, 4=Mar, 8=Apr, 16=May, 32=Jun, 64=Jul, 128=Aug, 256=Sep, 512=Oct, 1024=Nov, 2048=Dec.
|
TaskTrigger MonthsOfYear Y 0 4095 Integer Specifies the month(s) when the task runs. This value is a combination of the following values: 1=Jan, 2=Feb, 4=Mar, 8=Apr, 16=May, 32=Jun, 64=Jul, 128=Aug, 256=Sep, 512=Oct, 1024=Nov, 2048=Dec.
|
||||||
TaskTrigger Task_ N ScheduledTask 1 Identifier Key of the Trigger's task.
|
TaskTrigger Task_ N ScheduledTask 1 Identifier Key of the Trigger's task.
|
||||||
<<NOKEEP
|
<<NOKEEP
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# ActionText
|
||||||
|
|
||||||
|
Vse :: "$(JEZIK).$(CFG).$(PLAT).ActionText-2.idt"
|
||||||
|
|
||||||
|
"Sl.$(CFG).$(PLAT).ActionText-2.idtx" : "Makefile" "..\..\include\MSINast.mak"
|
||||||
|
-if exist $@ del /f /q $@
|
||||||
|
move /y << $@ > NUL
|
||||||
|
Action Description Template
|
||||||
|
s$(MSI_TIP_ID) L0 L0
|
||||||
|
1250 ActionText Action
|
||||||
|
caInstallScheduledTasks Registracija razporejenih opravil Opravilo: [1]
|
||||||
|
<<NOKEEP
|
||||||
|
|
||||||
|
"De.$(CFG).$(PLAT).ActionText-2.idt" : "Sl.$(CFG).$(PLAT).ActionText-2.idtx" "..\res\de_DE.po"
|
||||||
|
rcxgettext.exe idtp $@ $**
|
||||||
|
|
||||||
|
"En.$(CFG).$(PLAT).ActionText-2.idt" : "Sl.$(CFG).$(PLAT).ActionText-2.idtx" "..\res\en_GB.po"
|
||||||
|
rcxgettext.exe idtp $@ $**
|
||||||
|
|
||||||
|
"It.$(CFG).$(PLAT).ActionText-2.idt" : "Sl.$(CFG).$(PLAT).ActionText-2.idtx" "..\res\it_IT.po"
|
||||||
|
rcxgettext.exe idtp $@ $**
|
||||||
|
|
||||||
|
"Sl.$(CFG).$(PLAT).ActionText-2.idt" : "Sl.$(CFG).$(PLAT).ActionText-2.idtx"
|
||||||
|
copy /y $** $@ > NUL
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Binary
|
# Binary
|
||||||
|
|
||||||
@ -87,11 +115,37 @@ s$(MSI_TIP_ID) i2 S$(MSI_TIP_ID) S255
|
|||||||
CustomAction Action
|
CustomAction Action
|
||||||
caEvaluateScheduledTasks 1 binMSITSCA.dll EvaluateScheduledTasks
|
caEvaluateScheduledTasks 1 binMSITSCA.dll EvaluateScheduledTasks
|
||||||
caInstallScheduledTasks 1025 binMSITSCA.dll InstallScheduledTasks
|
caInstallScheduledTasks 1025 binMSITSCA.dll InstallScheduledTasks
|
||||||
caRollbackScheduledTasks 1281 binMSITSCA.dll RollbackScheduledTasks
|
caRollbackScheduledTasks 1281 binMSITSCA.dll FinalizeScheduledTasks
|
||||||
caCommitScheduledTasks 1537 binMSITSCA.dll CommitScheduledTasks
|
caCommitScheduledTasks 1537 binMSITSCA.dll FinalizeScheduledTasks
|
||||||
<<NOKEEP
|
<<NOKEEP
|
||||||
|
|
||||||
|
|
||||||
|
######################################################################
|
||||||
|
# Error
|
||||||
|
|
||||||
|
Vse :: "$(JEZIK).$(CFG).$(PLAT).Error-2.idt"
|
||||||
|
|
||||||
|
"Sl.$(CFG).$(PLAT).Error-2.idtx" : "Makefile" "..\..\include\MSINast.mak"
|
||||||
|
-if exist $@ del /f /q $@
|
||||||
|
move /y << $@ > NUL
|
||||||
|
Error Message
|
||||||
|
i2 L0
|
||||||
|
1250 Error Error
|
||||||
|
<<NOKEEP
|
||||||
|
|
||||||
|
"De.$(CFG).$(PLAT).Error-2.idt" : "Sl.$(CFG).$(PLAT).Error-2.idtx" "..\res\de_DE.po"
|
||||||
|
rcxgettext.exe idtp $@ $**
|
||||||
|
|
||||||
|
"En.$(CFG).$(PLAT).Error-2.idt" : "Sl.$(CFG).$(PLAT).Error-2.idtx" "..\res\en_GB.po"
|
||||||
|
rcxgettext.exe idtp $@ $**
|
||||||
|
|
||||||
|
"It.$(CFG).$(PLAT).Error-2.idt" : "Sl.$(CFG).$(PLAT).Error-2.idtx" "..\res\it_IT.po"
|
||||||
|
rcxgettext.exe idtp $@ $**
|
||||||
|
|
||||||
|
"Sl.$(CFG).$(PLAT).Error-2.idt" : "Sl.$(CFG).$(PLAT).Error-2.idtx"
|
||||||
|
copy /y $** $@ > NUL
|
||||||
|
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# InstallExecuteSequence
|
# InstallExecuteSequence
|
||||||
|
|
||||||
@ -132,8 +186,8 @@ Vse :: "$(JEZIK).$(CFG).$(PLAT).TaskTrigger-1.idt"
|
|||||||
"$(JEZIK).$(CFG).$(PLAT).TaskTrigger-1.idt" : "Makefile" "..\..\include\MSINast.mak"
|
"$(JEZIK).$(CFG).$(PLAT).TaskTrigger-1.idt" : "Makefile" "..\..\include\MSINast.mak"
|
||||||
-if exist $@ del /f /q $@
|
-if exist $@ del /f /q $@
|
||||||
move /y << $@ > NUL
|
move /y << $@ > NUL
|
||||||
Trigger BeginDate EndDate StartTime MinutesDuration MinutesInterval Flags Type DaysInterval WeeksInterval DaysOfTheWeek DaysOfMonth WeekOfMonth MonthsOfYear Task_
|
Trigger BeginDate EndDate StartTime StartTimeRand MinutesDuration MinutesInterval Flags Type DaysInterval WeeksInterval DaysOfTheWeek DaysOfMonth WeekOfMonth MonthsOfYear Task_
|
||||||
s$(MSI_TIP_ID) i2 I2 i2 I4 I4 i4 i2 I2 I2 I2 I4 I2 I2 s$(MSI_TIP_ID)
|
s$(MSI_TIP_ID) i2 I2 i2 I2 I4 I4 i4 i2 I2 I2 I2 I4 I2 I2 s$(MSI_TIP_ID)
|
||||||
TaskTrigger Trigger
|
TaskTrigger Trigger
|
||||||
<<NOKEEP
|
<<NOKEEP
|
||||||
|
|
||||||
|
@ -1,26 +1,37 @@
|
|||||||
#include "StdAfx.h"
|
#include "StdAfx.h"
|
||||||
|
|
||||||
#pragma comment(lib, "msi.lib")
|
#pragma comment(lib, "msi.lib")
|
||||||
|
#pragma comment(lib, "mstask.lib")
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Globalne spremenljivke
|
// Local constants
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
HINSTANCE MSITSCA::hInstance = NULL;
|
#define MSITSCA_TASK_TICK_SIZE 2048
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Globalne funkcije
|
// 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);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Global functions
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
|
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
|
||||||
{
|
{
|
||||||
|
UNREFERENCED_PARAMETER(hInstance);
|
||||||
UNREFERENCED_PARAMETER(lpReserved);
|
UNREFERENCED_PARAMETER(lpReserved);
|
||||||
|
|
||||||
switch (dwReason) {
|
switch (dwReason) {
|
||||||
case DLL_PROCESS_ATTACH:
|
case DLL_PROCESS_ATTACH:
|
||||||
MSITSCA::hInstance = hInstance;
|
// Randomize!
|
||||||
|
srand((unsigned)time(NULL));
|
||||||
|
|
||||||
case DLL_THREAD_ATTACH:
|
case DLL_THREAD_ATTACH:
|
||||||
case DLL_THREAD_DETACH:
|
case DLL_THREAD_DETACH:
|
||||||
@ -33,128 +44,494 @@ extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpRes
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Javne funkcije
|
// Exported functions
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
UINT MSITSCA_API EvaluateScheduledTasks(MSIHANDLE hInstall)
|
UINT MSITSCA_API EvaluateScheduledTasks(MSIHANDLE hInstall)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(hInstall);
|
UNREFERENCED_PARAMETER(hInstall);
|
||||||
|
|
||||||
UINT uiResult;
|
HRESULT hr;
|
||||||
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
|
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
|
||||||
CString sSequenceFilename, sComponent;
|
CString sSequenceFilename, sComponent;
|
||||||
CStringW sDisplayName;
|
CStringW sValue;
|
||||||
CFile fSequence;
|
CAtlFile fSequence;
|
||||||
PMSIHANDLE hDatabase, hViewST;
|
PMSIHANDLE hDatabase, hViewST;
|
||||||
|
PMSIHANDLE hRecordProg = ::MsiCreateRecord(2);
|
||||||
|
BOOL bRollbackEnabled;
|
||||||
|
|
||||||
assert(0); // Attach debugger here or press "Ignore"!
|
assert(hRecordProg);
|
||||||
|
assert(0); // Attach debugger here, or press "Ignore"!
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
// Prepare our own sequence script.
|
||||||
{
|
{
|
||||||
// Prepare our own "TODO" script.
|
|
||||||
LPTSTR szBuffer = sSequenceFilename.GetBuffer(MAX_PATH);
|
LPTSTR szBuffer = sSequenceFilename.GetBuffer(MAX_PATH);
|
||||||
assert(szBuffer);
|
assert(szBuffer);
|
||||||
::GetTempPath(MAX_PATH, szBuffer);
|
::GetTempPath(MAX_PATH, szBuffer);
|
||||||
::GetTempFileName(szBuffer, _T("TS"), 0, szBuffer);
|
::GetTempFileName(szBuffer, _T("TS"), 0, szBuffer);
|
||||||
sSequenceFilename.ReleaseBuffer();
|
sSequenceFilename.ReleaseBuffer();
|
||||||
}
|
}
|
||||||
if (!fSequence.Open(sSequenceFilename, CFile::modeWrite | CFile::shareDenyWrite | CFile::modeCreate | CFile::osSequentialScan)) {
|
hr = fSequence.Create(sSequenceFilename, GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN);
|
||||||
uiResult = ERROR_CREATE_FAILED;
|
if (FAILED(hr)) goto error1;
|
||||||
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 :
|
||||||
|
TRUE;
|
||||||
|
hr = fSequence << (int)bRollbackEnabled;
|
||||||
|
|
||||||
hDatabase = ::MsiGetActiveDatabase(hInstall);
|
hDatabase = ::MsiGetActiveDatabase(hInstall);
|
||||||
if (!hDatabase) {
|
if (!hDatabase) { hr = E_INVALIDARG; goto error2; }
|
||||||
uiResult = ERROR_INVALID_HANDLE;
|
|
||||||
goto error2;
|
|
||||||
}
|
|
||||||
|
|
||||||
uiResult = ::MsiDatabaseOpenView(hDatabase, _T("SELECT Component_, DisplayName FROM ScheduledTask"), &hViewST);
|
// Execute a query to get tasks.
|
||||||
if (uiResult != ERROR_SUCCESS) goto error2;
|
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));
|
||||||
|
if (FAILED(hr)) goto error2;
|
||||||
uiResult = ::MsiViewExecute(hViewST, NULL);
|
hr = HRESULT_FROM_WIN32(::MsiViewExecute(hViewST, NULL));
|
||||||
if (uiResult != ERROR_SUCCESS) goto error2;
|
if (FAILED(hr)) goto error2;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
PMSIHANDLE hRecord;
|
PMSIHANDLE hRecord, hViewTT;
|
||||||
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.
|
||||||
uiResult = ::MsiViewFetch(hViewST, &hRecord);
|
hr = HRESULT_FROM_WIN32(::MsiViewFetch(hViewST, &hRecord));
|
||||||
if (uiResult == ERROR_NO_MORE_ITEMS)
|
if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
|
||||||
|
hr = S_OK;
|
||||||
break;
|
break;
|
||||||
else if (uiResult != ERROR_SUCCESS)
|
} else if (FAILED(hr))
|
||||||
goto error2;
|
break;
|
||||||
|
|
||||||
|
// Read and evaluate condition.
|
||||||
|
hr = HRESULT_FROM_WIN32(::MsiRecordGetString(hRecord, 14, sValue));
|
||||||
|
if (FAILED(hr)) break;
|
||||||
|
condition = ::MsiEvaluateCondition(hInstall, sValue);
|
||||||
|
if (condition == MSICONDITION_FALSE)
|
||||||
|
continue;
|
||||||
|
else if (condition == MSICONDITION_ERROR) {
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_FIELD);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Read Component ID.
|
// Read Component ID.
|
||||||
uiResult = ::MsiRecordGetString(hRecord, 1, sComponent);
|
hr = HRESULT_FROM_WIN32(::MsiRecordGetString(hRecord, 15, sComponent));
|
||||||
if (uiResult != ERROR_SUCCESS) goto error2;
|
if (FAILED(hr)) break;
|
||||||
|
|
||||||
// Get component state and save for deferred processing.
|
// Get component state and save for deferred processing.
|
||||||
uiResult = ::MsiGetComponentState(hInstall, sComponent, &iInstalled, &iAction);
|
hr = HRESULT_FROM_WIN32(::MsiGetComponentState(hInstall, sComponent, &iInstalled, &iAction));
|
||||||
if (uiResult != ERROR_SUCCESS) goto error2;
|
if (FAILED(hr)) break;
|
||||||
fSequence << iAction;
|
hr = fSequence << (int)iInstalled;
|
||||||
|
if (FAILED(hr)) break;
|
||||||
|
hr = fSequence << (int)iAction;
|
||||||
|
if (FAILED(hr)) break;
|
||||||
|
|
||||||
// Get task's display name and save for deferred processing.
|
// Read and store task's data to script file.
|
||||||
uiResult = ::MsiRecordGetStringW(hRecord, 2, sDisplayName);
|
hr = HRESULT_FROM_WIN32(::MsiFormatAndStoreString(hInstall, hRecord, 2, fSequence, sValue)); // DisplayName
|
||||||
if (uiResult != ERROR_SUCCESS) goto error2;
|
if (FAILED(hr)) break;
|
||||||
fSequence << sDisplayName;
|
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.
|
||||||
|
verify(SUCCEEDED(fSequence.GetPosition(posTriggerCount)));
|
||||||
|
hr = fSequence << (int)nTriggers;
|
||||||
|
if (FAILED(hr)) break;
|
||||||
|
|
||||||
|
// Perform another query to get task's triggers.
|
||||||
|
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));
|
||||||
|
if (FAILED(hr)) break;
|
||||||
|
hr = HRESULT_FROM_WIN32(::MsiViewExecute(hViewTT, hRecord));
|
||||||
|
if (FAILED(hr)) break;
|
||||||
|
|
||||||
|
for (;; nTriggers++) {
|
||||||
|
PMSIHANDLE hRecord;
|
||||||
|
int iStartTime, iStartTimeRand;
|
||||||
|
|
||||||
|
// Fetch one record from the view.
|
||||||
|
hr = HRESULT_FROM_WIN32(::MsiViewFetch(hViewTT, &hRecord));
|
||||||
|
if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
|
||||||
|
hr = S_OK;
|
||||||
|
break;
|
||||||
|
} else if (FAILED(hr))
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Read StartTime and StartTimeRand
|
||||||
|
iStartTime = ::MsiRecordGetInteger(hRecord, 4);
|
||||||
|
if (iStartTime == MSI_NULL_INTEGER) {
|
||||||
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_FIELD);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
iStartTimeRand = ::MsiRecordGetInteger(hRecord, 5);
|
||||||
|
if (iStartTimeRand != MSI_NULL_INTEGER) {
|
||||||
|
// Add random delay to StartTime.
|
||||||
|
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)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL) { hr = E_ABORT; goto error2; }
|
||||||
}
|
}
|
||||||
|
|
||||||
verify(::MsiViewClose(hViewST) == ERROR_SUCCESS);
|
verify(::MsiViewClose(hViewST) == ERROR_SUCCESS);
|
||||||
uiResult = ERROR_SUCCESS;
|
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:
|
error2:
|
||||||
fSequence.Close();
|
fSequence.Close();
|
||||||
error1:
|
error1:
|
||||||
if (uiResult != ERROR_SUCCESS) ::DeleteFile(sSequenceFilename);
|
if (FAILED(hr)) ::DeleteFile(sSequenceFilename);
|
||||||
if (bIsCoInitialized) ::CoUninitialize();
|
if (bIsCoInitialized) ::CoUninitialize();
|
||||||
return uiResult;
|
return SUCCEEDED(hr) ? ERROR_SUCCESS : hr == E_ABORT ? ERROR_INSTALL_USEREXIT : ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UINT MSITSCA_API InstallScheduledTasks(MSIHANDLE hInstall)
|
UINT MSITSCA_API InstallScheduledTasks(MSIHANDLE hInstall)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(hInstall);
|
|
||||||
assert(::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED));
|
assert(::MsiGetMode(hInstall, MSIRUNMODE_SCHEDULED));
|
||||||
|
|
||||||
UINT uiResult;
|
HRESULT hr;
|
||||||
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
|
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
|
||||||
|
CString sSequenceFilename, sSequenceFilenameRB, sSequenceFilenameCM;
|
||||||
|
LPCTSTR pszExtension;
|
||||||
|
CStringW sDisplayName;
|
||||||
|
CAtlFile fSequence, fSequence2;
|
||||||
|
CComPtr<ITaskScheduler> pTaskScheduler;
|
||||||
|
CMSITSCAOperationList lstOperationsRB, lstOperationsCM;
|
||||||
|
PMSIHANDLE hRecordAction = ::MsiCreateRecord(3), hRecordProg = ::MsiCreateRecord(3);
|
||||||
|
BOOL bRollbackEnabled;
|
||||||
|
|
||||||
assert(0); // Attach debugger here or press "Ignore"!
|
assert(hRecordProg);
|
||||||
uiResult = ERROR_SUCCESS;
|
|
||||||
|
|
||||||
|
assert(0); // Attach debugger here, or press "Ignore"!
|
||||||
|
|
||||||
|
// Tell the installer to use explicit progress messages.
|
||||||
|
verify(::MsiRecordSetInteger(hRecordProg, 1, 1) == ERROR_SUCCESS);
|
||||||
|
verify(::MsiRecordSetInteger(hRecordProg, 2, 1) == ERROR_SUCCESS);
|
||||||
|
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.
|
||||||
|
verify(::MsiRecordSetInteger(hRecordProg, 1, 2) == ERROR_SUCCESS);
|
||||||
|
verify(::MsiRecordSetInteger(hRecordProg, 2, MSITSCA_TASK_TICK_SIZE) == ERROR_SUCCESS);
|
||||||
|
verify(::MsiRecordSetInteger(hRecordProg, 3, 0) == ERROR_SUCCESS);
|
||||||
|
|
||||||
|
// Determine sequence script file names.
|
||||||
|
hr = HRESULT_FROM_WIN32(::MsiGetProperty(hInstall, _T("CustomActionData"), sSequenceFilename));
|
||||||
|
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.
|
||||||
|
hr = fSequence.Create(sSequenceFilename, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN);
|
||||||
|
if (FAILED(hr)) goto error2;
|
||||||
|
|
||||||
|
// Get rollback-enabled state.
|
||||||
|
{
|
||||||
|
int iValue;
|
||||||
|
hr = fSequence >> iValue;
|
||||||
|
if (FAILED(hr)) goto error3;
|
||||||
|
bRollbackEnabled = iValue ? TRUE : FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr = pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL);
|
||||||
|
if (FAILED(hr)) goto error3;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
INSTALLSTATE iInstalled, iAction;
|
||||||
|
CComPtr<ITask> pTaskOrig;
|
||||||
|
|
||||||
|
hr = fSequence >> (int&)iInstalled;
|
||||||
|
if (hr == HRESULT_FROM_WIN32(ERROR_HANDLE_EOF)) {
|
||||||
|
hr = S_OK;
|
||||||
|
break;
|
||||||
|
} else if (FAILED(hr)) goto error4;
|
||||||
|
hr = fSequence >> (int&)iAction;
|
||||||
|
if (FAILED(hr)) goto error4;
|
||||||
|
hr = fSequence >> sDisplayName;
|
||||||
|
if (FAILED(hr)) goto error4;
|
||||||
|
|
||||||
|
verify(::MsiRecordSetString(hRecordAction, 1, sDisplayName) == ERROR_SUCCESS);
|
||||||
|
if (::MsiProcessMessage(hInstall, INSTALLMESSAGE_ACTIONDATA, hRecordAction) == IDCANCEL) { hr = E_ABORT; goto error1; }
|
||||||
|
|
||||||
|
// See if the task with this name already exists.
|
||||||
|
hr = pTaskScheduler->Activate(sDisplayName, IID_ITask, (IUnknown**)&pTaskOrig);
|
||||||
|
if (hr != COR_E_FILENOTFOUND && FAILED(hr)) goto error4;
|
||||||
|
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.
|
||||||
|
hr = pTaskScheduler->Delete(sDisplayName);
|
||||||
|
if (FAILED(hr)) goto error4;
|
||||||
|
|
||||||
|
if (bRollbackEnabled) {
|
||||||
|
// Order commit action to delete backup copy.
|
||||||
|
lstOperationsCM.AddTail(new CMSITSCAOpDeleteTask(sDisplayNameOrig));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
// 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.
|
||||||
|
lstOperationsRB.Execute(pTaskScheduler, TRUE);
|
||||||
|
::DeleteFile(sSequenceFilenameCM);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lstOperationsRB.Free();
|
||||||
|
lstOperationsCM.Free();
|
||||||
|
error3:
|
||||||
|
fSequence.Close();
|
||||||
|
error2:
|
||||||
|
::DeleteFile(sSequenceFilename);
|
||||||
|
error1:
|
||||||
if (bIsCoInitialized) ::CoUninitialize();
|
if (bIsCoInitialized) ::CoUninitialize();
|
||||||
return uiResult;
|
return SUCCEEDED(hr) ? ERROR_SUCCESS : hr == E_ABORT ? ERROR_INSTALL_USEREXIT : ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UINT MSITSCA_API CommitScheduledTasks(MSIHANDLE hInstall)
|
UINT MSITSCA_API FinalizeScheduledTasks(MSIHANDLE hInstall)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(hInstall);
|
HRESULT hr;
|
||||||
assert(::MsiGetMode(hInstall, MSIRUNMODE_COMMIT));
|
|
||||||
|
|
||||||
UINT uiResult;
|
|
||||||
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
|
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
|
||||||
|
CString sSequenceFilename, sSequenceFilename2;
|
||||||
|
CAtlFile fSequence;
|
||||||
|
CComPtr<ITaskScheduler> pTaskScheduler;
|
||||||
|
CMSITSCAOperationList lstOperations;
|
||||||
|
|
||||||
assert(0); // Attach debugger here or press "Ignore"!
|
assert(0); // Attach debugger here, or press "Ignore"!
|
||||||
uiResult = ERROR_SUCCESS;
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
::DeleteFile(sSequenceFilename);
|
||||||
|
}
|
||||||
|
|
||||||
if (bIsCoInitialized) ::CoUninitialize();
|
if (bIsCoInitialized) ::CoUninitialize();
|
||||||
return uiResult;
|
return SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INVALID_DATA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
UINT MSITSCA_API RollbackScheduledTasks(MSIHANDLE hInstall)
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Local functions
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
inline UINT MsiFormatAndStoreString(MSIHANDLE hInstall, MSIHANDLE hRecord, unsigned int iField, CAtlFile &fSequence, CStringW &sValue)
|
||||||
{
|
{
|
||||||
UNREFERENCED_PARAMETER(hInstall);
|
|
||||||
assert(::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK));
|
|
||||||
|
|
||||||
UINT uiResult;
|
UINT uiResult;
|
||||||
BOOL bIsCoInitialized = SUCCEEDED(::CoInitialize(NULL));
|
|
||||||
|
|
||||||
assert(0); // Attach debugger here or press "Ignore"!
|
uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, iField, sValue);
|
||||||
uiResult = ERROR_SUCCESS;
|
if (uiResult != ERROR_SUCCESS) return uiResult;
|
||||||
|
if (FAILED(fSequence << sValue)) return ERROR_WRITE_FAULT;
|
||||||
if (bIsCoInitialized) ::CoUninitialize();
|
return ERROR_SUCCESS;
|
||||||
return uiResult;
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Konstante
|
// Constants
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#define MSITSCA_VERSION 0x01000000
|
#define MSITSCA_VERSION 0x01000000
|
||||||
@ -17,11 +17,10 @@
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Kode virov
|
// Resource IDs
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#define IDR_MAINFRAME 1
|
#define IDR_MAINFRAME 1
|
||||||
// TODO: Dodaj definicije konstant virov tukaj.
|
|
||||||
|
|
||||||
#if !defined(RC_INVOKED) && !defined(MIDL_PASS)
|
#if !defined(RC_INVOKED) && !defined(MIDL_PASS)
|
||||||
|
|
||||||
@ -29,7 +28,7 @@
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Naèin klicanja funkcij
|
// Calling declaration
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#if defined(MSITSCA_DLL)
|
#if defined(MSITSCA_DLL)
|
||||||
@ -42,7 +41,7 @@
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Javne funkcije
|
// Exported functions
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -51,8 +50,7 @@ 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 CommitScheduledTasks(MSIHANDLE hInstall);
|
UINT MSITSCA_API FinalizeScheduledTasks(MSIHANDLE hInstall);
|
||||||
UINT MSITSCA_API RollbackScheduledTasks(MSIHANDLE hInstall);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
@ -60,28 +58,70 @@ extern "C" {
|
|||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Globalne funkcije in spremenljivke
|
// Local includes
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
namespace MSITSCA {
|
#include <atlfile.h>
|
||||||
extern HINSTANCE hInstance; // roèica modula
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
// Lokalni include
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#include <afx.h>
|
|
||||||
#include <atlstr.h>
|
#include <atlstr.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <msiquery.h>
|
#include <msiquery.h>
|
||||||
|
#include <mstask.h>
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
// Funkcije inline
|
// Inline Functions
|
||||||
////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
inline UINT MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName, CStringA &sValue)
|
||||||
|
{
|
||||||
|
DWORD dwSize = 0;
|
||||||
|
UINT uiResult;
|
||||||
|
|
||||||
|
// Query the actual string length first.
|
||||||
|
uiResult = ::MsiGetPropertyA(hInstall, szName, "", &dwSize);
|
||||||
|
if (uiResult == ERROR_MORE_DATA) {
|
||||||
|
// Prepare the buffer to read the string data into and read it.
|
||||||
|
LPSTR szBuffer = sValue.GetBuffer(dwSize++);
|
||||||
|
if (!szBuffer) return ERROR_OUTOFMEMORY;
|
||||||
|
uiResult = ::MsiGetPropertyA(hInstall, szName, szBuffer, &dwSize);
|
||||||
|
sValue.ReleaseBuffer(uiResult == ERROR_SUCCESS ? dwSize : 0);
|
||||||
|
return uiResult;
|
||||||
|
} else if (uiResult == ERROR_SUCCESS) {
|
||||||
|
// The string in database is empty.
|
||||||
|
sValue.Empty();
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
} else {
|
||||||
|
// Return error code.
|
||||||
|
return uiResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline UINT MsiGetPropertyW(MSIHANDLE hInstall, LPCWSTR szName, CStringW &sValue)
|
||||||
|
{
|
||||||
|
DWORD dwSize = 0;
|
||||||
|
UINT uiResult;
|
||||||
|
|
||||||
|
// Query the actual string length first.
|
||||||
|
uiResult = ::MsiGetPropertyW(hInstall, szName, L"", &dwSize);
|
||||||
|
if (uiResult == ERROR_MORE_DATA) {
|
||||||
|
// Prepare the buffer to read the string data into and read it.
|
||||||
|
LPWSTR szBuffer = sValue.GetBuffer(dwSize++);
|
||||||
|
if (!szBuffer) return ERROR_OUTOFMEMORY;
|
||||||
|
uiResult = ::MsiGetPropertyW(hInstall, szName, szBuffer, &dwSize);
|
||||||
|
sValue.ReleaseBuffer(uiResult == ERROR_SUCCESS ? dwSize : 0);
|
||||||
|
return uiResult;
|
||||||
|
} else if (uiResult == ERROR_SUCCESS) {
|
||||||
|
// The string in database is empty.
|
||||||
|
sValue.Empty();
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
} else {
|
||||||
|
// Return error code.
|
||||||
|
return uiResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
inline UINT MsiRecordGetStringA(MSIHANDLE hRecord, unsigned int iField, CStringA &sValue)
|
inline UINT MsiRecordGetStringA(MSIHANDLE hRecord, unsigned int iField, CStringA &sValue)
|
||||||
{
|
{
|
||||||
DWORD dwSize = 0;
|
DWORD dwSize = 0;
|
||||||
@ -92,17 +132,10 @@ inline UINT MsiRecordGetStringA(MSIHANDLE hRecord, unsigned int iField, CStringA
|
|||||||
if (uiResult == ERROR_MORE_DATA) {
|
if (uiResult == ERROR_MORE_DATA) {
|
||||||
// Prepare the buffer to read the string data into and read it.
|
// Prepare the buffer to read the string data into and read it.
|
||||||
LPSTR szBuffer = sValue.GetBuffer(dwSize++);
|
LPSTR szBuffer = sValue.GetBuffer(dwSize++);
|
||||||
assert(szBuffer);
|
if (!szBuffer) return ERROR_OUTOFMEMORY;
|
||||||
uiResult = ::MsiRecordGetStringA(hRecord, iField, szBuffer, &dwSize);
|
uiResult = ::MsiRecordGetStringA(hRecord, iField, szBuffer, &dwSize);
|
||||||
if (uiResult == ERROR_SUCCESS) {
|
sValue.ReleaseBuffer(uiResult == ERROR_SUCCESS ? dwSize : 0);
|
||||||
// Read succeeded.
|
return uiResult;
|
||||||
sValue.ReleaseBuffer(dwSize);
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
} else {
|
|
||||||
// Read failed. Empty the string and return error code.
|
|
||||||
sValue.ReleaseBuffer(0);
|
|
||||||
return uiResult;
|
|
||||||
}
|
|
||||||
} else if (uiResult == ERROR_SUCCESS) {
|
} else if (uiResult == ERROR_SUCCESS) {
|
||||||
// The string in database is empty.
|
// The string in database is empty.
|
||||||
sValue.Empty();
|
sValue.Empty();
|
||||||
@ -124,17 +157,10 @@ inline UINT MsiRecordGetStringW(MSIHANDLE hRecord, unsigned int iField, CStringW
|
|||||||
if (uiResult == ERROR_MORE_DATA) {
|
if (uiResult == ERROR_MORE_DATA) {
|
||||||
// Prepare the buffer to read the string data into and read it.
|
// Prepare the buffer to read the string data into and read it.
|
||||||
LPWSTR szBuffer = sValue.GetBuffer(dwSize++);
|
LPWSTR szBuffer = sValue.GetBuffer(dwSize++);
|
||||||
assert(szBuffer);
|
if (!szBuffer) return ERROR_OUTOFMEMORY;
|
||||||
uiResult = ::MsiRecordGetStringW(hRecord, iField, szBuffer, &dwSize);
|
uiResult = ::MsiRecordGetStringW(hRecord, iField, szBuffer, &dwSize);
|
||||||
if (uiResult == ERROR_SUCCESS) {
|
sValue.ReleaseBuffer(uiResult == ERROR_SUCCESS ? dwSize : 0);
|
||||||
// Read succeeded.
|
return uiResult;
|
||||||
sValue.ReleaseBuffer(dwSize);
|
|
||||||
return ERROR_SUCCESS;
|
|
||||||
} else {
|
|
||||||
// Read failed. Empty the string and return error code.
|
|
||||||
sValue.ReleaseBuffer(0);
|
|
||||||
return uiResult;
|
|
||||||
}
|
|
||||||
} else if (uiResult == ERROR_SUCCESS) {
|
} else if (uiResult == ERROR_SUCCESS) {
|
||||||
// The string in database is empty.
|
// The string in database is empty.
|
||||||
sValue.Empty();
|
sValue.Empty();
|
||||||
@ -146,32 +172,364 @@ inline UINT MsiRecordGetStringW(MSIHANDLE hRecord, unsigned int iField, CStringW
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////
|
inline UINT MsiFormatRecordA(MSIHANDLE hInstall, MSIHANDLE hRecord, CStringA &sValue)
|
||||||
// Operatorji inline
|
|
||||||
////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
inline CFile& operator <<(CFile &f, int i)
|
|
||||||
{
|
{
|
||||||
f.Write(&i, sizeof(int));
|
DWORD dwSize = 0;
|
||||||
return f;
|
UINT uiResult;
|
||||||
|
|
||||||
|
// Query the final string length first.
|
||||||
|
uiResult = ::MsiFormatRecordA(hInstall, hRecord, "", &dwSize);
|
||||||
|
if (uiResult == ERROR_MORE_DATA) {
|
||||||
|
// Prepare the buffer to format the string data into and read it.
|
||||||
|
LPSTR szBuffer = sValue.GetBuffer(dwSize++);
|
||||||
|
if (!szBuffer) return ERROR_OUTOFMEMORY;
|
||||||
|
uiResult = ::MsiFormatRecordA(hInstall, hRecord, szBuffer, &dwSize);
|
||||||
|
sValue.ReleaseBuffer(uiResult == ERROR_SUCCESS ? dwSize : 0);
|
||||||
|
return uiResult;
|
||||||
|
} else if (uiResult == ERROR_SUCCESS) {
|
||||||
|
// The result is empty.
|
||||||
|
sValue.Empty();
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
} else {
|
||||||
|
// Return error code.
|
||||||
|
return uiResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline CFile& operator <<(CFile &f, const CStringA &str)
|
inline UINT MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord, CStringW &sValue)
|
||||||
{
|
{
|
||||||
int iLength = str.GetLength();
|
DWORD dwSize = 0;
|
||||||
f.Write(&iLength, sizeof(int));
|
UINT uiResult;
|
||||||
f.Write((LPCSTR)str, sizeof(CHAR) * iLength);
|
|
||||||
return f;
|
// Query the final string length first.
|
||||||
|
uiResult = ::MsiFormatRecordW(hInstall, hRecord, L"", &dwSize);
|
||||||
|
if (uiResult == ERROR_MORE_DATA) {
|
||||||
|
// Prepare the buffer to format the string data into and read it.
|
||||||
|
LPWSTR szBuffer = sValue.GetBuffer(dwSize++);
|
||||||
|
if (!szBuffer) return ERROR_OUTOFMEMORY;
|
||||||
|
uiResult = ::MsiFormatRecordW(hInstall, hRecord, szBuffer, &dwSize);
|
||||||
|
sValue.ReleaseBuffer(uiResult == ERROR_SUCCESS ? dwSize : 0);
|
||||||
|
return uiResult;
|
||||||
|
} else if (uiResult == ERROR_SUCCESS) {
|
||||||
|
// The result is empty.
|
||||||
|
sValue.Empty();
|
||||||
|
return ERROR_SUCCESS;
|
||||||
|
} else {
|
||||||
|
// Return error code.
|
||||||
|
return uiResult;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
inline CFile& operator <<(CFile &f, const CStringW &str)
|
inline UINT MsiRecordFormatStringA(MSIHANDLE hInstall, MSIHANDLE hRecord, unsigned int iField, CStringA &sValue)
|
||||||
{
|
{
|
||||||
|
UINT uiResult;
|
||||||
|
PMSIHANDLE hRecordEx;
|
||||||
|
|
||||||
|
// Read string to format.
|
||||||
|
uiResult = ::MsiRecordGetStringA(hRecord, iField, sValue);
|
||||||
|
if (uiResult != ERROR_SUCCESS) return uiResult;
|
||||||
|
|
||||||
|
// If the string is empty, there's nothing left to do.
|
||||||
|
if (sValue.IsEmpty()) return ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// Create a record.
|
||||||
|
hRecordEx = ::MsiCreateRecord(1);
|
||||||
|
if (!hRecordEx) return ERROR_INVALID_HANDLE;
|
||||||
|
|
||||||
|
// Populate record with data.
|
||||||
|
uiResult = ::MsiRecordSetStringA(hRecordEx, 0, sValue);
|
||||||
|
if (uiResult != ERROR_SUCCESS) return uiResult;
|
||||||
|
|
||||||
|
// Do the formatting.
|
||||||
|
return ::MsiFormatRecordA(hInstall, hRecordEx, sValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline UINT MsiRecordFormatStringW(MSIHANDLE hInstall, MSIHANDLE hRecord, unsigned int iField, CStringW &sValue)
|
||||||
|
{
|
||||||
|
UINT uiResult;
|
||||||
|
PMSIHANDLE hRecordEx;
|
||||||
|
|
||||||
|
// Read string to format.
|
||||||
|
uiResult = ::MsiRecordGetStringW(hRecord, iField, sValue);
|
||||||
|
if (uiResult != ERROR_SUCCESS) return uiResult;
|
||||||
|
|
||||||
|
// If the string is empty, there's nothing left to do.
|
||||||
|
if (sValue.IsEmpty()) return ERROR_SUCCESS;
|
||||||
|
|
||||||
|
// Create a record.
|
||||||
|
hRecordEx = ::MsiCreateRecord(1);
|
||||||
|
if (!hRecordEx) return ERROR_INVALID_HANDLE;
|
||||||
|
|
||||||
|
// Populate record with data.
|
||||||
|
uiResult = ::MsiRecordSetStringW(hRecordEx, 0, sValue);
|
||||||
|
if (uiResult != ERROR_SUCCESS) return uiResult;
|
||||||
|
|
||||||
|
// Do the formatting.
|
||||||
|
return ::MsiFormatRecordW(hInstall, hRecordEx, sValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef UNICODE
|
||||||
|
#define MsiRecordFormatString MsiRecordFormatStringW
|
||||||
|
#else
|
||||||
|
#define MsiRecordFormatString MsiRecordFormatStringA
|
||||||
|
#endif // !UNICODE
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
// Inline operators
|
||||||
|
////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
inline HRESULT operator <<(CAtlFile &f, int i)
|
||||||
|
{
|
||||||
|
return f.Write(&i, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline HRESULT operator >>(CAtlFile &f, int &i)
|
||||||
|
{
|
||||||
|
return f.Read(&i, sizeof(int));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline HRESULT operator <<(CAtlFile &f, const CStringA &str)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
int iLength = str.GetLength();
|
int iLength = str.GetLength();
|
||||||
f.Write(&iLength, sizeof(int));
|
|
||||||
f.Write((LPCWSTR)str, sizeof(WCHAR) * iLength);
|
// Write string length (in characters) as 32-bit integer.
|
||||||
return f;
|
hr = f.Write(&iLength, sizeof(int));
|
||||||
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
|
// Write string data (without terminator).
|
||||||
|
return f.Write((LPCSTR)str, sizeof(CHAR) * iLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline HRESULT operator >>(CAtlFile &f, CStringA &str)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
int iLength;
|
||||||
|
LPSTR buf;
|
||||||
|
|
||||||
|
// Read string length (in characters) as 32-bit integer.
|
||||||
|
hr = f.Read(&iLength, sizeof(int));
|
||||||
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
|
// Allocate the buffer.
|
||||||
|
buf = str.GetBuffer(iLength);
|
||||||
|
if (!buf) return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
// Read string data (without terminator).
|
||||||
|
hr = f.Read(buf, sizeof(CHAR) * iLength);
|
||||||
|
str.ReleaseBuffer(SUCCEEDED(hr) ? iLength : 0);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline HRESULT operator <<(CAtlFile &f, const CStringW &str)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
int iLength = str.GetLength();
|
||||||
|
|
||||||
|
// Write string length (in characters) as 32-bit integer.
|
||||||
|
hr = f.Write(&iLength, sizeof(int));
|
||||||
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
|
// Write string data (without terminator).
|
||||||
|
return f.Write((LPCWSTR)str, sizeof(WCHAR) * iLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline HRESULT operator >>(CAtlFile &f, CStringW &str)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
int iLength;
|
||||||
|
LPWSTR buf;
|
||||||
|
|
||||||
|
// Read string length (in characters) as 32-bit integer.
|
||||||
|
hr = f.Read(&iLength, sizeof(int));
|
||||||
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
|
// Allocate the buffer.
|
||||||
|
buf = str.GetBuffer(iLength);
|
||||||
|
if (!buf) return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
// Read string data (without terminator).
|
||||||
|
hr = f.Read(buf, sizeof(WCHAR) * iLength);
|
||||||
|
str.ReleaseBuffer(SUCCEEDED(hr) ? iLength : 0);
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline HRESULT operator >>(CAtlFile &f, ITask *pTask)
|
||||||
|
{
|
||||||
|
assert(pTask);
|
||||||
|
|
||||||
|
HRESULT hr;
|
||||||
|
CStringW sValue, sValue2;
|
||||||
|
int iValue, iValue2;
|
||||||
|
UINT nTriggers;
|
||||||
|
|
||||||
|
hr = f >> sValue;
|
||||||
|
if (FAILED(hr)) return hr;
|
||||||
|
hr = pTask->SetApplicationName(sValue);
|
||||||
|
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)
|
||||||
|
@ -249,6 +249,7 @@
|
|||||||
</ItemDefinitionGroup>
|
</ItemDefinitionGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="MSITSCA.cpp" />
|
<ClCompile Include="MSITSCA.cpp" />
|
||||||
|
<ClCompile Include="MSITSCAOp.cpp" />
|
||||||
<ClCompile Include="StdAfx.cpp">
|
<ClCompile Include="StdAfx.cpp">
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||||
@ -262,6 +263,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="MSITSCA.h" />
|
<ClInclude Include="MSITSCA.h" />
|
||||||
|
<ClInclude Include="MSITSCAOp.h" />
|
||||||
<ClInclude Include="StdAfx.h" />
|
<ClInclude Include="StdAfx.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
@ -21,6 +21,9 @@
|
|||||||
<ClCompile Include="StdAfx.cpp">
|
<ClCompile Include="StdAfx.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="MSITSCAOp.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="MSITSCA.h">
|
<ClInclude Include="MSITSCA.h">
|
||||||
@ -29,6 +32,9 @@
|
|||||||
<ClInclude Include="StdAfx.h">
|
<ClInclude Include="StdAfx.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="MSITSCAOp.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ResourceCompile Include="res\MSITSCA.rc">
|
<ResourceCompile Include="res\MSITSCA.rc">
|
||||||
|
226
MSICALib/MSITSCAOp.cpp
Normal file
226
MSICALib/MSITSCAOp.cpp
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
#include "StdAfx.h"
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOp
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CMSITSCAOp::CMSITSCAOp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpSingleTaskOperation
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CMSITSCAOpSingleTaskOperation::CMSITSCAOpSingleTaskOperation(LPCWSTR pszTaskName) :
|
||||||
|
m_sTaskName(pszTaskName),
|
||||||
|
CMSITSCAOp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpsrcDstTaskOperation
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CMSITSCAOpSrcDstTaskOperation::CMSITSCAOpSrcDstTaskOperation(LPCWSTR pszSourceTaskName, LPCWSTR pszDestinationTaskName) :
|
||||||
|
m_sSourceTaskName(pszSourceTaskName),
|
||||||
|
m_sDestinationTaskName(pszDestinationTaskName),
|
||||||
|
CMSITSCAOp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpDeleteTask
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CMSITSCAOpDeleteTask::CMSITSCAOpDeleteTask(LPCWSTR pszTaskName) :
|
||||||
|
CMSITSCAOpSingleTaskOperation(pszTaskName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT CMSITSCAOpDeleteTask::Execute(ITaskScheduler *pTaskScheduler)
|
||||||
|
{
|
||||||
|
assert(pTaskScheduler);
|
||||||
|
|
||||||
|
// Delete the task.
|
||||||
|
return pTaskScheduler->Delete(m_sTaskName);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpEnableTask
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CMSITSCAOpEnableTask::CMSITSCAOpEnableTask(LPCWSTR pszTaskName, BOOL bEnable) :
|
||||||
|
m_bEnable(bEnable),
|
||||||
|
CMSITSCAOpSingleTaskOperation(pszTaskName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT CMSITSCAOpEnableTask::Execute(ITaskScheduler *pTaskScheduler)
|
||||||
|
{
|
||||||
|
assert(pTaskScheduler);
|
||||||
|
HRESULT hr;
|
||||||
|
CComPtr<ITask> pTask;
|
||||||
|
|
||||||
|
// Load the task.
|
||||||
|
hr = pTaskScheduler->Activate(m_sTaskName, IID_ITask, (IUnknown**)&pTask);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
DWORD dwFlags;
|
||||||
|
|
||||||
|
// Get task's current flags.
|
||||||
|
hr = pTask->GetFlags(&dwFlags);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
// Modify flags.
|
||||||
|
if (m_bEnable)
|
||||||
|
dwFlags &= ~TASK_FLAG_DISABLED;
|
||||||
|
else
|
||||||
|
dwFlags |= TASK_FLAG_DISABLED;
|
||||||
|
|
||||||
|
// Set flags.
|
||||||
|
hr = pTask->SetFlags(dwFlags);
|
||||||
|
if (SUCCEEDED(hr)) {
|
||||||
|
// Save the task.
|
||||||
|
CComQIPtr<IPersistFile> pTaskFile(pTask);
|
||||||
|
hr = pTaskFile->Save(NULL, TRUE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpCopyTask
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CMSITSCAOpCopyTask::CMSITSCAOpCopyTask(LPCWSTR pszSourceTaskName, LPCWSTR pszDestinationTaskName) :
|
||||||
|
CMSITSCAOpSrcDstTaskOperation(pszSourceTaskName, pszDestinationTaskName)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT CMSITSCAOpCopyTask::Execute(ITaskScheduler *pTaskScheduler)
|
||||||
|
{
|
||||||
|
assert(pTaskScheduler);
|
||||||
|
HRESULT hr;
|
||||||
|
CComPtr<ITask> pTask;
|
||||||
|
|
||||||
|
// Load the source task.
|
||||||
|
hr = pTaskScheduler->Activate(m_sSourceTaskName, IID_ITask, (IUnknown**)&pTask);
|
||||||
|
if (SUCCEEDED(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOperationList
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
CMSITSCAOperationList::CMSITSCAOperationList() :
|
||||||
|
CAtlList<CMSITSCAOp*>(sizeof(CMSITSCAOp*))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT CMSITSCAOperationList::Save(CAtlFile &f) const
|
||||||
|
{
|
||||||
|
POSITION pos;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
for (pos = GetHeadPosition(); pos;) {
|
||||||
|
const CMSITSCAOp *pOp = GetNext(pos);
|
||||||
|
if (dynamic_cast<const CMSITSCAOpDeleteTask*>(pOp))
|
||||||
|
hr = Save<CMSITSCAOpDeleteTask, OPERATION_DELETE_TASK>(f, pOp);
|
||||||
|
else if (dynamic_cast<const CMSITSCAOpEnableTask*>(pOp))
|
||||||
|
hr = Save<CMSITSCAOpEnableTask, OPERATION_ENABLE_TASK>(f, pOp);
|
||||||
|
else if (dynamic_cast<const CMSITSCAOpCopyTask*>(pOp))
|
||||||
|
hr = Save<CMSITSCAOpCopyTask, OPERATION_COPY_TASK>(f, pOp);
|
||||||
|
else {
|
||||||
|
// Unsupported type of operation.
|
||||||
|
assert(0);
|
||||||
|
hr = E_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HRESULT CMSITSCAOperationList::Execute(ITaskScheduler *pTaskScheduler, BOOL bContinueOnError)
|
||||||
|
{
|
||||||
|
POSITION pos;
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
for (pos = GetHeadPosition(); pos;) {
|
||||||
|
hr = GetNext(pos)->Execute(pTaskScheduler);
|
||||||
|
if (!bContinueOnError && FAILED(hr)) return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
233
MSICALib/MSITSCAOp.h
Normal file
233
MSICALib/MSITSCAOp.h
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#ifndef __MSITSCAOP_H__
|
||||||
|
#define __MSITSCAOP_H__
|
||||||
|
|
||||||
|
#include "MSITSCA.h"
|
||||||
|
#include <atlcoll.h>
|
||||||
|
#include <atlfile.h>
|
||||||
|
#include <atlstr.h>
|
||||||
|
#include <mstask.h>
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOp
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class CMSITSCAOp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMSITSCAOp();
|
||||||
|
|
||||||
|
virtual HRESULT Execute(ITaskScheduler *pTaskScheduler) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpSingleTaskOperation
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class CMSITSCAOpSingleTaskOperation : public CMSITSCAOp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMSITSCAOpSingleTaskOperation(LPCWSTR pszTaskName = L"");
|
||||||
|
|
||||||
|
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSingleTaskOperation &op);
|
||||||
|
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSingleTaskOperation &op);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CStringW m_sTaskName;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpsrcDstTaskOperation
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class CMSITSCAOpSrcDstTaskOperation : public CMSITSCAOp
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMSITSCAOpSrcDstTaskOperation(LPCWSTR pszSourceTaskName = L"", LPCWSTR pszDestinationTaskName = L"");
|
||||||
|
|
||||||
|
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpSrcDstTaskOperation &op);
|
||||||
|
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpSrcDstTaskOperation &op);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
CStringW m_sSourceTaskName;
|
||||||
|
CStringW m_sDestinationTaskName;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpDeleteTask
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class CMSITSCAOpDeleteTask : public CMSITSCAOpSingleTaskOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMSITSCAOpDeleteTask(LPCWSTR pszTaskName = L"");
|
||||||
|
virtual HRESULT Execute(ITaskScheduler *pTaskScheduler);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpEnableTask
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class CMSITSCAOpEnableTask : public CMSITSCAOpSingleTaskOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMSITSCAOpEnableTask(LPCWSTR pszTaskName = L"", BOOL bEnable = TRUE);
|
||||||
|
virtual HRESULT Execute(ITaskScheduler *pTaskScheduler);
|
||||||
|
|
||||||
|
friend inline HRESULT operator <<(CAtlFile &f, const CMSITSCAOpEnableTask &op);
|
||||||
|
friend inline HRESULT operator >>(CAtlFile &f, CMSITSCAOpEnableTask &op);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
BOOL m_bEnable;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOpCopyTask
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class CMSITSCAOpCopyTask : public CMSITSCAOpSrcDstTaskOperation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMSITSCAOpCopyTask(LPCWSTR pszSourceTaskName = L"", LPCWSTR pszDestinationTaskName = L"");
|
||||||
|
virtual HRESULT Execute(ITaskScheduler *pTaskScheduler);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// CMSITSCAOperationList
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
class CMSITSCAOperationList : public CAtlList<CMSITSCAOp*>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CMSITSCAOperationList();
|
||||||
|
|
||||||
|
HRESULT Save(CAtlFile &f) const;
|
||||||
|
HRESULT Load(CAtlFile &f);
|
||||||
|
void Free();
|
||||||
|
|
||||||
|
HRESULT Execute(ITaskScheduler *pTaskScheduler, BOOL bContinueOnError);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum OPERATION {
|
||||||
|
OPERATION_DELETE_TASK = 1,
|
||||||
|
OPERATION_ENABLE_TASK,
|
||||||
|
OPERATION_COPY_TASK
|
||||||
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
template <class T, int ID> inline static HRESULT Save(CAtlFile &f, const CMSITSCAOp *p);
|
||||||
|
template <class T> inline HRESULT LoadAndAddTail(CAtlFile &f);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Inline methods
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
template <class T, int ID> inline static HRESULT CMSITSCAOperationList::Save(CAtlFile &f, const CMSITSCAOp *p)
|
||||||
|
{
|
||||||
|
assert(p);
|
||||||
|
HRESULT hr;
|
||||||
|
const T *pp = dynamic_cast<const T*>(p);
|
||||||
|
assert(pp);
|
||||||
|
|
||||||
|
hr = f << (int)ID;
|
||||||
|
if (FAILED(hr)) return hr;
|
||||||
|
|
||||||
|
return f << *pp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <class T> inline HRESULT CMSITSCAOperationList::LoadAndAddTail(CAtlFile &f)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
// Create element.
|
||||||
|
T *p = new T();
|
||||||
|
if (!p) return E_OUTOFMEMORY;
|
||||||
|
|
||||||
|
// Load element from file.
|
||||||
|
hr = f >> *p;
|
||||||
|
if (FAILED(hr)) {
|
||||||
|
delete p;
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add element.
|
||||||
|
AddTail(p);
|
||||||
|
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__
|
@ -26,8 +26,8 @@
|
|||||||
#define _ATL_NO_AUTOMATIC_NAMESPACE
|
#define _ATL_NO_AUTOMATIC_NAMESPACE
|
||||||
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // Some CString constructors will be explicit
|
#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // Some CString constructors will be explicit
|
||||||
|
|
||||||
#include <afx.h>
|
|
||||||
#include <atlbase.h>
|
#include <atlbase.h>
|
||||||
|
#include <atlfile.h>
|
||||||
#include <atlstr.h>
|
#include <atlstr.h>
|
||||||
|
|
||||||
using namespace ATL;
|
using namespace ATL;
|
||||||
@ -35,11 +35,15 @@ using namespace ATL;
|
|||||||
#include "BuildNum.h"
|
#include "BuildNum.h"
|
||||||
|
|
||||||
#include "MSITSCA.h"
|
#include "MSITSCA.h"
|
||||||
|
#include "MSITSCAOp.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <corerror.h>
|
||||||
#include <msi.h>
|
#include <msi.h>
|
||||||
#include <msiquery.h>
|
#include <msiquery.h>
|
||||||
#include <mstask.h>
|
#include <mstask.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#define verify(expr) ((void)(expr))
|
#define verify(expr) ((void)(expr))
|
||||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user