Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1199 lines
36 KiB

//+----------------------------------------------------------------------------
//
// 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;
}