|
|
//+----------------------------------------------------------------------------
//
// Job Schedule Application Job Object Handler
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: util.cxx
//
// Contents: job & trigger objects IUnknown methods, class factory, DLL fcns,
// plus misc utility fcns
//
// Classes: CJob (continued), CJobCF, CTrigger
//
// Interfaces: IUnknown, IClassFactory
//
// History: 24-May-95 EricB created
//
//-----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include "job.hxx"
#include <StrSafe.h>
//+----------------------------------------------------------------------------
//
// Member: CJob::GetAtInfo
//
// Synopsis: for a downlevel job, return its data in an AT_INFO struct.
//
// Arguments: [pAt] - pointer to the AT_INFO struct
// [pwszCommand] - buffer for the command string
// [pcchCommand] - on input, size of supplied buffer, on output,
// size needed/used.
//
// Returns: HRESULTS - ERROR_INSUFFICIENT_BUFFER if too small
// - SCHED_E_NOT_AN_AT_JOB if not an AT job
//
// Notes: This method is not exposed to external clients, thus it is not
// part of a public interface.
//-----------------------------------------------------------------------------
HRESULT CJob::GetAtInfo(PAT_INFO pAt, LPWSTR pwszCommand, DWORD * pcchCommand) { TRACE(CJob, GetAtInfo); HRESULT hr = S_OK;
if (!(m_rgFlags & JOB_I_FLAG_NET_SCHEDULE)) { schDebugOut((DEB_ERROR, "CJob::GetAtInfo: Task object is not an AT job!\n")); return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); }
//
// The ApplicationName and Parameters properties need to be concatonated
// and returned as pwszCommand. If there is any white space in the app
// name string, then it must be enclosed in quotes.
//
LPWSTR pwszAppName, pwszParams;
hr = GetApplicationName(&pwszAppName); if (FAILED(hr)) { ERR_OUT("GetAtInfo: GetApplicationName", hr); return hr; }
hr = GetParameters(&pwszParams); if (FAILED(hr)) { ERR_OUT("GetAtInfo: GetParameters", hr); CoTaskMemFree(pwszAppName); return hr; }
//
// Check for whitespace in the app name.
//
BOOL fAppNameHasSpaces = HasSpaces(pwszAppName);
//
// If there is app name whitespace, add two for the quotes to be added.
//
DWORD cchApp = wcslen(pwszAppName) + (fAppNameHasSpaces ? 2 : 0); DWORD cchParam = wcslen(pwszParams); //
// Add one for the terminating null and ont to concatenate the params
//
DWORD cch = cchApp + cchParam + 1 + 1;
if (cch > *pcchCommand) { *pcchCommand = cch; CoTaskMemFree(pwszAppName); CoTaskMemFree(pwszParams); return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); } *pcchCommand = cch;
if (fAppNameHasSpaces) { StringCchCopy(pwszCommand, *pcchCommand, L"\""); StringCchCat(pwszCommand, *pcchCommand, pwszAppName); StringCchCat(pwszCommand, *pcchCommand, L"\""); } else { StringCchCopy(pwszCommand, *pcchCommand, pwszAppName); }
if (cchParam > 0) { StringCchCat(pwszCommand, *pcchCommand, L" "); StringCchCat(pwszCommand, *pcchCommand, pwszParams); }
CoTaskMemFree(pwszAppName); CoTaskMemFree(pwszParams);
//
// An AT job can have one or two triggers of type TASK_TIME_TRIGGER_WEEKLY
// and/or TASK_TIME_TRIGGER_MONTHLYDATE. It may also have, instead, a single trigger
// of type TASK_TIME_TRIGGER_ONCE, which indicates it runs either today or
// tomorrow, only.
//
WORD cTriggers = m_Triggers.GetCount();
if (cTriggers == 0 || cTriggers > 2) { ERR_OUT("GetAtInfo: Incorrect Trigger Count", E_FAIL); return E_FAIL; }
PTASK_TRIGGER pjt;
pjt = _GetTrigger(0); if (pjt == NULL) { schAssert(!"GetCount > 0 but no trigger 0"); return E_FAIL; }
pAt->JobTime = (pjt->wStartHour * JOB_MINS_PER_HOUR + pjt->wStartMinute) * JOB_MILLISECONDS_PER_MINUTE;
pAt->DaysOfMonth = pAt->DaysOfWeek = 0;
switch (pjt->TriggerType) { case TASK_TIME_TRIGGER_WEEKLY: //
// Convert Scheduler DOW to AT_INFO DOW:
// Scheduler rgfDaysOfTheWeek: Sunday = bit 0, Monday = bit 1.
// AT_INFO DaysOfWeek: Monday = bit 0, Sunday = bit 6.
//
pAt->DaysOfWeek = pjt->Type.Weekly.rgfDaysOfTheWeek >> 1;
if (pjt->Type.Weekly.rgfDaysOfTheWeek & 0x0001) { pAt->DaysOfWeek |= 0x0040; } break;
case TASK_TIME_TRIGGER_MONTHLYDATE: pAt->DaysOfMonth = pjt->Type.MonthlyDate.rgfDays; break;
case TASK_TIME_TRIGGER_ONCE: // Day of Month & Week are NULL if the job runs only once.
break;
default: schAssert(FALSE && "GetAtInfo: wrong trigger type"); ERR_OUT("GetAtInfo: wrong trigger type", hr); return E_FAIL; }
if (cTriggers == 2) { pjt = _GetTrigger(1);
switch (pjt->TriggerType) { case TASK_TIME_TRIGGER_WEEKLY: //
// Convert Scheduler DOW to AT_INFO DOW:
// Scheduler rgfDaysOfTheWeek: Sunday = bit 0, Monday = bit 1.
// AT_INFO DaysOfWeek: Monday = bit 0, Sunday = bit 6.
//
pAt->DaysOfWeek = pjt->Type.Weekly.rgfDaysOfTheWeek >> 1;
if (pjt->Type.Weekly.rgfDaysOfTheWeek & 0x0001) { pAt->DaysOfWeek |= 0x0040; } break;
case TASK_TIME_TRIGGER_MONTHLYDATE: pAt->DaysOfMonth = pjt->Type.MonthlyDate.rgfDays; break;
case TASK_TIME_TRIGGER_ONCE: schAssert(FALSE && "GetAtInfo: Once triggers not allowed in multiple triggers!"); ERR_OUT("GetAtInfo: Once triggers not allowed in multiple triggers!", 0); return E_FAIL; break;
default: schAssert(FALSE && "GetAtInfo: wrong trigger type"); ERR_OUT("GetAtInfo: wrong trigger type", 0); return E_FAIL; } }
//
// Set the AT_INFO.Flags.
//
pAt->Flags = 0;
if (!(m_rgFlags & TASK_FLAG_INTERACTIVE)) { pAt->Flags |= JOB_NONINTERACTIVE; }
if (!(pjt->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE) && (pjt->TriggerType != TASK_TIME_TRIGGER_ONCE)) { pAt->Flags |= JOB_RUN_PERIODICALLY; }
//
// Check whether job runs today, or is even running in a time window
// that is valid for a NetScheduleJob.
//
SYSTEMTIME stNow, stMidnight, stTomorrow; GetLocalTime(&stNow);
WORD cRuns = 0;
// Check to see if there is a run today and set the flag JOB_RUNS_TODAY
stMidnight = stNow; stMidnight.wHour = stMidnight.wMinute = stMidnight.wSecond = stMidnight.wMilliseconds = 0; IncrementDay(&stMidnight);
// Zero out cRuns - we used it, and it is not initialized in GetRunTimesP
cRuns = 0;
hr = GetRunTimesP(&stNow, &stMidnight, &cRuns, 1, NULL, NULL);
if (FAILED(hr)) { ERR_OUT("GetAtInfo: GetRunTimes", hr); return hr; }
if (cRuns > 0) { pAt->Flags |= JOB_RUNS_TODAY; }
//
// Check exit status of last run and set TASK_EXEC_ERROR as needed.
//
if (IsFlagSet(JOB_I_FLAG_LAST_LAUNCH_FAILED) || IsFlagSet(JOB_I_FLAG_ERROR_IN_LAST_RUN)) { pAt->Flags |= JOB_EXEC_ERROR; }
//
// Safety check - if the trigger is a ONCE trigger, the job
// must within the next 24 hours (or already be running)
//
//
if (pjt->TriggerType == TASK_TIME_TRIGGER_ONCE && !(pAt->Flags & JOB_EXEC_ERROR)) { stTomorrow = stNow; IncrementDay(&stTomorrow);
hr = GetRunTimesP(&stNow, &stTomorrow, &cRuns, 1, NULL, NULL); if (FAILED(hr)) { ERR_OUT("GetAtInfo: GetRunTimes for Once Trigger", hr); return hr; } HRESULT status; GetStatus(&status);
if (cRuns == 0) { if (status == SCHED_S_TASK_RUNNING) pAt->Flags |= JOB_RUNS_TODAY; else { ERR_OUT("GetAtInfo: Once trigger outside permitted time interval", 0); return E_FAIL; } } }
//
// Omit jobs whose end date has passed (usually jobs that haven't yet
// been deleted because they're still running)
//
if (pjt->rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE) { SYSTEMTIME stEnd; stEnd.wYear = pjt->wEndYear; stEnd.wMonth = pjt->wEndMonth; stEnd.wDayOfWeek = 0; stEnd.wDay = pjt->wEndDay; stEnd.wHour = pjt->wStartHour; stEnd.wMinute = pjt->wStartMinute; stEnd.wSecond = 0; stEnd.wMilliseconds = 0; if (IsFirstTimeEarlier(&stEnd, &stNow) && !(pAt->Flags & JOB_EXEC_ERROR)) { return E_FAIL; } }
return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CJob::UpdateJobState
//
// Synopsis: Update the job flags and status depending on whether there are
// valid triggers with more run times.
//
// Arguments: [fRunning] - optional param, defaults to false.
// CSchedWorker::RunJobs sets this to true while
// setting the new job status to
// SCHED_S_TASK_RUNNING. So, don't change the job's
// status if fRunning.
//
// Returns: HRESULTS
//
// Notes: The triggers have to be loaded.
//-----------------------------------------------------------------------------
HRESULT CJob::UpdateJobState(BOOL fRunning) { //TRACE(CJob, UpdateJobState);
if (!IsFlagSet(JOB_I_FLAG_HAS_APPNAME)) { //
// The job can't run without an appname.
//
SetStatus(SCHED_S_TASK_NOT_SCHEDULED); return S_OK; }
SYSTEMTIME stNow; GetLocalTime(&stNow);
if (fRunning) { //
// Increment the minute value so that the current run won't be returned.
//
stNow.wMinute++; if (stNow.wMinute >= JOB_MINS_PER_HOUR) { stNow.wHour++; stNow.wMinute = 0; if (stNow.wHour >= JOB_HOURS_PER_DAY) { stNow.wHour = 0; IncrementDay(&stNow); } } }
WORD cRuns = 0; HRESULT hr;
hr = GetRunTimesP(&stNow, NULL, &cRuns, 1, NULL, NULL); if (FAILED(hr)) { ERR_OUT("CJob::UpdateJobState", hr); return hr; }
//
// Update the job flags and status properties.
//
if (hr == SCHED_S_TASK_NO_VALID_TRIGGERS) { SetFlag(JOB_I_FLAG_NO_VALID_TRIGGERS);
SetStatus(SCHED_S_TASK_NOT_SCHEDULED); } else { ClearFlag(JOB_I_FLAG_NO_VALID_TRIGGERS);
if (cRuns == 0 && hr != SCHED_S_EVENT_TRIGGER) { SetFlag(JOB_I_FLAG_NO_MORE_RUNS); } else { ClearFlag(JOB_I_FLAG_NO_MORE_RUNS);
//
// If the job isn't currently running and had not been ready to run but
// now have both valid triggers and valid properties, set the status.
// TODO: test TASK_FLAG_HAS_OBJPATH and TASK_FLAG_HAS_ACCOUNT when those
// properties are functional.
//
if (!fRunning && IsFlagSet(JOB_I_FLAG_HAS_APPNAME)) { if (m_stMostRecentRunTime.wYear == 0) { //
// Job has never run if last-run-time property is null (all
// elements will be zero if null, testing year is sufficient).
//
SetStatus(SCHED_S_TASK_HAS_NOT_RUN); } else { //
// Job has run in the past, so it is now waiting to run again.
//
SetStatus(SCHED_S_TASK_READY); } } } }
//
// The disabled flag takes precedence over other states.
//
if (IsFlagSet(TASK_FLAG_DISABLED)) { SetStatus(SCHED_S_TASK_DISABLED); } return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CJob::PostRunUpdate
//
// Synopsis: update the status of a job object after a run exits
//
// Arguments: [ExitCode] - the run's exit code
// [fFinishedOK] - only set the job object's exit code if TRUE.
//
// Returns: HRESULTS
//
// Notes: Set the ExitCode and Status values for the job and log the run
// if the LogRunHistory property is set.
// This method is not exposed to external clients, thus it is not
// part of a public interface.
// If any of the variable length properties or the triggers are
// needed, then the caller will need to do a full activation of
// the job.
//
//-----------------------------------------------------------------------------
HRESULT CJob::PostRunUpdate(long ExitCode, BOOL fFinishedOK) { schDebugOut((DEB_ITRACE, "PostRunUpdate: decrementing running instance " "count (%d before decrement)\n", m_cRunningInstances)); if (m_cRunningInstances > 0) { m_cRunningInstances--; }
//
// Don't update the status unless the running instance count is back to
// zero.
//
if (m_cRunningInstances == 0) { if (IsFlagSet(JOB_I_FLAG_NO_VALID_TRIGGERS)) { SetStatus(SCHED_S_TASK_NO_VALID_TRIGGERS); } else { SetStatus(SCHED_S_TASK_READY); } }
if (fFinishedOK) { m_ExitCode = ExitCode; }
ClearFlag(JOB_I_FLAG_ABORT_NOW);
return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CJob::IfEventJobAddToList
//
// Synopsis: Check if the job has any triggers of the specified event type.
// If so, allocate and initialize a CRun object and add it to the
// list. Add it to pIdleWaitList if it needs to wait for an
// idle period, otherwise add it to pRunList.
//
// Arguments: [EventType] - the event trigger type
// [ptszJobName] - the short job name to pass to CRun.
// [pRunList] - a pointer to a run list object.
// [pIdleWaitList] - a pointer to a run list object sorted by
// idle wait times.
//
// Returns: S_OK if the event trigger is found, S_FALSE if not, or a
// fatal error code.
//
// Assumes: Triggers have already been loaded.
// If EventType is not TASK_EVENT_TRIGGER_ON_IDLE, this method is
// only called when the event has occurred. (If EventType is
// TASK_EVENT_TRIGGER_ON_IDLE, this method is called when
// building the wait list from the tasks folder's contents.)
//
//-----------------------------------------------------------------------------
HRESULT CJob::IfEventJobAddToList(TASK_TRIGGER_TYPE EventType, LPCTSTR ptszJobName, CRunList * pRunList, CIdleRunList * pIdleWaitList) { schAssert(EventType == TASK_EVENT_TRIGGER_ON_IDLE || EventType == TASK_EVENT_TRIGGER_AT_SYSTEMSTART || EventType == TASK_EVENT_TRIGGER_AT_LOGON);
if (! IsFlagSet(JOB_I_FLAG_HAS_TRIGGERS)) { // (An optimization; the function would still work without it)
return S_FALSE; }
if (EventType == TASK_EVENT_TRIGGER_ON_IDLE && m_wIdleWait == 0) { //
// We ignore all idle triggers if the idle wait time is 0.
//
return S_FALSE; }
//
// Will the job need to wait for an idle period before being started?
//
BOOL fNeedIdleWait = ((EventType == TASK_EVENT_TRIGGER_ON_IDLE || IsFlagSet(TASK_FLAG_START_ONLY_IF_IDLE)) && m_wIdleWait > 0);
HRESULT hr; FILETIME ftNow = GetLocalTimeAsFileTime(); // BUGBUG ftNow should be passed in, and the same ftNow should be used
// when checking against the deadlines. Otherwise a run can be missed
// if we're slow here and the deadline elapses.
//
// See if the job has any triggers of the specified type.
// Find the latest deadline of these triggers.
//
FILETIME ftLatestDeadline = { 0, 0 }; for (WORD i = 0; i < m_Triggers.GetCount(); i++) { if (m_Triggers[i].TriggerType != EventType) { continue; }
//
// If the trigger is an idle trigger then the trigger's deadline
// is simply midnight on the trigger's end date.
//
// If the job does not have TASK_FLAG_START_ONLY_IF_IDLE then
// the trigger's deadline is simply midnight on the trigger's
// end date.
//
// If the job does have TASK_FLAG_START_ONLY_IF_IDLE then the
// trigger's deadline is
// min( trigger's end date,
// job's start time + trigger's MinutesDuration)
//
FILETIME ftDeadline // This trigger's deadline
= MAX_FILETIME; // End date if no end date set
if (m_Triggers[i].rgFlags & TASK_TRIGGER_FLAG_HAS_END_DATE) { SYSTEMTIME stEnd = // This trigger's end date
{ m_Triggers[i].wEndYear, m_Triggers[i].wEndMonth, 0, // wDayOfWeek
m_Triggers[i].wEndDay, 23, // wHour
59, // wMinute
0, // wSecond
0 // wMilliseconds
};
if (!SystemTimeToFileTime(&stEnd, &ftDeadline)) { // Presume the trigger had an invalid end date.
// Ignore the trigger. BUGBUG return an error ?
continue; } }
if (EventType != TASK_EVENT_TRIGGER_ON_IDLE && fNeedIdleWait) { //
// Calculate (job's start time + trigger's MinutesDuration)
// This method is only called when the event that fires the
// trigger has occurred, so the job's start time is now.
//
FILETIME ftDurationEnd = ftNow; AddMinutesToFileTime(&ftDurationEnd, m_Triggers[i].MinutesDuration);
ftDeadline = minFileTime(ftDeadline, ftDurationEnd); }
ftLatestDeadline = maxFileTime(ftLatestDeadline, ftDeadline); }
if (CompareFileTime(&ftLatestDeadline, &ftNow) < 0) { //
// All the triggers of this type have expired.
//
return S_FALSE; }
//
// Add the job to the appropriate run list.
//
CRun * pNewRun; if (fNeedIdleWait) { DBG_OUT("Adding idle job to list."); pNewRun = new CRun(m_dwMaxRunTime, GetUserFlags(), m_wIdleWait, ftLatestDeadline, (EventType == TASK_EVENT_TRIGGER_ON_IDLE)); } else { pNewRun = new CRun(m_dwMaxRunTime, GetUserFlags(), MAX_FILETIME, FALSE); }
if (pNewRun == NULL) { ERR_OUT("CJob::IfEventJobAddToList new CRun", E_OUTOFMEMORY); return E_OUTOFMEMORY; }
// Complete job info object initialization.
//
hr = pNewRun->Initialize(ptszJobName);
if (FAILED(hr)) { ERR_OUT("CJob::IfEventJobAddToList, CRun->Initialize", hr); delete pNewRun; return hr; }
if (fNeedIdleWait) { pIdleWaitList->AddSortedByIdleWait(pNewRun); } else { pRunList->Add(pNewRun); } return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CJob::IfStartupJobAddToList
//
// Synopsis: Check if the job has any startup triggers. If so, allocate and
// initialize a CRun object and add it to the list.
//
// Arguments: [ptszJobName] - the short job name to pass to CRun.
// [pRunList] - a pointer to a run list object.
//
// Returns: S_OK if the event trigger is found, S_FALSE if not, or a
// fatal error code.
//
// Assumes: Triggers have already been loaded.
//
//-----------------------------------------------------------------------------
HRESULT CJob::IfStartupJobAddToList(LPTSTR ptszJobName, CRunList * pRunList, CIdleRunList * pIdleWaitList) { return IfEventJobAddToList(TASK_EVENT_TRIGGER_AT_SYSTEMSTART, ptszJobName, pRunList, pIdleWaitList); }
//+----------------------------------------------------------------------------
//
// Member: CJob::IfLogonJobAddToList
//
// Synopsis: Check if the job has any logon triggers. If so, allocate and
// initialize a CRun object and add it to the list.
//
// Arguments: [ptszJobName] - the short job name to pass to CRun.
// [pRunList] - a pointer to a run list object.
//
// Returns: S_OK if the event trigger is found, S_FALSE if not, or a
// fatal error code.
//
// Assumes: Triggers have already been loaded.
//
//-----------------------------------------------------------------------------
HRESULT CJob::IfLogonJobAddToList(LPTSTR ptszJobName, CRunList * pRunList, CIdleRunList * pIdleWaitList) { return IfEventJobAddToList(TASK_EVENT_TRIGGER_AT_LOGON, ptszJobName, pRunList, pIdleWaitList); }
//+----------------------------------------------------------------------------
//
// Member: CJob::IfIdleJobAddToList
//
// Synopsis: Check if the job has any idle triggers. If so, allocate and
// initialize a CRun object and add it to the list.
//
// Arguments: [ptszJobName] - the short job name to pass to CRun.
// [pIdleWaitList] - a pointer to a run list object.
//
// Returns: S_OK if the event trigger is found, S_FALSE if not, or a
// fatal error code.
//
// Assumes: Triggers have already been loaded.
//
//-----------------------------------------------------------------------------
HRESULT CJob::IfIdleJobAddToList(LPTSTR ptszJobName, CIdleRunList * pIdleWaitList) { return IfEventJobAddToList(TASK_EVENT_TRIGGER_ON_IDLE, ptszJobName, NULL, pIdleWaitList); }
//+----------------------------------------------------------------------------
//
// Member: CJob::Delete
//
// Synopsis: Remove the file object for this job.
//
//-----------------------------------------------------------------------------
HRESULT CJob::Delete(void) { schDebugOut((DEB_ITRACE, "CJob:Delete on %S\n", m_ptszFileName));
if (m_ptszFileName == NULL) { return E_INVALIDARG; }
if (!DeleteFile(m_ptszFileName)) { schDebugOut((DEB_ITRACE, "DeleteFile failed with error %d\n", GetLastError())); return HRESULT_FROM_WIN32(GetLastError()); } return S_OK; }
//+----------------------------------------------------------------------------
//
// CJob::IUnknown methods
//
//-----------------------------------------------------------------------------
//+----------------------------------------------------------------------------
//
// Member: CJob::IUnknown::QueryInterface
//
// Synopsis: Returns requested interface pointer
//
//-----------------------------------------------------------------------------
STDMETHODIMP CJob::QueryInterface(REFIID riid, void ** ppvObject) { //schDebugOut((DEB_ITRACE, "CJob::QueryInterface\n"));
if (IID_IUnknown == riid) { *ppvObject = (IUnknown *)(ITask *)this; } else if (IID_ITask == riid) { *ppvObject = (IUnknown *)(ITask *)this; } else if (IID_IScheduledWorkItem == riid) { *ppvObject = (IUnknown *)(IScheduledWorkItem *)this; } else if (IID_IPersist == riid) { *ppvObject = (IUnknown *)(IPersist *)this; } else if (IID_IPersistFile == riid) { *ppvObject = (IUnknown *)(IPersistFile *)this; } else if (IID_IProvideTaskPage == riid) { *ppvObject = (IUnknown *)(IProvideTaskPage *)this; } else { *ppvObject = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CJob::IUnknown::AddRef
//
// Synopsis: increments reference count
//
// Returns: the reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CJob::AddRef(void) { //schDebugOut((DEB_ITRACE, "CJob::AddRef refcount going in %d\n", m_cReferences));
return InterlockedIncrement((long *)&m_cReferences); }
//+----------------------------------------------------------------------------
//
// Member: CJob::IUnknown::Release
//
// Synopsis: Decrements the object's reference count and frees it when
// no longer referenced.
//
// Returns: the reference count
//
// Notes: BUGBUG: do we need to check the refcount on the triggers
// before freeing the job object?
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CJob::Release(void) { //schDebugOut((DEB_ITRACE, "CJob::Release ref count going in %d\n", m_cReferences));
unsigned long uTmp; if ((uTmp = InterlockedDecrement((long *)&m_cReferences)) == 0) { delete this; } return uTmp; }
//+----------------------------------------------------------------------------
//
// CJobCF - class factory for the Job object
//
//-----------------------------------------------------------------------------
//+----------------------------------------------------------------------------
//
// Member: CJobCF::Create
//
// Synopsis: creates a new class factory object
//
//-----------------------------------------------------------------------------
IClassFactory * CJobCF::Create(void) { //schDebugOut((DEB_ITRACE, "CJobCF::Create\n"));
return new CJobCF; }
//+----------------------------------------------------------------------------
//
// Member: CJobCF::CJobCF
//
// Synopsis: ctor
//
//-----------------------------------------------------------------------------
CJobCF::CJobCF(void) { m_uRefs = 1; }
//+----------------------------------------------------------------------------
//
// Member: CJobCF::~CJobCF
//
// Synopsis: dtor
//
//-----------------------------------------------------------------------------
CJobCF::~CJobCF(void) { //schDebugOut((DEB_ITRACE, "~CJobCF: DLL ref count going in %d\n",
// g_cDllRefs));
}
//+----------------------------------------------------------------------------
//
// Member: CJobCF::IUnknown::QueryInterface
//
// Synopsis: Returns requested interface pointer
//
//-----------------------------------------------------------------------------
STDMETHODIMP CJobCF::QueryInterface(REFIID riid, void ** ppvObject) { //schDebugOut((DEB_ITRACE, "CJobCF::QueryInterface"));
if (IID_IUnknown == riid) { *ppvObject = (IUnknown *)this; } else if (IsEqualIID(IID_IClassFactory, riid)) { *ppvObject = (IClassFactory *)this; } else { *ppvObject = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CJobCF::IUnknown::AddRef
//
// Synopsis: increments reference count
//
// Returns: the reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CJobCF::AddRef(void) { //schDebugOut((DEB_ITRACE, "CJobCF::AddRef\n"));
return InterlockedIncrement((long *)&m_uRefs); }
//+----------------------------------------------------------------------------
//
// Member: CJobCF::IUnknown::Release
//
// Synopsis: Decrements the object's reference count and frees it when
// no longer referenced.
//
// Returns: the reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CJobCF::Release(void) { //schDebugOut((DEB_ITRACE, "CJobCF::Release ref count going in %d\n",
// m_uRefs));
unsigned long uTmp; if ((uTmp = InterlockedDecrement((long *)&m_uRefs)) == 0) { delete this; } return uTmp; }
//+----------------------------------------------------------------------------
//
// Member: CJobCF::IClassFactory::CreateInstance
//
// Synopsis: create an incore instance of the job class object
//
// Arguments: [pUnkOuter] - aggregator
// [riid] - requested interface
// [ppvObject] - receptor for itf ptr
//
// Returns: HRESULTS
//
//-----------------------------------------------------------------------------
STDMETHODIMP CJobCF::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject) { //schDebugOut((DEB_ITRACE, "CJobCF::CreateInstance\n"));
SCODE sc = S_OK; ITask * pJob = CJob::Create(); if (pJob == NULL) { *ppvObject = NULL; return E_OUTOFMEMORY; } sc = pJob->QueryInterface(riid, ppvObject); if (FAILED(sc)) { *ppvObject = NULL; return sc; } // we got a refcount of one when launched, and the above QI increments it
// to 2, so call release to take it back to 1
pJob->Release(); return sc; }
//+----------------------------------------------------------------------------
//
// Member: CJobCF::IClassFactory::LockServer
//
// Synopsis: Called with fLock set to TRUE to indicate that the server
// should continue to run even if none of its objects are active
//
// Arguments: [fLock] - increment/decrement the instance count
//
// Returns: HRESULTS
//
// Notes: This function is a no-op since the server is in-proc.
//
//-----------------------------------------------------------------------------
STDMETHODIMP CJobCF::LockServer(BOOL fLock) { return S_OK; }
//+----------------------------------------------------------------------------
//
// CTrigger::IUnknown methods
//
// Notes: A trigger is not a first class COM object. They do not exist as
// separate entities outside of Job objects; you cannot do a
// CoCreateInstance on one and they do not go away when their ref
// count goes to zero. When a job object is instanciated, its triggers
// are also instanciated and they are freed from memory when the job
// object is freed from memory. Trigger ref counting is used only to
// prevent a client from deleting a trigger while it is holding a
// pointer to that trigger.
//
// Note also that the containing job object is AddRef'd and Release'd
// along with each trigger so that the job object will not be deleted
// while clients are holding trigger pointers.
//
//-----------------------------------------------------------------------------
//+----------------------------------------------------------------------------
//
// Member: CTrigger::IUnknown::QueryInterface
//
// Synopsis: Returns requested interface pointer
//
//-----------------------------------------------------------------------------
STDMETHODIMP CTrigger::QueryInterface(REFIID riid, void ** ppvObject) { if (IID_IUnknown == riid) { *ppvObject = (IUnknown *)(ITaskTrigger *)this; } else if (IID_ITaskTrigger == riid) { *ppvObject = (IUnknown *)(ITaskTrigger *)this; } else { *ppvObject = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CTrigger::IUnknown::AddRef
//
// Synopsis: increments reference count
//
// Returns: the reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CTrigger::AddRef(void) { return InterlockedIncrement((long *)&m_cReferences); }
//+----------------------------------------------------------------------------
//
// Member: CTrigger::IUnknown::Release
//
// Synopsis: Decrements the object's reference count.
//
// Returns: the reference count
//
//-----------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CTrigger::Release(void) { unsigned long uTmp;
if ((uTmp = InterlockedDecrement((long *)&m_cReferences)) == 0) { delete this; }
return uTmp; }
//+--------------------------------------------------------------------------
//
// Function: IsValidMonthlyDateTrigger
//
// Synopsis: Return TRUE if [pTrigger] has a valid combination of month
// and day bits set.
//
// History: 10-07-1997 DavidMun Created
//
//---------------------------------------------------------------------------
BOOL IsValidMonthlyDateTrigger( PTASK_TRIGGER pTrigger) { if (pTrigger->Type.MonthlyDate.rgfDays > JOB_RGFDAYS_MAX || pTrigger->Type.MonthlyDate.rgfMonths == 0 || pTrigger->Type.MonthlyDate.rgfMonths > JOB_RGFMONTHS_MAX) { return FALSE; }
if (pTrigger->Type.MonthlyDate.rgfDays == 0) { //
// rgfDays must be non-zero.
//
return FALSE; }
//
// More detailed testing to see if non-existent dates have been
// specified, for example: Feb. 30th, without specifying valid dates.
// That is, it is OK to specify invalid dates as long as there are
// valid dates. E.g., someone may want to specify: run on the 25th
// through 31st of every month. While that is acceptable, saying run
// only on Feb 31st is invalid.
//
if (pTrigger->Type.MonthlyDate.rgfDays & 0x40000000 && pTrigger->Type.MonthlyDate.rgfMonths & (TASK_FEBRUARY | TASK_APRIL | TASK_JUNE | TASK_SEPTEMBER | TASK_NOVEMBER) && !(pTrigger->Type.MonthlyDate.rgfMonths & ~(TASK_FEBRUARY | TASK_APRIL | TASK_JUNE | TASK_SEPTEMBER | TASK_NOVEMBER))) { //
// None of these months have a 31st day.
//
return FALSE; }
if (pTrigger->Type.MonthlyDate.rgfDays & 0x20000000 && pTrigger->Type.MonthlyDate.rgfMonths & TASK_FEBRUARY && !(pTrigger->Type.MonthlyDate.rgfMonths & ~TASK_FEBRUARY)) { //
// February does not have a 30th day. Allow for the specification
// of the 29th to run on leap year.
//
return FALSE; }
return TRUE; }
|