diff --git a/.gitattributes b/.gitattributes
index 575c1f0..642a25b 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,4 +1,15 @@
* text=auto !eol
+AMSICA/AMSICA.props -text svneol=unset#text/xml
+AMSICA/AMSICALib/AMSICALib.vcxproj -text svneol=unset#text/xml
+AMSICA/AMSICALib/AMSICALib.vcxproj.filters -text svneol=unset#text/xml
+AMSICA/include/AMSICA.h -text
+AMSICA/include/StdAfx.h -text
+AMSICA/msm/Makefile -text svneol=unset#text/plain%3B+charset%3DWindows-1250
+AMSICA/res/en_GB.po -text
+AMSICA/src/AMSICA.cpp -text
+AMSICA/src/AMSICAFileOp.cpp -text
+AMSICA/src/AMSICATSOp.cpp -text
+AMSICA/src/StdAfx.cpp -text
MSITSCA/L10N/sl_SI.po -text
MSITSCA/MSITSCA.cpp -text
MSITSCA/MSITSCA.h -text
diff --git a/.gitignore b/.gitignore
index 135dfe5..5ad5115 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,22 @@
+AMSICA/AMSICALib/*.user
+AMSICA/AMSICALib/Debug.Win32
+AMSICA/AMSICALib/Debug.x64
+AMSICA/AMSICALib/Release.Win32
+AMSICA/AMSICALib/Release.x64
+AMSICA/AMSICALib/Unicode[!!-~]Debug.Win32
+AMSICA/AMSICALib/Unicode[!!-~]Debug.x64
+AMSICA/AMSICALib/Unicode[!!-~]Release.Win32
+AMSICA/AMSICALib/Unicode[!!-~]Release.x64
+AMSICA/msm/*-1.idt
+AMSICA/msm/*-2.idt
+AMSICA/msm/*-2.idtx
+AMSICA/msm/*.Binary-1
+AMSICA/msm/*.Binary-2
+AMSICA/msm/*.Icon-1
+AMSICA/msm/*.Icon-2
+AMSICA/msm/*.lst
+AMSICA/msm/*.msm
+AMSICA/res/*.mo
MSITSCA/*.user
MSITSCA/L10N/*.mo
MSITSCA/MSITSCA.Debug.Win32
diff --git a/AMSICA/AMSICA.props b/AMSICA/AMSICA.props
new file mode 100644
index 0000000..33ea2e9
--- /dev/null
+++ b/AMSICA/AMSICA.props
@@ -0,0 +1,20 @@
+
+
+
+ <_ProjectFileVersion>10.0.30319.1
+
+
+
+ $(IntDir);..\include;..\..\include;..\..\AOsn\include;%(AdditionalIncludeDirectories)
+
+
+ $(IntDir);..\include;..\..\include;..\res;..\..\AOsn\include;%(AdditionalIncludeDirectories)
+
+
+ ..\res\en_GB.po
+
+
+ ..\res\en_GB.po
+
+
+
\ No newline at end of file
diff --git a/AMSICA/AMSICALib/AMSICALib.vcxproj b/AMSICA/AMSICALib/AMSICALib.vcxproj
new file mode 100644
index 0000000..50830d2
--- /dev/null
+++ b/AMSICA/AMSICALib/AMSICALib.vcxproj
@@ -0,0 +1,236 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+ Unicode Debug
+ Win32
+
+
+ Unicode Debug
+ x64
+
+
+ Unicode Release
+ Win32
+
+
+ Unicode Release
+ x64
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
+ {8552EE55-177E-4F51-B51B-BAF7D6462CDE}
+ Win32Proj
+ AMSICALib
+ AMSICALib
+
+
+
+ StaticLibrary
+ Unicode
+ Static
+
+
+ StaticLibrary
+ MultiByte
+ Static
+
+
+ StaticLibrary
+ MultiByte
+ true
+ Static
+
+
+ StaticLibrary
+ Unicode
+ true
+ Static
+
+
+ StaticLibrary
+ Unicode
+ Static
+
+
+ StaticLibrary
+ MultiByte
+ Static
+
+
+ StaticLibrary
+ MultiByte
+ true
+ Static
+
+
+ StaticLibrary
+ Unicode
+ true
+ Static
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Windows
+ true
+
+
+
+
+
+ Windows
+ true
+
+
+
+
+
+ Windows
+ true
+
+
+
+
+
+ Windows
+ true
+
+
+
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AMSICA/AMSICALib/AMSICALib.vcxproj.filters b/AMSICA/AMSICALib/AMSICALib.vcxproj.filters
new file mode 100644
index 0000000..5ddd929
--- /dev/null
+++ b/AMSICA/AMSICALib/AMSICALib.vcxproj.filters
@@ -0,0 +1,39 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/AMSICA/include/AMSICA.h b/AMSICA/include/AMSICA.h
new file mode 100644
index 0000000..2905241
--- /dev/null
+++ b/AMSICA/include/AMSICA.h
@@ -0,0 +1,979 @@
+#ifndef __AMSICA_H__
+#define __AMSICA_H__
+
+////////////////////////////////////////////////////////////////////////////
+// Version
+////////////////////////////////////////////////////////////////////////////
+
+#define AMSICA_VERSION 0x01000000
+
+#define AMSICA_VERSION_MAJ 1
+#define AMSICA_VERSION_MIN 0
+#define AMSICA_VERSION_REV 0
+
+#define AMSICA_VERSION_STR "1.0"
+
+
+#if !defined(RC_INVOKED) && !defined(MIDL_PASS)
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+////////////////////////////////////////////////////////////////////
+// Error codes (last unused 2561L)
+////////////////////////////////////////////////////////////////////
+
+#define ERROR_INSTALL_DELETE_FAILED 2554L
+#define ERROR_INSTALL_MOVE_FAILED 2555L
+#define ERROR_INSTALL_TASK_CREATE_FAILED 2556L
+#define ERROR_INSTALL_TASK_DELETE_FAILED 2557L
+#define ERROR_INSTALL_TASK_ENABLE_FAILED 2558L
+#define ERROR_INSTALL_TASK_COPY_FAILED 2559L
+
+// Errors reported by MSITSCA
+#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_SCRIPT_READ 2560L
+#define ERROR_INSTALL_SCHEDULED_TASKS_PROPERTY_SET 2553L
+
+
+namespace AMSICA {
+
+
+////////////////////////////////////////////////////////////////////////////
+// Forward declarations
+////////////////////////////////////////////////////////////////////////////
+
+class CSession;
+
+
+////////////////////////////////////////////////////////////////////////////
+// COperation
+////////////////////////////////////////////////////////////////////////////
+
+class COperation
+{
+public:
+ COperation(int iTicks = 0);
+
+ virtual HRESULT Execute(CSession *pSession) = 0;
+
+ friend class COpList;
+ friend inline HRESULT operator <<(ATL::CAtlFile &f, const COperation &op);
+ friend inline HRESULT operator >>(ATL::CAtlFile &f, COperation &op);
+
+protected:
+ int m_iTicks; // Number of ticks on a progress bar required for this action execution
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTypeSingleString
+////////////////////////////////////////////////////////////////////////////
+
+class COpTypeSingleString : public COperation
+{
+public:
+ COpTypeSingleString(LPCWSTR pszValue = L"", int iTicks = 0);
+
+ friend inline HRESULT operator <<(ATL::CAtlFile &f, const AMSICA::COpTypeSingleString &op);
+ friend inline HRESULT operator >>(ATL::CAtlFile &f, AMSICA::COpTypeSingleString &op);
+
+protected:
+ ATL::CAtlStringW m_sValue;
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpDoubleStringOperation
+////////////////////////////////////////////////////////////////////////////
+
+class COpTypeSrcDstString : public COperation
+{
+public:
+ COpTypeSrcDstString(LPCWSTR pszValue1 = L"", LPCWSTR pszValue2 = L"", int iTicks = 0);
+
+ friend inline HRESULT operator <<(ATL::CAtlFile &f, const AMSICA::COpTypeSrcDstString &op);
+ friend inline HRESULT operator >>(ATL::CAtlFile &f, AMSICA::COpTypeSrcDstString &op);
+
+protected:
+ ATL::CAtlStringW m_sValue1;
+ ATL::CAtlStringW m_sValue2;
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTypeBoolean
+////////////////////////////////////////////////////////////////////////////
+
+class COpTypeBoolean : public COperation
+{
+public:
+ COpTypeBoolean(BOOL bValue = TRUE, int iTicks = 0);
+
+ friend inline HRESULT operator <<(ATL::CAtlFile &f, const AMSICA::COpTypeBoolean &op);
+ friend inline HRESULT operator >>(ATL::CAtlFile &f, AMSICA::COpTypeBoolean &op);
+
+protected:
+ BOOL m_bValue;
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpRollbackEnable
+////////////////////////////////////////////////////////////////////////////
+
+class COpRollbackEnable : public COpTypeBoolean
+{
+public:
+ COpRollbackEnable(BOOL bEnable = TRUE, int iTicks = 0);
+ virtual HRESULT Execute(CSession *pSession);
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpFileDelete
+////////////////////////////////////////////////////////////////////////////
+
+class COpFileDelete : public COpTypeSingleString
+{
+public:
+ COpFileDelete(LPCWSTR pszFileName = L"", int iTicks = 0);
+ virtual HRESULT Execute(CSession *pSession);
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpFileMove
+////////////////////////////////////////////////////////////////////////////
+
+class COpFileMove : public COpTypeSrcDstString
+{
+public:
+ COpFileMove(LPCWSTR pszFileSrc = L"", LPCWSTR pszFileDst = L"", int iTicks = 0);
+ virtual HRESULT Execute(CSession *pSession);
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTaskCreate
+////////////////////////////////////////////////////////////////////////////
+
+class COpTaskCreate : public COpTypeSingleString
+{
+public:
+ COpTaskCreate(LPCWSTR pszTaskName = L"", int iTicks = 0);
+ virtual ~COpTaskCreate();
+ virtual HRESULT Execute(CSession *pSession);
+
+ UINT SetFromRecord(MSIHANDLE hInstall, MSIHANDLE hRecord);
+ UINT SetTriggersFromView(MSIHANDLE hView);
+
+ friend inline HRESULT operator <<(ATL::CAtlFile &f, const AMSICA::COpTaskCreate &op);
+ friend inline HRESULT operator >>(ATL::CAtlFile &f, AMSICA::COpTaskCreate &op);
+
+protected:
+ ATL::CAtlStringW m_sApplicationName;
+ ATL::CAtlStringW m_sParameters;
+ ATL::CAtlStringW m_sWorkingDirectory;
+ ATL::CAtlStringW m_sAuthor;
+ ATL::CAtlStringW m_sComment;
+ DWORD m_dwFlags;
+ DWORD m_dwPriority;
+ ATL::CAtlStringW m_sAccountName;
+ ATL::CAtlStringW m_sPassword;
+ WORD m_wIdleMinutes;
+ WORD m_wDeadlineMinutes;
+ DWORD m_dwMaxRuntimeMS;
+
+ ATL::CAtlList m_lTriggers;
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTaskDelete
+////////////////////////////////////////////////////////////////////////////
+
+class COpTaskDelete : public COpTypeSingleString
+{
+public:
+ COpTaskDelete(LPCWSTR pszTaskName = L"", int iTicks = 0);
+ virtual HRESULT Execute(CSession *pSession);
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTaskEnable
+////////////////////////////////////////////////////////////////////////////
+
+class COpTaskEnable : public COpTypeSingleString
+{
+public:
+ COpTaskEnable(LPCWSTR pszTaskName = L"", BOOL bEnable = TRUE, int iTicks = 0);
+ virtual HRESULT Execute(CSession *pSession);
+
+ friend inline HRESULT operator <<(ATL::CAtlFile &f, const COpTaskEnable &op);
+ friend inline HRESULT operator >>(ATL::CAtlFile &f, COpTaskEnable &op);
+
+protected:
+ BOOL m_bEnable;
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTaskCopy
+////////////////////////////////////////////////////////////////////////////
+
+class COpTaskCopy : public COpTypeSrcDstString
+{
+public:
+ COpTaskCopy(LPCWSTR pszTaskSrc = L"", LPCWSTR pszTaskDst = L"", int iTicks = 0);
+ virtual HRESULT Execute(CSession *pSession);
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpList
+////////////////////////////////////////////////////////////////////////////
+
+class COpList : public COperation, public ATL::CAtlList
+{
+public:
+ COpList(int iTicks = 0);
+
+ void Free();
+ HRESULT LoadFromFile(LPCTSTR pszFileName);
+ HRESULT SaveToFile(LPCTSTR pszFileName) const;
+
+ virtual HRESULT Execute(CSession *pSession);
+
+ friend inline HRESULT operator <<(ATL::CAtlFile &f, const COpList &op);
+ friend inline HRESULT operator >>(ATL::CAtlFile &f, COpList &op);
+
+protected:
+ enum OPERATION {
+ OPERATION_ENABLE_ROLLBACK = 1,
+ OPERATION_DELETE_FILE,
+ OPERATION_MOVE_FILE,
+ OPERATION_CREATE_TASK,
+ OPERATION_DELETE_TASK,
+ OPERATION_ENABLE_TASK,
+ OPERATION_COPY_TASK,
+ OPERATION_SUBLIST
+ };
+
+protected:
+ template inline static HRESULT Save(ATL::CAtlFile &f, const COperation *p);
+ template inline HRESULT LoadAndAddTail(ATL::CAtlFile &f);
+};
+
+
+////////////////////////////////////////////////////////////////////////////
+// CSession
+////////////////////////////////////////////////////////////////////////////
+
+class CSession
+{
+public:
+ CSession();
+
+ MSIHANDLE m_hInstall; // Installer handle
+ BOOL m_bContinueOnError; // Continue execution on operation error?
+ BOOL m_bRollbackEnabled; // Is rollback enabled?
+ COpList m_olRollback; // Rollback operation list
+ COpList m_olCommit; // Commit operation list
+};
+
+} // namespace AMSICA
+
+
+////////////////////////////////////////////////////////////////////
+// Local includes
+////////////////////////////////////////////////////////////////////
+
+#include
+#include
+#include
+#include
+#include
+
+
+////////////////////////////////////////////////////////////////////
+// Inline Functions
+////////////////////////////////////////////////////////////////////
+
+inline UINT MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName, ATL::CAtlStringA &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, ATL::CAtlStringW &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, ATL::CAtlStringA &sValue)
+{
+ DWORD dwSize = 0;
+ UINT uiResult;
+
+ // Query the actual string length first.
+ uiResult = ::MsiRecordGetStringA(hRecord, iField, "", &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 = ::MsiRecordGetStringA(hRecord, iField, 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 MsiRecordGetStringW(MSIHANDLE hRecord, unsigned int iField, ATL::CAtlStringW &sValue)
+{
+ DWORD dwSize = 0;
+ UINT uiResult;
+
+ // Query the actual string length first.
+ uiResult = ::MsiRecordGetStringW(hRecord, iField, 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 = ::MsiRecordGetStringW(hRecord, iField, 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 MsiFormatRecordA(MSIHANDLE hInstall, MSIHANDLE hRecord, ATL::CAtlStringA &sValue)
+{
+ DWORD dwSize = 0;
+ 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 UINT MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord, ATL::CAtlStringW &sValue)
+{
+ DWORD dwSize = 0;
+ UINT uiResult;
+
+ // 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 UINT MsiRecordFormatStringA(MSIHANDLE hInstall, MSIHANDLE hRecord, unsigned int iField, ATL::CAtlStringA &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, ATL::CAtlStringW &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 UINT MsiGetTargetPathA(MSIHANDLE hInstall, LPCSTR szFolder, ATL::CAtlStringA &sValue)
+{
+ DWORD dwSize = 0;
+ UINT uiResult;
+
+ // Query the final string length first.
+ uiResult = ::MsiGetTargetPathA(hInstall, szFolder, "", &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 = ::MsiGetTargetPathA(hInstall, szFolder, 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 UINT MsiGetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder, ATL::CAtlStringW &sValue)
+{
+ DWORD dwSize = 0;
+ UINT uiResult;
+
+ // Query the final string length first.
+ uiResult = ::MsiGetTargetPathW(hInstall, szFolder, 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 = ::MsiGetTargetPathW(hInstall, szFolder, 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;
+ }
+}
+
+
+namespace AMSICA {
+
+////////////////////////////////////////////////////////////////////////////
+// Inline operators
+////////////////////////////////////////////////////////////////////////////
+
+inline HRESULT operator <<(ATL::CAtlFile &f, int i)
+{
+ return f.Write(&i, sizeof(int));
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, int &i)
+{
+ return f.Read(&i, sizeof(int));
+}
+
+
+inline HRESULT operator <<(ATL::CAtlFile &f, const ATL::CAtlStringA &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((LPCSTR)str, sizeof(CHAR) * iLength);
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, ATL::CAtlStringA &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 <<(ATL::CAtlFile &f, const ATL::CAtlStringW &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 >>(ATL::CAtlFile &f, ATL::CAtlStringW &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 <<(ATL::CAtlFile &f, const TASK_TRIGGER &ttData)
+{
+ return f.Write(&ttData, sizeof(TASK_TRIGGER));
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, TASK_TRIGGER &ttData)
+{
+ return f.Read(&ttData, sizeof(TASK_TRIGGER));
+}
+
+
+inline HRESULT operator <<(ATL::CAtlFile &f, const AMSICA::COperation &op)
+{
+ return f << op.m_iTicks;
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, COperation &op)
+{
+ return f >> op.m_iTicks;
+}
+
+
+inline HRESULT operator <<(ATL::CAtlFile &f, const COpTypeSingleString &op)
+{
+ HRESULT hr;
+
+ hr = f << (const COperation &)op;
+ if (FAILED(hr)) return hr;
+
+ return f << op.m_sValue;
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, COpTypeSingleString &op)
+{
+ HRESULT hr;
+
+ hr = f >> (COperation &)op;
+ if (FAILED(hr)) return hr;
+
+ return f >> op.m_sValue;
+}
+
+
+inline HRESULT operator <<(ATL::CAtlFile &f, const COpTypeSrcDstString &op)
+{
+ HRESULT hr;
+
+ hr = f << (const COperation &)op;
+ if (FAILED(hr)) return hr;
+
+ hr = f << op.m_sValue1;
+ if (FAILED(hr)) return hr;
+
+ return f << op.m_sValue2;
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, COpTypeSrcDstString &op)
+{
+ HRESULT hr;
+
+ hr = f >> (COperation &)op;
+ if (FAILED(hr)) return hr;
+
+ hr = f >> op.m_sValue1;
+ if (FAILED(hr)) return hr;
+
+ return f >> op.m_sValue2;
+}
+
+
+inline HRESULT operator <<(ATL::CAtlFile &f, const COpTypeBoolean &op)
+{
+ HRESULT hr;
+
+ hr = f << (const COperation &)op;
+ if (FAILED(hr)) return hr;
+
+ return f << (int)op.m_bValue;
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, COpTypeBoolean &op)
+{
+ int iValue;
+ HRESULT hr;
+
+ hr = f >> (COperation &)op;
+ if (FAILED(hr)) return hr;
+
+ hr = f >> iValue;
+ if (FAILED(hr)) return hr;
+ op.m_bValue = iValue ? TRUE : FALSE;
+
+ return S_OK;
+}
+
+
+inline HRESULT operator <<(ATL::CAtlFile &f, const COpTaskCreate &op)
+{
+ HRESULT hr;
+ POSITION pos;
+
+ hr = f << (const COpTypeSingleString&)op; 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_sAuthor; 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 >>(ATL::CAtlFile &f, COpTaskCreate &op)
+{
+ HRESULT hr;
+ DWORD dwValue;
+
+ hr = f >> (COpTypeSingleString&)op; 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_sAuthor; 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 <<(ATL::CAtlFile &f, const COpTaskEnable &op)
+{
+ HRESULT hr;
+
+ hr = f << (const COpTypeSingleString&)op;
+ if (FAILED(hr)) return hr;
+
+ return f << (int)op.m_bEnable;
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, COpTaskEnable &op)
+{
+ HRESULT hr;
+ int iTemp;
+
+ hr = f >> (COpTypeSingleString&)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 <<(ATL::CAtlFile &f, const COpList &list)
+{
+ POSITION pos;
+ HRESULT hr;
+
+ hr = f << (const COperation &)list;
+ if (FAILED(hr)) return hr;
+
+ hr = f << (int)list.GetCount();
+ if (FAILED(hr)) return hr;
+
+ for (pos = list.GetHeadPosition(); pos;) {
+ const COperation *pOp = list.GetNext(pos);
+ if (dynamic_cast(pOp))
+ hr = list.Save(f, pOp);
+ else if (dynamic_cast(pOp))
+ hr = list.Save(f, pOp);
+ else if (dynamic_cast(pOp))
+ hr = list.Save(f, pOp);
+ else if (dynamic_cast(pOp))
+ hr = list.Save(f, pOp);
+ else if (dynamic_cast(pOp))
+ hr = list.Save(f, pOp);
+ else if (dynamic_cast(pOp))
+ hr = list.Save(f, pOp);
+ else if (dynamic_cast(pOp))
+ hr = list.Save(f, pOp);
+ else if (dynamic_cast(pOp))
+ hr = list.Save(f, pOp);
+ else {
+ // Unsupported type of operation.
+ assert(0);
+ hr = E_UNEXPECTED;
+ }
+
+ if (FAILED(hr)) return hr;
+ }
+
+ return S_OK;
+}
+
+
+inline HRESULT operator >>(ATL::CAtlFile &f, COpList &list)
+{
+ HRESULT hr;
+ DWORD dwCount;
+
+ hr = f >> (COperation &)list;
+ if (FAILED(hr)) return hr;
+
+ hr = f >> (int&)dwCount;
+ if (FAILED(hr)) return hr;
+
+ while (dwCount--) {
+ int iTemp;
+
+ hr = f >> iTemp;
+ if (FAILED(hr)) return hr;
+
+ switch ((COpList::OPERATION)iTemp) {
+ case COpList::OPERATION_ENABLE_ROLLBACK:
+ hr = list.LoadAndAddTail(f);
+ break;
+ case COpList::OPERATION_DELETE_FILE:
+ hr = list.LoadAndAddTail(f);
+ break;
+ case COpList::OPERATION_MOVE_FILE:
+ hr = list.LoadAndAddTail(f);
+ break;
+ case COpList::OPERATION_CREATE_TASK:
+ hr = list.LoadAndAddTail(f);
+ break;
+ case COpList::OPERATION_DELETE_TASK:
+ hr = list.LoadAndAddTail(f);
+ break;
+ case COpList::OPERATION_ENABLE_TASK:
+ hr = list.LoadAndAddTail(f);
+ break;
+ case COpList::OPERATION_COPY_TASK:
+ hr = list.LoadAndAddTail(f);
+ break;
+ case COpList::OPERATION_SUBLIST:
+ hr = list.LoadAndAddTail(f);
+ break;
+ default:
+ // Unsupported type of operation.
+ assert(0);
+ hr = E_UNEXPECTED;
+ }
+
+ if (FAILED(hr)) return hr;
+ }
+
+ return S_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Inline methods
+////////////////////////////////////////////////////////////////////////////
+
+template inline static HRESULT COpList::Save(ATL::CAtlFile &f, const COperation *p)
+{
+ assert(p);
+ HRESULT hr;
+ const T *pp = dynamic_cast(p);
+ if (!pp) return E_UNEXPECTED;
+
+ hr = f << (int)ID;
+ if (FAILED(hr)) return hr;
+
+ return f << *pp;
+}
+
+
+template inline HRESULT COpList::LoadAndAddTail(ATL::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;
+}
+
+} // namespace AMSICA
+
+
+#endif // RC_INVOKED
+#endif // __AMSICA_H__
diff --git a/AMSICA/include/StdAfx.h b/AMSICA/include/StdAfx.h
new file mode 100644
index 0000000..d9c5475
--- /dev/null
+++ b/AMSICA/include/StdAfx.h
@@ -0,0 +1,48 @@
+// StdAfx.h : include file for standard system include files,
+// or project specific include files that are used frequently,
+// but are changed infrequently
+
+#pragma once
+
+#if defined(_UNICODE) && !defined(UNICODE)
+#define UNICODE
+#endif
+
+#if defined(_WIN32) && !defined(WIN32)
+#define WIN32
+#endif
+
+#ifndef STRICT
+#define STRICT
+#endif
+
+#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
+#define _WIN32_WINNT 0x0501 // Include Windows XP symbols
+#define _WINSOCKAPI_ // Prevent inclusion of winsock.h in windows.h
+#ifdef _WINDLL
+#define AMSICA_DLL // This is a DLL
+#endif
+//#define _ATL_APARTMENT_THREADED
+//#define _ATL_NO_AUTOMATIC_NAMESPACE
+//#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS // Some CString constructors will be explicit
+
+#include
+#include
+#include
+
+using namespace ATL;
+
+#include "BuildNum.h"
+
+#include "AMSICA.h"
+
+#include
+#include
+#include
+#include
+
+#ifdef NDEBUG
+#define verify(expr) ((void)(expr))
+#else
+#define verify(expr) assert(expr)
+#endif
diff --git a/AMSICA/msm/Makefile b/AMSICA/msm/Makefile
new file mode 100644
index 0000000..273750a
--- /dev/null
+++ b/AMSICA/msm/Makefile
@@ -0,0 +1,59 @@
+!INCLUDE "..\..\include\MSINast.mak"
+
+MSM_IMA_LOKALIZACIJO=1
+!IF "$(CFG)" == "ReleaseU"
+CFG_VC=Unicode Release
+!ELSEIF "$(CFG)" == "DebugU"
+CFG_VC=Unicode Debug
+!ELSE
+CFG_VC=$(CFG)
+!ENDIF
+
+
+######################################################################
+# Error
+
+Vse :: \
+ "$(JEZIK).$(CFG).$(PLAT).Error-1.idt" \
+ "$(JEZIK).$(CFG).$(PLAT).Error-2.idt"
+
+"$(JEZIK).$(CFG).$(PLAT).Error-1.idt" : "Makefile" "..\..\include\MSINast.mak"
+ -if exist $@ del /f /q $@
+ move /y << $@ > NUL
+Error Message
+i2 L0
+Error Error
+< NUL
+Error Message
+i2 L0
+1250 Error Error
+2554 Pri brisanju datoteke [2] je prilo do napake [3]. Obrnite se na svojo tehnino slubo.
+2555 Pri premikanju datoteke [2] v [3] je prilo do napake [4]. Obrnite se na svojo tehnino slubo.
+2556 Pri ustvarjanju razporejenega opravila [2] je prilo do napake [3]. Obrnite se na svojo tehnino slubo.
+2557 Pri brisanju razporejenega opravila [2] je prilo do napake [3]. Obrnite se na svojo tehnino slubo.
+2558 Pri o(ne)mogoanju razporejenega opravila [2] je prilo do napake [3]. Obrnite se na svojo tehnino slubo.
+2559 Pri kopiranju razporejenega opravila [2] v [3] je prilo do napake [4]. Obrnite se na svojo tehnino slubo.
+< NUL
+
+
+######################################################################
+# Izdelava modula MSM
+######################################################################
+
+!INCLUDE "..\..\AOsn\msm\MSM.mak"
diff --git a/AMSICA/res/en_GB.po b/AMSICA/res/en_GB.po
new file mode 100644
index 0000000..d5b6544
--- /dev/null
+++ b/AMSICA/res/en_GB.po
@@ -0,0 +1,77 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: MSITSCA\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-04-26 11:07+0100\n"
+"PO-Revision-Date: \n"
+"Last-Translator: Simon Rozman \n"
+"Language-Team: Amebis, d. o. o., Kamnik \n"
+"Language: en\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-SourceCharset: UTF-8\n"
+"X-Poedit-KeywordsList: __\n"
+"X-Poedit-Basepath: .\n"
+"X-Generator: Poedit 1.5.5\n"
+"X-Poedit-SearchPath-0: .\n"
+"X-Poedit-SearchPath-1: ..\\msm\n"
+
+# Privzeta kodna stran ANSI za ta jezik (desetiško)
+#: ..\msm/Sl.DebugU.Win32.Error-2.idtx:3 ..\msm/Sl.DebugU.x64.Error-2.idtx:3
+#: ..\msm/Sl.ReleaseU.Win32.Error-2.idtx:3
+msgid "1250"
+msgstr "1252"
+
+#: ..\msm/Sl.DebugU.Win32.Error-2.idtx:4 ..\msm/Sl.DebugU.x64.Error-2.idtx:4
+#: ..\msm/Sl.ReleaseU.Win32.Error-2.idtx:4
+msgid ""
+"Pri brisanju datoteke »[2]« je prišlo do napake [3]. Obrnite se na svojo "
+"tehnično službo."
+msgstr ""
+"Error [3] deleting \"[2]\" file. Please, contact your support personnel."
+
+#: ..\msm/Sl.DebugU.Win32.Error-2.idtx:7 ..\msm/Sl.DebugU.x64.Error-2.idtx:7
+#: ..\msm/Sl.ReleaseU.Win32.Error-2.idtx:7
+msgid ""
+"Pri brisanju razporejenega opravila »[2]« je prišlo do napake [3]. Obrnite "
+"se na svojo tehnično službo."
+msgstr ""
+"Error [3] deleting \"[2]\" scheduled task. Please, contact your support "
+"personnel."
+
+#: ..\msm/Sl.DebugU.Win32.Error-2.idtx:9 ..\msm/Sl.DebugU.x64.Error-2.idtx:9
+#: ..\msm/Sl.ReleaseU.Win32.Error-2.idtx:9
+msgid ""
+"Pri kopiranju razporejenega opravila »[2]« v »[3]« je prišlo do napake [4]. "
+"Obrnite se na svojo tehnično službo."
+msgstr ""
+"Error [4] copying \"[2]\" scheduled task to \"[3]\". Please, contact your "
+"support personnel."
+
+#: ..\msm/Sl.DebugU.Win32.Error-2.idtx:8 ..\msm/Sl.DebugU.x64.Error-2.idtx:8
+#: ..\msm/Sl.ReleaseU.Win32.Error-2.idtx:8
+msgid ""
+"Pri o(ne)mogočanju razporejenega opravila »[2]« je prišlo do napake [3]. "
+"Obrnite se na svojo tehnično službo."
+msgstr ""
+"Error [3] enabling/disabling \"[2]\" scheduled task. Please, contact your "
+"support personnel."
+
+#: ..\msm/Sl.DebugU.Win32.Error-2.idtx:5 ..\msm/Sl.DebugU.x64.Error-2.idtx:5
+#: ..\msm/Sl.ReleaseU.Win32.Error-2.idtx:5
+msgid ""
+"Pri premikanju datoteke »[2]« v »[3]« je prišlo do napake [4]. Obrnite se na "
+"svojo tehnično službo."
+msgstr ""
+"Error [4] moving \"[2]\" file to \"[3]\". Please, contact your support "
+"personnel."
+
+#: ..\msm/Sl.DebugU.Win32.Error-2.idtx:6 ..\msm/Sl.DebugU.x64.Error-2.idtx:6
+#: ..\msm/Sl.ReleaseU.Win32.Error-2.idtx:6
+msgid ""
+"Pri ustvarjanju razporejenega opravila »[2]« je prišlo do napake [3]. "
+"Obrnite se na svojo tehnično službo."
+msgstr ""
+"Error [3] creating \"[2]\" scheduled task. Please, contact your support "
+"personnel."
diff --git a/AMSICA/src/AMSICA.cpp b/AMSICA/src/AMSICA.cpp
new file mode 100644
index 0000000..64665a2
--- /dev/null
+++ b/AMSICA/src/AMSICA.cpp
@@ -0,0 +1,182 @@
+#include "StdAfx.h"
+
+
+namespace AMSICA {
+
+////////////////////////////////////////////////////////////////////////////
+// COperation
+////////////////////////////////////////////////////////////////////////////
+
+COperation::COperation(int iTicks) :
+ m_iTicks(iTicks)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTypeSingleString
+////////////////////////////////////////////////////////////////////////////
+
+COpTypeSingleString::COpTypeSingleString(LPCWSTR pszValue, int iTicks) :
+ m_sValue(pszValue),
+ COperation(iTicks)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpDoubleStringOperation
+////////////////////////////////////////////////////////////////////////////
+
+COpTypeSrcDstString::COpTypeSrcDstString(LPCWSTR pszValue1, LPCWSTR pszValue2, int iTicks) :
+ m_sValue1(pszValue1),
+ m_sValue2(pszValue2),
+ COperation(iTicks)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTypeBoolean
+////////////////////////////////////////////////////////////////////////////
+
+COpTypeBoolean::COpTypeBoolean(BOOL bValue, int iTicks) :
+ m_bValue(bValue),
+ COperation(iTicks)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpRollbackEnable
+////////////////////////////////////////////////////////////////////////////
+
+COpRollbackEnable::COpRollbackEnable(BOOL bEnable, int iTicks) :
+ COpTypeBoolean(bEnable, iTicks)
+{
+}
+
+
+HRESULT COpRollbackEnable::Execute(CSession *pSession)
+{
+ assert(pSession);
+
+ pSession->m_bRollbackEnabled = m_bValue;
+ return S_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpList
+////////////////////////////////////////////////////////////////////////////
+
+COpList::COpList(int iTicks) :
+ COperation(iTicks),
+ ATL::CAtlList(sizeof(COperation*))
+{
+}
+
+
+void COpList::Free()
+{
+ POSITION pos;
+
+ for (pos = GetHeadPosition(); pos;) {
+ COperation *pOp = GetNext(pos);
+ COpList *pOpList = dynamic_cast(pOp);
+
+ if (pOpList) {
+ // Recursivelly free sublists.
+ pOpList->Free();
+ }
+ delete pOp;
+ }
+
+ RemoveAll();
+}
+
+
+HRESULT COpList::LoadFromFile(LPCTSTR pszFileName)
+{
+ assert(pszFileName);
+ HRESULT hr;
+ ATL::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 COpList::SaveToFile(LPCTSTR pszFileName) const
+{
+ assert(pszFileName);
+ HRESULT hr;
+ ATL::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 COpList::Execute(CSession *pSession)
+{
+ assert(pSession);
+ POSITION pos;
+ HRESULT hr;
+ PMSIHANDLE hRecordProg = ::MsiCreateRecord(3);
+
+ // 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);
+ ::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg);
+
+ // Prepare hRecordProg for progress messages.
+ verify(::MsiRecordSetInteger(hRecordProg, 1, 2) == ERROR_SUCCESS);
+ verify(::MsiRecordSetInteger(hRecordProg, 3, 0) == ERROR_SUCCESS);
+
+ for (pos = GetHeadPosition(); pos;) {
+ COperation *pOp = GetNext(pos);
+ assert(pOp);
+
+ hr = pOp->Execute(pSession);
+ if (!pSession->m_bContinueOnError && FAILED(hr)) {
+ // Operation failed. Its Execute() method should have sent error message to Installer.
+ // Therefore, just quit here.
+ return hr;
+ }
+
+ verify(::MsiRecordSetInteger(hRecordProg, 2, pOp->m_iTicks) == ERROR_SUCCESS);
+ if (::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg) == IDCANCEL)
+ return AtlHresultFromWin32(ERROR_INSTALL_USEREXIT);
+ }
+
+ verify(::MsiRecordSetInteger(hRecordProg, 2, m_iTicks) == ERROR_SUCCESS);
+ ::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_PROGRESS, hRecordProg);
+
+ return S_OK;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// CSession
+////////////////////////////////////////////////////////////////////////////
+
+CSession::CSession() :
+ m_bContinueOnError(FALSE),
+ m_bRollbackEnabled(FALSE)
+{
+}
+
+
+} // namespace AMSICA
diff --git a/AMSICA/src/AMSICAFileOp.cpp b/AMSICA/src/AMSICAFileOp.cpp
new file mode 100644
index 0000000..b9b8b1b
--- /dev/null
+++ b/AMSICA/src/AMSICAFileOp.cpp
@@ -0,0 +1,90 @@
+#include "StdAfx.h"
+
+
+namespace AMSICA {
+
+////////////////////////////////////////////////////////////////////////////
+// COpFileDelete
+////////////////////////////////////////////////////////////////////////////
+
+COpFileDelete::COpFileDelete(LPCWSTR pszFileName, int iTicks) :
+ COpTypeSingleString(pszFileName, iTicks)
+{
+}
+
+
+HRESULT COpFileDelete::Execute(CSession *pSession)
+{
+ assert(pSession);
+ DWORD dwError;
+
+ if (pSession->m_bRollbackEnabled) {
+ ATL::CAtlStringW sBackupName;
+ UINT uiCount = 0;
+
+ 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_ALREADY_EXISTS);
+ if (dwError == ERROR_SUCCESS) {
+ // Order rollback action to restore from backup copy.
+ pSession->m_olRollback.AddHead(new COpFileMove(sBackupName, m_sValue));
+
+ // Order commit action to delete backup copy.
+ pSession->m_olCommit.AddTail(new COpFileDelete(sBackupName));
+ }
+ } else {
+ // Delete the file.
+ dwError = ::DeleteFileW(m_sValue) ? ERROR_SUCCESS : ::GetLastError();
+ }
+
+ if (dwError == ERROR_SUCCESS || dwError == ERROR_FILE_NOT_FOUND)
+ return S_OK;
+ else {
+ PMSIHANDLE hRecordProg = ::MsiCreateRecord(3);
+ verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_DELETE_FAILED) == ERROR_SUCCESS);
+ verify(::MsiRecordSetStringW(hRecordProg, 2, m_sValue ) == ERROR_SUCCESS);
+ verify(::MsiRecordSetInteger(hRecordProg, 3, dwError ) == ERROR_SUCCESS);
+ ::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
+ return AtlHresultFromWin32(dwError);
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpFileMove
+////////////////////////////////////////////////////////////////////////////
+
+COpFileMove::COpFileMove(LPCWSTR pszFileSrc, LPCWSTR pszFileDst, int iTicks) :
+ COpTypeSrcDstString(pszFileSrc, pszFileDst, iTicks)
+{
+}
+
+
+HRESULT COpFileMove::Execute(CSession *pSession)
+{
+ assert(pSession);
+ DWORD dwError;
+
+ // Move the file.
+ dwError = ::MoveFileW(m_sValue1, m_sValue2) ? ERROR_SUCCESS : ::GetLastError();
+ if (dwError == ERROR_SUCCESS) {
+ if (pSession->m_bRollbackEnabled) {
+ // Order rollback action to move it back.
+ pSession->m_olRollback.AddHead(new COpFileMove(m_sValue2, m_sValue1));
+ }
+
+ return S_OK;
+ } else {
+ PMSIHANDLE hRecordProg = ::MsiCreateRecord(4);
+ verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_MOVE_FAILED) == ERROR_SUCCESS);
+ verify(::MsiRecordSetStringW(hRecordProg, 2, m_sValue1 ) == ERROR_SUCCESS);
+ verify(::MsiRecordSetStringW(hRecordProg, 3, m_sValue2 ) == ERROR_SUCCESS);
+ verify(::MsiRecordSetInteger(hRecordProg, 4, dwError ) == ERROR_SUCCESS);
+ ::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
+ return AtlHresultFromWin32(dwError);
+ }
+}
+
+} // namespace AMSICA
diff --git a/AMSICA/src/AMSICATSOp.cpp b/AMSICA/src/AMSICATSOp.cpp
new file mode 100644
index 0000000..50950c2
--- /dev/null
+++ b/AMSICA/src/AMSICATSOp.cpp
@@ -0,0 +1,1040 @@
+#include "StdAfx.h"
+
+#pragma comment(lib, "taskschd.lib")
+
+
+namespace AMSICA {
+
+////////////////////////////////////////////////////////////////////////////
+// COpTaskCreate
+////////////////////////////////////////////////////////////////////////////
+
+COpTaskCreate::COpTaskCreate(LPCWSTR pszTaskName, int iTicks) :
+ m_dwFlags(0),
+ m_dwPriority(NORMAL_PRIORITY_CLASS),
+ m_wIdleMinutes(0),
+ m_wDeadlineMinutes(0),
+ m_dwMaxRuntimeMS(INFINITE),
+ COpTypeSingleString(pszTaskName, iTicks)
+{
+}
+
+
+COpTaskCreate::~COpTaskCreate()
+{
+ // Clear the password in memory.
+ int iLength = m_sPassword.GetLength();
+ ::SecureZeroMemory(m_sPassword.GetBuffer(iLength), sizeof(WCHAR) * iLength);
+ m_sPassword.ReleaseBuffer(0);
+}
+
+
+HRESULT COpTaskCreate::Execute(CSession *pSession)
+{
+ HRESULT hr;
+ PMSIHANDLE hRecordMsg = ::MsiCreateRecord(1);
+ CComPtr pService;
+ POSITION pos;
+
+ // Display our custom message in the progress bar.
+ verify(::MsiRecordSetStringW(hRecordMsg, 1, m_sValue) == ERROR_SUCCESS);
+ if (MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_ACTIONDATA, hRecordMsg) == IDCANCEL)
+ return AtlHresultFromWin32(ERROR_INSTALL_USEREXIT);
+
+ {
+ // Delete existing task first.
+ // Since task deleting is a complicated job (when rollback/commit support is required), and we do have an operation just for that, we use it.
+ // Don't worry, COpTaskDelete::Execute() returns S_OK if task doesn't exist.
+ COpTaskDelete opDeleteTask(m_sValue);
+ hr = opDeleteTask.Execute(pSession);
+ if (FAILED(hr)) goto finish;
+ }
+
+ hr = pService.CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER);
+ if (SUCCEEDED(hr)) {
+ // Windows Vista or newer.
+ CComVariant vEmpty;
+ CComPtr pTaskDefinition;
+ CComPtr pTaskSettings;
+ CComPtr pPrincipal;
+ CComPtr pActionCollection;
+ CComPtr pAction;
+ CComPtr pIdleSettings;
+ CComPtr pExecAction;
+ CComPtr pRegististrationInfo;
+ CComPtr pTriggerCollection;
+ CComPtr pTaskFolder;
+ CComPtr pTask;
+ ATL::CAtlStringW str;
+ UINT iTrigger;
+ TASK_LOGON_TYPE logonType;
+ CComBSTR bstrContext(L"Author");
+
+ // Connect to local task service.
+ hr = pService->Connect(vEmpty, vEmpty, vEmpty, vEmpty);
+ if (FAILED(hr)) goto finish;
+
+ // Prepare the definition for a new task.
+ hr = pService->NewTask(0, &pTaskDefinition);
+ if (FAILED(hr)) goto finish;
+
+ // Get the task's settings.
+ hr = pTaskDefinition->get_Settings(&pTaskSettings);
+ if (FAILED(hr)) goto finish;
+
+ // Enable/disable task.
+ 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 COpTaskEnable(m_sValue, TRUE));
+ m_dwFlags |= TASK_FLAG_DISABLED;
+ }
+ hr = pTaskSettings->put_Enabled(m_dwFlags & TASK_FLAG_DISABLED ? VARIANT_FALSE : VARIANT_TRUE); if (FAILED(hr)) goto finish;
+
+ // Get task actions.
+ hr = pTaskDefinition->get_Actions(&pActionCollection);
+ if (FAILED(hr)) goto finish;
+
+ // Add execute action.
+ hr = pActionCollection->Create(TASK_ACTION_EXEC, &pAction);
+ if (FAILED(hr)) goto finish;
+ hr = pAction.QueryInterface(&pExecAction);
+ if (FAILED(hr)) goto finish;
+
+ // Configure the action.
+ hr = pExecAction->put_Path (CComBSTR(m_sApplicationName )); if (FAILED(hr)) goto finish;
+ hr = pExecAction->put_Arguments (CComBSTR(m_sParameters )); if (FAILED(hr)) goto finish;
+ hr = pExecAction->put_WorkingDirectory(CComBSTR(m_sWorkingDirectory)); if (FAILED(hr)) goto finish;
+
+ // Set task description.
+ hr = pTaskDefinition->get_RegistrationInfo(&pRegististrationInfo);
+ if (FAILED(hr)) goto finish;
+ hr = pRegististrationInfo->put_Author(CComBSTR(m_sAuthor));
+ if (FAILED(hr)) goto finish;
+ hr = pRegististrationInfo->put_Description(CComBSTR(m_sComment));
+ if (FAILED(hr)) goto finish;
+
+ // Configure task "flags".
+ if (m_dwFlags & TASK_FLAG_DELETE_WHEN_DONE) {
+ hr = pTaskSettings->put_DeleteExpiredTaskAfter(CComBSTR(L"PT24H"));
+ if (FAILED(hr)) goto finish;
+ }
+ hr = pTaskSettings->put_Hidden(m_dwFlags & TASK_FLAG_HIDDEN ? VARIANT_TRUE : VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+ hr = pTaskSettings->put_WakeToRun(m_dwFlags & TASK_FLAG_SYSTEM_REQUIRED ? VARIANT_TRUE : VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+ hr = pTaskSettings->put_DisallowStartIfOnBatteries(m_dwFlags & TASK_FLAG_DONT_START_IF_ON_BATTERIES ? VARIANT_TRUE : VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+ hr = pTaskSettings->put_StopIfGoingOnBatteries(m_dwFlags & TASK_FLAG_KILL_IF_GOING_ON_BATTERIES ? VARIANT_TRUE : VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+
+ // Configure task priority.
+ hr = pTaskSettings->put_Priority(
+ m_dwPriority == REALTIME_PRIORITY_CLASS ? 0 :
+ m_dwPriority == HIGH_PRIORITY_CLASS ? 1 :
+ m_dwPriority == ABOVE_NORMAL_PRIORITY_CLASS ? 2 :
+ m_dwPriority == NORMAL_PRIORITY_CLASS ? 4 :
+ m_dwPriority == BELOW_NORMAL_PRIORITY_CLASS ? 7 :
+ m_dwPriority == IDLE_PRIORITY_CLASS ? 9 : 7);
+ if (FAILED(hr)) goto finish;
+
+ // Get task principal.
+ hr = pTaskDefinition->get_Principal(&pPrincipal);
+ if (FAILED(hr)) goto finish;
+
+ if (m_sAccountName.IsEmpty()) {
+ logonType = TASK_LOGON_SERVICE_ACCOUNT;
+ hr = pPrincipal->put_LogonType(logonType); if (FAILED(hr)) goto finish;
+ hr = pPrincipal->put_UserId(CComBSTR(L"S-1-5-18")); if (FAILED(hr)) goto finish;
+ hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST); if (FAILED(hr)) goto finish;
+ } else if (m_dwFlags & TASK_FLAG_RUN_ONLY_IF_LOGGED_ON) {
+ logonType = TASK_LOGON_INTERACTIVE_TOKEN;
+ hr = pPrincipal->put_LogonType(logonType); if (FAILED(hr)) goto finish;
+ hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_LUA); if (FAILED(hr)) goto finish;
+ } else {
+ logonType = TASK_LOGON_PASSWORD;
+ hr = pPrincipal->put_LogonType(logonType); if (FAILED(hr)) goto finish;
+ hr = pPrincipal->put_UserId(CComBSTR(m_sAccountName)); if (FAILED(hr)) goto finish;
+ hr = pPrincipal->put_RunLevel(TASK_RUNLEVEL_HIGHEST); if (FAILED(hr)) goto finish;
+ }
+
+ // Connect principal and action collection.
+ hr = pPrincipal->put_Id(bstrContext);
+ if (FAILED(hr)) goto finish;
+ hr = pActionCollection->put_Context(bstrContext);
+ if (FAILED(hr)) goto finish;
+
+ // Configure idle settings.
+ hr = pTaskSettings->put_RunOnlyIfIdle(m_dwFlags & TASK_FLAG_START_ONLY_IF_IDLE ? VARIANT_TRUE : VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+ hr = pTaskSettings->get_IdleSettings(&pIdleSettings);
+ if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uS", m_wIdleMinutes*60);
+ hr = pIdleSettings->put_IdleDuration(CComBSTR(str));
+ if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uS", m_wDeadlineMinutes*60);
+ hr = pIdleSettings->put_WaitTimeout(CComBSTR(str));
+ if (FAILED(hr)) goto finish;
+ hr = pIdleSettings->put_RestartOnIdle(m_dwFlags & TASK_FLAG_RESTART_ON_IDLE_RESUME ? VARIANT_TRUE : VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+ hr = pIdleSettings->put_StopOnIdleEnd(m_dwFlags & TASK_FLAG_KILL_ON_IDLE_END ? VARIANT_TRUE : VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+
+ // Configure task runtime limit.
+ str.Format(L"PT%uS", m_dwMaxRuntimeMS != INFINITE ? (m_dwMaxRuntimeMS + 500) / 1000 : 0);
+ hr = pTaskSettings->put_ExecutionTimeLimit(CComBSTR(str));
+ if (FAILED(hr)) goto finish;
+
+ // Get task trigger colection.
+ hr = pTaskDefinition->get_Triggers(&pTriggerCollection);
+ if (FAILED(hr)) goto finish;
+
+ // Add triggers.
+ for (pos = m_lTriggers.GetHeadPosition(), iTrigger = 0; pos; iTrigger++) {
+ CComPtr pTrigger;
+ TASK_TRIGGER &ttData = m_lTriggers.GetNext(pos);
+
+ switch (ttData.TriggerType) {
+ case TASK_TIME_TRIGGER_ONCE: {
+ CComPtr pTriggerTime;
+ hr = pTriggerCollection->Create(TASK_TRIGGER_TIME, &pTrigger); if (FAILED(hr)) goto finish;
+ hr = pTrigger.QueryInterface(&pTriggerTime); if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.wRandomMinutesInterval);
+ hr = pTriggerTime->put_RandomDelay(CComBSTR(str)); if (FAILED(hr)) goto finish;
+ break;
+ }
+
+ case TASK_TIME_TRIGGER_DAILY: {
+ CComPtr pTriggerDaily;
+ hr = pTriggerCollection->Create(TASK_TRIGGER_DAILY, &pTrigger); if (FAILED(hr)) goto finish;
+ hr = pTrigger.QueryInterface(&pTriggerDaily); if (FAILED(hr)) goto finish;
+ hr = pTriggerDaily->put_DaysInterval(ttData.Type.Daily.DaysInterval); if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.wRandomMinutesInterval);
+ hr = pTriggerDaily->put_RandomDelay(CComBSTR(str)); if (FAILED(hr)) goto finish;
+ break;
+ }
+
+ case TASK_TIME_TRIGGER_WEEKLY: {
+ CComPtr pTriggerWeekly;
+ hr = pTriggerCollection->Create(TASK_TRIGGER_WEEKLY, &pTrigger); if (FAILED(hr)) goto finish;
+ hr = pTrigger.QueryInterface(&pTriggerWeekly); if (FAILED(hr)) goto finish;
+ hr = pTriggerWeekly->put_WeeksInterval(ttData.Type.Weekly.WeeksInterval); if (FAILED(hr)) goto finish;
+ hr = pTriggerWeekly->put_DaysOfWeek(ttData.Type.Weekly.rgfDaysOfTheWeek); if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.wRandomMinutesInterval);
+ hr = pTriggerWeekly->put_RandomDelay(CComBSTR(str)); if (FAILED(hr)) goto finish;
+ break;
+ }
+
+ case TASK_TIME_TRIGGER_MONTHLYDATE: {
+ CComPtr pTriggerMonthly;
+ hr = pTriggerCollection->Create(TASK_TRIGGER_MONTHLY, &pTrigger); if (FAILED(hr)) goto finish;
+ hr = pTrigger.QueryInterface(&pTriggerMonthly); if (FAILED(hr)) goto finish;
+ hr = pTriggerMonthly->put_DaysOfMonth(ttData.Type.MonthlyDate.rgfDays); if (FAILED(hr)) goto finish;
+ hr = pTriggerMonthly->put_MonthsOfYear(ttData.Type.MonthlyDate.rgfMonths); if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.wRandomMinutesInterval);
+ hr = pTriggerMonthly->put_RandomDelay(CComBSTR(str)); if (FAILED(hr)) goto finish;
+ break;
+ }
+
+ case TASK_TIME_TRIGGER_MONTHLYDOW: {
+ CComPtr pTriggerMonthlyDOW;
+ hr = pTriggerCollection->Create(TASK_TRIGGER_MONTHLYDOW, &pTrigger); if (FAILED(hr)) goto finish;
+ hr = pTrigger.QueryInterface(&pTriggerMonthlyDOW); if (FAILED(hr)) goto finish;
+ hr = pTriggerMonthlyDOW->put_WeeksOfMonth(
+ ttData.Type.MonthlyDOW.wWhichWeek == TASK_FIRST_WEEK ? 0x01 :
+ ttData.Type.MonthlyDOW.wWhichWeek == TASK_SECOND_WEEK ? 0x02 :
+ ttData.Type.MonthlyDOW.wWhichWeek == TASK_THIRD_WEEK ? 0x04 :
+ ttData.Type.MonthlyDOW.wWhichWeek == TASK_FOURTH_WEEK ? 0x08 :
+ ttData.Type.MonthlyDOW.wWhichWeek == TASK_LAST_WEEK ? 0x10 : 0x00); if (FAILED(hr)) goto finish;
+ hr = pTriggerMonthlyDOW->put_DaysOfWeek(ttData.Type.MonthlyDOW.rgfDaysOfTheWeek); if (FAILED(hr)) goto finish;
+ hr = pTriggerMonthlyDOW->put_MonthsOfYear(ttData.Type.MonthlyDOW.rgfMonths); if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.wRandomMinutesInterval);
+ hr = pTriggerMonthlyDOW->put_RandomDelay(CComBSTR(str)); if (FAILED(hr)) goto finish;
+ break;
+ }
+
+ case TASK_EVENT_TRIGGER_ON_IDLE: {
+ hr = pTriggerCollection->Create(TASK_TRIGGER_IDLE, &pTrigger); if (FAILED(hr)) goto finish;
+ break;
+ }
+
+ case TASK_EVENT_TRIGGER_AT_SYSTEMSTART: {
+ CComPtr pTriggerBoot;
+ hr = pTriggerCollection->Create(TASK_TRIGGER_BOOT, &pTrigger); if (FAILED(hr)) goto finish;
+ hr = pTrigger.QueryInterface(&pTriggerBoot); if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.wRandomMinutesInterval);
+ hr = pTriggerBoot->put_Delay(CComBSTR(str)); if (FAILED(hr)) goto finish;
+ break;
+ }
+
+ case TASK_EVENT_TRIGGER_AT_LOGON: {
+ CComPtr pTriggerLogon;
+ hr = pTriggerCollection->Create(TASK_TRIGGER_LOGON, &pTrigger); if (FAILED(hr)) goto finish;
+ hr = pTrigger.QueryInterface(&pTriggerLogon); if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.wRandomMinutesInterval);
+ hr = pTriggerLogon->put_Delay(CComBSTR(str)); if (FAILED(hr)) goto finish;
+ break;
+ }
+ }
+
+ // Set trigger ID.
+ str.Format(L"%u", iTrigger);
+ hr = pTrigger->put_Id(CComBSTR(str));
+ if (FAILED(hr)) goto finish;
+
+ // Set trigger start date.
+ str.Format(L"%04u-%02u-%02uT%02u:%02u:00", ttData.wBeginYear, ttData.wBeginMonth, ttData.wBeginDay, ttData.wStartHour, ttData.wStartMinute);
+ hr = pTrigger->put_StartBoundary(CComBSTR(str));
+ if (FAILED(hr)) goto finish;
+
+ if (ttData.rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE) {
+ // Set trigger end date.
+ str.Format(L"%04u-%02u-%02u", ttData.wEndYear, ttData.wEndMonth, ttData.wEndDay);
+ hr = pTrigger->put_EndBoundary(CComBSTR(str));
+ if (FAILED(hr)) goto finish;
+ }
+
+ // Set trigger repetition duration and interval.
+ if (ttData.MinutesDuration || ttData.MinutesInterval) {
+ CComPtr pRepetitionPattern;
+
+ hr = pTrigger->get_Repetition(&pRepetitionPattern);
+ if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.MinutesDuration);
+ hr = pRepetitionPattern->put_Duration(CComBSTR(str));
+ if (FAILED(hr)) goto finish;
+ str.Format(L"PT%uM", ttData.MinutesInterval);
+ hr = pRepetitionPattern->put_Interval(CComBSTR(str));
+ if (FAILED(hr)) goto finish;
+ hr = pRepetitionPattern->put_StopAtDurationEnd(ttData.rgFlags & TASK_TRIGGER_FLAG_KILL_AT_DURATION_END ? VARIANT_TRUE : VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+ }
+
+ // Enable/disable trigger.
+ hr = pTrigger->put_Enabled(ttData.rgFlags & TASK_TRIGGER_FLAG_DISABLED ? VARIANT_FALSE : VARIANT_TRUE);
+ if (FAILED(hr)) goto finish;
+ }
+
+ // Get the task folder.
+ hr = pService->GetFolder(CComBSTR(L"\\"), &pTaskFolder);
+ if (FAILED(hr)) goto finish;
+
+#ifdef _DEBUG
+ CComBSTR xml;
+ hr = pTaskDefinition->get_XmlText(&xml);
+#endif
+
+ // Register the task.
+ hr = pTaskFolder->RegisterTaskDefinition(
+ CComBSTR(m_sValue), // path
+ pTaskDefinition, // pDefinition
+ TASK_CREATE, // flags
+ vEmpty, // userId
+ logonType != TASK_LOGON_SERVICE_ACCOUNT && !m_sPassword.IsEmpty() ? CComVariant(m_sPassword) : vEmpty, // password
+ logonType, // logonType
+ vEmpty, // sddl
+ &pTask); // ppTask
+ } else {
+ // Windows XP or older.
+ CComPtr pTaskScheduler;
+ CComPtr pTask;
+
+ // Get task scheduler object.
+ hr = pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL);
+ if (FAILED(hr)) goto finish;
+
+ // Create the new task.
+ hr = 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 COpTaskDelete(m_sValue));
+ }
+ if (FAILED(hr)) goto finish;
+
+ // Set its properties.
+ hr = pTask->SetApplicationName (m_sApplicationName ); if (FAILED(hr)) goto finish;
+ hr = pTask->SetParameters (m_sParameters ); if (FAILED(hr)) goto finish;
+ hr = pTask->SetWorkingDirectory(m_sWorkingDirectory); if (FAILED(hr)) goto finish;
+ hr = pTask->SetComment (m_sComment ); if (FAILED(hr)) goto finish;
+ 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 COpTaskEnable(m_sValue, TRUE));
+ m_dwFlags |= TASK_FLAG_DISABLED;
+ }
+ hr = pTask->SetFlags (m_dwFlags ); if (FAILED(hr)) goto finish;
+ hr = pTask->SetPriority (m_dwPriority ); if (FAILED(hr)) goto finish;
+
+ // Set task credentials.
+ hr = m_sAccountName.IsEmpty() ?
+ pTask->SetAccountInformation(L"", NULL ) :
+ pTask->SetAccountInformation(m_sAccountName, m_sPassword);
+ if (FAILED(hr)) goto finish;
+
+ hr = pTask->SetIdleWait (m_wIdleMinutes, m_wDeadlineMinutes); if (FAILED(hr)) goto finish;
+ hr = pTask->SetMaxRunTime(m_dwMaxRuntimeMS ); if (FAILED(hr)) goto finish;
+
+ // Add triggers.
+ for (pos = m_lTriggers.GetHeadPosition(); pos;) {
+ WORD wTriggerIdx;
+ CComPtr pTrigger;
+ TASK_TRIGGER ttData = m_lTriggers.GetNext(pos); // Don't use reference! We don't want to modify original trigger data by adding random startup delay.
+
+ hr = pTask->CreateTrigger(&wTriggerIdx, &pTrigger);
+ if (FAILED(hr)) goto finish;
+
+ if (ttData.wRandomMinutesInterval) {
+ // Windows XP doesn't support random startup delay. However, we can add fixed "random" delay when creating the trigger.
+ WORD wStartTime = ttData.wStartHour * 60 + ttData.wStartMinute + (WORD)::MulDiv(rand(), ttData.wRandomMinutesInterval, RAND_MAX);
+ FILETIME ftValue;
+ SYSTEMTIME stValue;
+ ULONGLONG ullValue;
+
+ // Convert MDY date to numerical date (SYSTEMTIME -> FILETIME -> ULONGLONG).
+ memset(&stValue, 0, sizeof(SYSTEMTIME));
+ stValue.wYear = ttData.wBeginYear;
+ stValue.wMonth = ttData.wBeginMonth;
+ stValue.wDay = ttData.wBeginDay;
+ verify(::SystemTimeToFileTime(&stValue, &ftValue));
+ ullValue = ((ULONGLONG)(ftValue.dwHighDateTime) << 32) | ftValue.dwLowDateTime;
+
+ // Wrap days.
+ while (wStartTime >= 1440) {
+ ullValue += (ULONGLONG)864000000000;
+ wStartTime -= 1440;
+ }
+
+ // Convert numerical date to DMY (ULONGLONG -> FILETIME -> SYSTEMTIME).
+ ftValue.dwHighDateTime = ullValue >> 32;
+ ftValue.dwLowDateTime = ullValue & 0xffffffff;
+ verify(::FileTimeToSystemTime(&ftValue, &stValue));
+
+ // Set new trigger date and time.
+ ttData.wBeginYear = stValue.wYear;
+ ttData.wBeginMonth = stValue.wMonth;
+ ttData.wBeginDay = stValue.wDay;
+ ttData.wStartHour = wStartTime / 60;
+ ttData.wStartMinute = wStartTime % 60;
+ }
+
+ hr = pTrigger->SetTrigger(&ttData);
+ if (FAILED(hr)) goto finish;
+ }
+
+ // Save the task.
+ CComQIPtr pTaskFile(pTask);
+ if (!pTaskFile) { hr = E_NOINTERFACE; goto finish; }
+ hr = pTaskFile->Save(NULL, TRUE);
+ }
+
+finish:
+ if (FAILED(hr)) {
+ PMSIHANDLE hRecordProg = ::MsiCreateRecord(3);
+ verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_TASK_CREATE_FAILED) == ERROR_SUCCESS);
+ verify(::MsiRecordSetStringW(hRecordProg, 2, m_sValue ) == ERROR_SUCCESS);
+ verify(::MsiRecordSetInteger(hRecordProg, 3, hr ) == ERROR_SUCCESS);
+ ::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
+ }
+ return hr;
+}
+
+
+UINT COpTaskCreate::SetFromRecord(MSIHANDLE hInstall, MSIHANDLE hRecord)
+{
+ UINT uiResult;
+ int iValue;
+ ATL::CAtlStringW sFolder;
+
+ 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 = ::MsiRecordGetStringW(hRecord, 5, sFolder);
+ if (uiResult != ERROR_SUCCESS) return uiResult;
+ uiResult = ::MsiGetTargetPathW(hInstall, sFolder, m_sWorkingDirectory);
+ if (uiResult != ERROR_SUCCESS) return uiResult;
+ if (!m_sWorkingDirectory.IsEmpty() && m_sWorkingDirectory.GetAt(m_sWorkingDirectory.GetLength() - 1) == L'\\') {
+ // Trim trailing backslash.
+ m_sWorkingDirectory.Truncate(m_sWorkingDirectory.GetLength() - 1);
+ }
+
+ uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 10, m_sAuthor);
+ if (uiResult != ERROR_SUCCESS) return uiResult;
+
+ uiResult = ::MsiRecordFormatStringW(hInstall, hRecord, 11, 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, 12);
+ m_wIdleMinutes = iValue != MSI_NULL_INTEGER ? (WORD)iValue : 0;
+
+ iValue = ::MsiRecordGetInteger(hRecord, 13);
+ m_wDeadlineMinutes = iValue != MSI_NULL_INTEGER ? (WORD)iValue : 0;
+
+ m_dwMaxRuntimeMS = ::MsiRecordGetInteger(hRecord, 14);
+ if (m_dwMaxRuntimeMS == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
+
+ return ERROR_SUCCESS;
+}
+
+
+UINT COpTaskCreate::SetTriggersFromView(MSIHANDLE hView)
+{
+ for (;;) {
+ UINT uiResult;
+ PMSIHANDLE hRecord;
+ TASK_TRIGGER ttData;
+ ULONGLONG ullValue;
+ FILETIME ftValue;
+ SYSTEMTIME stValue;
+ int iValue;
+
+ // 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.
+ iValue = ::MsiRecordGetInteger(hRecord, 4);
+ if (iValue == MSI_NULL_INTEGER) return ERROR_INVALID_FIELD;
+ ttData.wStartHour = (WORD)(iValue / 60);
+ ttData.wStartMinute = (WORD)(iValue % 60);
+
+ // Get StartTimeRand.
+ iValue = ::MsiRecordGetInteger(hRecord, 5);
+ ttData.wRandomMinutesInterval = iValue != MSI_NULL_INTEGER ? (WORD)iValue : 0;
+
+ // 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 ERROR_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTaskDelete
+////////////////////////////////////////////////////////////////////////////
+
+COpTaskDelete::COpTaskDelete(LPCWSTR pszTaskName, int iTicks) :
+ COpTypeSingleString(pszTaskName, iTicks)
+{
+}
+
+
+HRESULT COpTaskDelete::Execute(CSession *pSession)
+{
+ assert(pSession);
+ HRESULT hr;
+ CComPtr pService;
+
+ hr = pService.CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER);
+ if (SUCCEEDED(hr)) {
+ // Windows Vista or newer.
+ CComVariant vEmpty;
+ CComPtr pTaskFolder;
+
+ // Connect to local task service.
+ hr = pService->Connect(vEmpty, vEmpty, vEmpty, vEmpty);
+ if (FAILED(hr)) goto finish;
+
+ // Get task folder.
+ hr = pService->GetFolder(CComBSTR(L"\\"), &pTaskFolder);
+ if (FAILED(hr)) goto finish;
+
+ if (pSession->m_bRollbackEnabled) {
+ CComPtr pTask, pTaskOrig;
+ CComPtr pTaskDefinition;
+ CComPtr pPrincipal;
+ VARIANT_BOOL bEnabled;
+ TASK_LOGON_TYPE logonType;
+ CComBSTR sSSDL;
+ CComVariant vSSDL;
+ ATL::CAtlStringW sDisplayNameOrig;
+ UINT uiCount = 0;
+
+ // Get the source task.
+ hr = pTaskFolder->GetTask(CComBSTR(m_sValue), &pTask);
+ if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { hr = S_OK; goto finish; }
+ else if (FAILED(hr)) goto finish;
+
+ // Disable the task.
+ hr = pTask->get_Enabled(&bEnabled);
+ if (FAILED(hr)) goto finish;
+ if (bEnabled) {
+ // The task is enabled.
+
+ // In case the task disabling fails halfway, try to re-enable it anyway on rollback.
+ pSession->m_olRollback.AddHead(new COpTaskEnable(m_sValue, TRUE));
+
+ // Disable it.
+ hr = pTask->put_Enabled(VARIANT_FALSE);
+ if (FAILED(hr)) goto finish;
+ }
+
+ // Get task's definition.
+ hr = pTask->get_Definition(&pTaskDefinition);
+ if (FAILED(hr)) goto finish;
+
+ // Get task principal.
+ hr = pTaskDefinition->get_Principal(&pPrincipal);
+ if (FAILED(hr)) goto finish;
+
+ // Get task logon type.
+ hr = pPrincipal->get_LogonType(&logonType);
+ if (FAILED(hr)) goto finish;
+
+ // Get task security descriptor.
+ hr = pTask->GetSecurityDescriptor(DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, &sSSDL);
+ if (hr == HRESULT_FROM_WIN32(ERROR_PRIVILEGE_NOT_HELD)) vSSDL.Clear();
+ else if (FAILED(hr)) goto finish;
+ else {
+ V_VT (&vSSDL) = VT_BSTR;
+ V_BSTR(&vSSDL) = sSSDL.Detach();
+ }
+
+ // Register a backup copy of task.
+ do {
+ sDisplayNameOrig.Format(L"%ls (orig %u)", (LPCWSTR)m_sValue, ++uiCount);
+ hr = pTaskFolder->RegisterTaskDefinition(
+ CComBSTR(sDisplayNameOrig), // path
+ pTaskDefinition, // pDefinition
+ TASK_CREATE, // flags
+ vEmpty, // userId
+ vEmpty, // password
+ logonType, // logonType
+ vSSDL, // sddl
+ &pTaskOrig); // ppTask
+ } 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 COpTaskDelete(sDisplayNameOrig));
+ if (FAILED(hr)) goto finish;
+
+ // Order rollback action to restore from backup copy.
+ pSession->m_olRollback.AddHead(new COpTaskCopy(sDisplayNameOrig, m_sValue));
+
+ // Delete it.
+ hr = pTaskFolder->DeleteTask(CComBSTR(m_sValue), 0);
+ if (FAILED(hr)) goto finish;
+
+ // Order commit action to delete backup copy.
+ pSession->m_olCommit.AddTail(new COpTaskDelete(sDisplayNameOrig));
+ } else {
+ // Delete the task.
+ hr = pTaskFolder->DeleteTask(CComBSTR(m_sValue), 0);
+ if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) hr = S_OK;
+ }
+ } else {
+ // Windows XP or older.
+ CComPtr pTaskScheduler;
+
+ // Get task scheduler object.
+ hr = pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL);
+ if (FAILED(hr)) goto finish;
+
+ if (pSession->m_bRollbackEnabled) {
+ CComPtr pTask;
+ DWORD dwFlags;
+ ATL::CAtlStringW sDisplayNameOrig;
+ UINT uiCount = 0;
+
+ // Load the task.
+ hr = pTaskScheduler->Activate(m_sValue, IID_ITask, (IUnknown**)&pTask);
+ if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { hr = S_OK; goto finish; }
+ else if (FAILED(hr)) goto finish;
+
+ // Disable the task.
+ hr = pTask->GetFlags(&dwFlags);
+ if (FAILED(hr)) goto finish;
+ 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 COpTaskEnable(m_sValue, TRUE));
+
+ // Disable it.
+ dwFlags |= TASK_FLAG_DISABLED;
+ hr = pTask->SetFlags(dwFlags);
+ if (FAILED(hr)) goto finish;
+ }
+
+ // Prepare a backup copy of task.
+ do {
+ sDisplayNameOrig.Format(L"%ls (orig %u)", (LPCWSTR)m_sValue, ++uiCount);
+ hr = 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 COpTaskDelete(sDisplayNameOrig));
+ if (FAILED(hr)) goto finish;
+
+ // Save the backup copy.
+ CComQIPtr pTaskFile(pTask);
+ if (!pTaskFile) { hr = E_NOINTERFACE; goto finish; }
+ hr = pTaskFile->Save(NULL, TRUE);
+ if (FAILED(hr)) goto finish;
+
+ // Order rollback action to restore from backup copy.
+ pSession->m_olRollback.AddHead(new COpTaskCopy(sDisplayNameOrig, m_sValue));
+
+ // Delete it.
+ hr = pTaskScheduler->Delete(m_sValue);
+ if (FAILED(hr)) goto finish;
+
+ // Order commit action to delete backup copy.
+ pSession->m_olCommit.AddTail(new COpTaskDelete(sDisplayNameOrig));
+ } else {
+ // Delete the task.
+ hr = pTaskScheduler->Delete(m_sValue);
+ if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) hr = S_OK;
+ }
+ }
+
+finish:
+ if (FAILED(hr)) {
+ PMSIHANDLE hRecordProg = ::MsiCreateRecord(3);
+ verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_TASK_DELETE_FAILED) == ERROR_SUCCESS);
+ verify(::MsiRecordSetStringW(hRecordProg, 2, m_sValue ) == ERROR_SUCCESS);
+ verify(::MsiRecordSetInteger(hRecordProg, 3, hr ) == ERROR_SUCCESS);
+ ::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
+ }
+ return hr;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTaskEnable
+////////////////////////////////////////////////////////////////////////////
+
+COpTaskEnable::COpTaskEnable(LPCWSTR pszTaskName, BOOL bEnable, int iTicks) :
+ m_bEnable(bEnable),
+ COpTypeSingleString(pszTaskName, iTicks)
+{
+}
+
+
+HRESULT COpTaskEnable::Execute(CSession *pSession)
+{
+ assert(pSession);
+ HRESULT hr;
+ CComPtr pService;
+
+ hr = pService.CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER);
+ if (SUCCEEDED(hr)) {
+ // Windows Vista or newer.
+ CComVariant vEmpty;
+ CComPtr pTaskFolder;
+ CComPtr pTask;
+ VARIANT_BOOL bEnabled;
+
+ // Connect to local task service.
+ hr = pService->Connect(vEmpty, vEmpty, vEmpty, vEmpty);
+ if (FAILED(hr)) goto finish;
+
+ // Get task folder.
+ hr = pService->GetFolder(CComBSTR(L"\\"), &pTaskFolder);
+ if (FAILED(hr)) goto finish;
+
+ // Get task.
+ hr = pTaskFolder->GetTask(CComBSTR(m_sValue), &pTask);
+ if (FAILED(hr)) goto finish;
+
+ // Get currently enabled state.
+ hr = pTask->get_Enabled(&bEnabled);
+ if (FAILED(hr)) goto finish;
+
+ // Modify enable state.
+ if (m_bEnable) {
+ if (pSession->m_bRollbackEnabled && !bEnabled) {
+ // 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 COpTaskEnable(m_sValue, TRUE));
+ hr = S_OK; goto finish;
+ } else
+ bEnabled = VARIANT_TRUE;
+ } else {
+ if (pSession->m_bRollbackEnabled && bEnabled) {
+ // The task is enabled and we will disable it now.
+ // Order rollback to re-enable it.
+ pSession->m_olRollback.AddHead(new COpTaskEnable(m_sValue, TRUE));
+ }
+ bEnabled = VARIANT_FALSE;
+ }
+
+ // Set enable/disable.
+ hr = pTask->put_Enabled(bEnabled);
+ if (FAILED(hr)) goto finish;
+ } else {
+ // Windows XP or older.
+ CComPtr pTaskScheduler;
+ CComPtr pTask;
+ DWORD dwFlags;
+
+ // Get task scheduler object.
+ hr = pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL);
+ if (FAILED(hr)) goto finish;
+
+ // Load the task.
+ hr = pTaskScheduler->Activate(m_sValue, IID_ITask, (IUnknown**)&pTask);
+ if (FAILED(hr)) goto finish;
+
+ // Get task's current flags.
+ hr = pTask->GetFlags(&dwFlags);
+ if (FAILED(hr)) goto finish;
+
+ // Modify flags.
+ if (m_bEnable) {
+ if (pSession->m_bRollbackEnabled && (dwFlags & TASK_FLAG_DISABLED)) {
+ // The task is disabled and supposed to be enabled.
+ // However, we shall enable it at commit to prevent it from accidentally starting in the very installation phase.
+ pSession->m_olCommit.AddTail(new COpTaskEnable(m_sValue, TRUE));
+ hr = S_OK; goto finish;
+ } 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 COpTaskEnable(m_sValue, TRUE));
+ }
+ dwFlags |= TASK_FLAG_DISABLED;
+ }
+
+ // Set flags.
+ hr = pTask->SetFlags(dwFlags);
+ if (FAILED(hr)) goto finish;
+
+ // Save the task.
+ CComQIPtr pTaskFile(pTask);
+ if (!pTaskFile) { hr = E_NOINTERFACE; goto finish; }
+ hr = pTaskFile->Save(NULL, TRUE);
+ if (FAILED(hr)) goto finish;
+ }
+
+finish:
+ if (FAILED(hr)) {
+ PMSIHANDLE hRecordProg = ::MsiCreateRecord(3);
+ verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_TASK_ENABLE_FAILED) == ERROR_SUCCESS);
+ verify(::MsiRecordSetStringW(hRecordProg, 2, m_sValue ) == ERROR_SUCCESS);
+ verify(::MsiRecordSetInteger(hRecordProg, 3, hr ) == ERROR_SUCCESS);
+ ::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
+ }
+ return hr;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// COpTaskCopy
+////////////////////////////////////////////////////////////////////////////
+
+COpTaskCopy::COpTaskCopy(LPCWSTR pszTaskSrc, LPCWSTR pszTaskDst, int iTicks) :
+ COpTypeSrcDstString(pszTaskSrc, pszTaskDst, iTicks)
+{
+}
+
+
+HRESULT COpTaskCopy::Execute(CSession *pSession)
+{
+ assert(pSession);
+ HRESULT hr;
+ CComPtr pService;
+
+ hr = pService.CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER);
+ if (SUCCEEDED(hr)) {
+ // Windows Vista or newer.
+ CComVariant vEmpty;
+ CComPtr pTaskFolder;
+ CComPtr pTask, pTaskOrig;
+ CComPtr pTaskDefinition;
+ CComPtr pPrincipal;
+ TASK_LOGON_TYPE logonType;
+ CComBSTR sSSDL;
+
+ // Connect to local task service.
+ hr = pService->Connect(vEmpty, vEmpty, vEmpty, vEmpty);
+ if (FAILED(hr)) goto finish;
+
+ // Get task folder.
+ hr = pService->GetFolder(CComBSTR(L"\\"), &pTaskFolder);
+ if (FAILED(hr)) goto finish;
+
+ // Get the source task.
+ hr = pTaskFolder->GetTask(CComBSTR(m_sValue1), &pTask);
+ if (FAILED(hr)) goto finish;
+
+ // Get task's definition.
+ hr = pTask->get_Definition(&pTaskDefinition);
+ if (FAILED(hr)) goto finish;
+
+ // Get task principal.
+ hr = pTaskDefinition->get_Principal(&pPrincipal);
+ if (FAILED(hr)) goto finish;
+
+ // Get task logon type.
+ hr = pPrincipal->get_LogonType(&logonType);
+ if (FAILED(hr)) goto finish;
+
+ // Get task security descriptor.
+ hr = pTask->GetSecurityDescriptor(DACL_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | OWNER_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION | LABEL_SECURITY_INFORMATION, &sSSDL);
+ if (FAILED(hr)) goto finish;
+
+ // Register a new task.
+ hr = pTaskFolder->RegisterTaskDefinition(
+ CComBSTR(m_sValue2), // path
+ pTaskDefinition, // pDefinition
+ TASK_CREATE, // flags
+ vEmpty, // userId
+ vEmpty, // password
+ logonType, // logonType
+ CComVariant(sSSDL), // sddl
+ &pTaskOrig); // ppTask
+ if (FAILED(hr)) goto finish;
+ } else {
+ // Windows XP or older.
+ CComPtr pTaskScheduler;
+ CComPtr pTask;
+
+ // Get task scheduler object.
+ hr = pTaskScheduler.CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_ALL);
+ if (FAILED(hr)) goto finish;
+
+ // Load the source task.
+ hr = pTaskScheduler->Activate(m_sValue1, IID_ITask, (IUnknown**)&pTask);
+ if (FAILED(hr)) goto finish;
+
+ // Add with different name.
+ hr = 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 COpTaskDelete(m_sValue2));
+ }
+ if (FAILED(hr)) goto finish;
+
+ // Save the task.
+ CComQIPtr pTaskFile(pTask);
+ if (!pTaskFile) { hr = E_NOINTERFACE; goto finish; }
+ hr = pTaskFile->Save(NULL, TRUE);
+ if (FAILED(hr)) goto finish;
+ }
+
+finish:
+ if (FAILED(hr)) {
+ PMSIHANDLE hRecordProg = ::MsiCreateRecord(4);
+ verify(::MsiRecordSetInteger(hRecordProg, 1, ERROR_INSTALL_TASK_COPY_FAILED) == ERROR_SUCCESS);
+ verify(::MsiRecordSetStringW(hRecordProg, 2, m_sValue1 ) == ERROR_SUCCESS);
+ verify(::MsiRecordSetStringW(hRecordProg, 3, m_sValue2 ) == ERROR_SUCCESS);
+ verify(::MsiRecordSetInteger(hRecordProg, 4, hr ) == ERROR_SUCCESS);
+ ::MsiProcessMessage(pSession->m_hInstall, INSTALLMESSAGE_ERROR, hRecordProg);
+ }
+ return hr;
+}
+
+} // namespace AMSICA
diff --git a/AMSICA/src/StdAfx.cpp b/AMSICA/src/StdAfx.cpp
new file mode 100644
index 0000000..9b13c1c
--- /dev/null
+++ b/AMSICA/src/StdAfx.cpp
@@ -0,0 +1 @@
+#include "StdAfx.h"