|
|
//+----------------------------------------------------------------------------
//
// Job Scheduler service
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 1996.
//
// File: sch_at.cxx
//
// Contents: scheduler class object methods to support the NetSchedule
// (AT) APIs.
//
// Classes: CSchedule
//
// History: 30-Jan-96 EricB created
//
//-----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#pragma hdrstop
#include "Sched.hxx"
//
// Nothing here is needed for Chicago version since there is no AT support
// there.
//
#if !defined(_CHICAGO_)
//
// Forward references
//
VOID SetDomTrigger2Days( DWORD dwDaysOfMonth, WORD wFirstDayToCheck, WORD wLastDayToCheck, SYSTEMTIME *pstStart2, SYSTEMTIME *pstEnd2);
HRESULT CSchedule::AddAtJobCommon( const AT_INFO &At, DWORD *pID, CJob **ppJob, WCHAR wszName[], WCHAR wszID[] ) { HRESULT hr = S_OK;
//
// If the next at id is > 1 but there aren't any at jobs, the id can be
// reset to 1.
//
if (m_dwNextID > 1 && S_FALSE == _AtTaskExists()) { ResetAtID(); }
//
// Compose a name for the new AT job.
//
wcscpy(wszName, m_ptszFolderPath); wcscat(wszName, L"\\" TSZ_AT_JOB_PREFIX); _itow(m_dwNextID, wszID, 10); wcscat(wszName, wszID); wcscat(wszName, L"." TSZ_JOB); //
// Create a new job
//
CJob * pJob = CJob::Create(); if (pJob == NULL) { ERR_OUT("CSchedule::AddAtJob: CJob::Create", E_OUTOFMEMORY); return E_OUTOFMEMORY; }
//
// Convert the AT command line.
//
WCHAR * pwszApp, * pwszParams, wszCommand[MAX_PATH];
//
// net\svcdlls\atcmd\atcmd.c defines MAX_COMMAND_LEN to be 128. This
// should at some point be changed to MAX_PATH.
//
wcscpy(wszCommand, At.Command);
pwszApp = wszCommand;
//
// The app name and any command line params are all passed in one string,
// At.Command, so separate the app name from the params. Any path to the
// app plus the app name may be quoted. Otherwise, the parameters are
// separated from the app name by white space.
//
if (*pwszApp == L'"') { //
// Initial quote found, scan for end quote. The app name passed to
// SetApplicationName should not be quoted.
//
pwszApp++; pwszParams = pwszApp + 1; while (TRUE) { if (*pwszParams == L'\0') { //
// End of string found, no params.
//
pwszParams = NULL; break; } if (*pwszParams == L'"') { //
// End quote found.
//
break; } pwszParams++; } } else { //
// App path/name not quoted, scan for first white space for parameters.
//
pwszParams = wcspbrk(pwszApp, L" \t"); }
if (pwszParams != NULL) { // Null terminate app name string.
//
*pwszParams = L'\0'; //
// Move to first char of the parameters.
//
pwszParams++; //
// Skip any leading white space.
//
while (*pwszParams != L'\0') { if (*pwszParams != L' ' && *pwszParams != L'\t') { break; } pwszParams++; }
if (*pwszParams == L'\0') { //
// No params.
//
pwszParams = NULL; } }
hr = pJob->SetApplicationName(pwszApp); if (FAILED(hr)) { ERR_OUT("AddAtJob: SetApplicationName", hr); pJob->Release(); return hr; } if (pwszParams != NULL) { hr = pJob->SetParameters(pwszParams); if (FAILED(hr)) { ERR_OUT("AddAtJob: SetParameters", hr); pJob->Release(); return hr; } }
pJob->m_rgFlags |= JOB_I_FLAG_NET_SCHEDULE | TASK_FLAG_DELETE_WHEN_DONE;
if (!(At.Flags & JOB_NONINTERACTIVE)) { pJob->m_rgFlags |= TASK_FLAG_INTERACTIVE; }
pJob->m_hrStatus = SCHED_S_TASK_READY;
WCHAR szComment[SCH_BUF_LEN + 1]; if (LoadString(g_hInstance, IDS_NETSCHED_COMMENT, szComment, SCH_BUF_LEN) > 0) { pJob->SetComment(szComment); }
//
// Convert from NetSchedule representation to Job Scheduler representation
// of the run dates and times.
//
SYSTEMTIME stNow, stStart; SYSTEMTIME stDomStart1, stDomEnd1, stDomStart2, stDomEnd2; SYSTEMTIME stDowStart, stDowEnd;
stDomStart2.wDay = 0; // this serves as a flag
GetLocalTime(&stNow); stStart = stNow;
//
// JobTime is expressed as milliseconds after midnight, so convert to
// minutes.
//
DWORD dwMins = (DWORD)(At.JobTime / JOB_MILLISECONDS_PER_MINUTE);
stStart.wHour = (WORD)(dwMins / JOB_MINS_PER_HOUR); stStart.wMinute = (WORD)(dwMins % JOB_MINS_PER_HOUR); stStart.wSecond = stStart.wMilliseconds = 0;
DWORD DaysOfMonth = At.DaysOfMonth;
WORD wFirstDowRunOffset = 0, wFirstDomRunOffset = 0;
TASK_TRIGGER Trigger;
if (At.Flags & JOB_ADD_CURRENT_DATE) { //
// The flag is set, so add today as the first run date.
//
DaysOfMonth |= 1 << (stStart.wDay - 1); } else { if (DaysOfMonth == 0 && At.DaysOfWeek == 0) { //
// Neither bitmask is set, so run at the next opportunity.
//
Trigger.TriggerType = TASK_TIME_TRIGGER_ONCE;
if (! IsFirstTimeEarlier(&stNow, &stStart)) { // Job runs tomorrow
IncrementDay(&stStart); } } }
//
// Set the trigger values and save the new trigger(s).
//
// Initialize the start and end dates in case this is a periodic trigger.
// If it is not periodic, then new start and end dates will overwrite
// these initialization values.
//
Trigger.cbTriggerSize = sizeof(TASK_TRIGGER); Trigger.Reserved1 = pJob->m_Triggers.GetCount(); Trigger.wBeginYear = stStart.wYear; Trigger.wBeginMonth = stStart.wMonth; Trigger.wBeginDay = stStart.wDay; Trigger.wEndYear = 0; Trigger.wEndMonth = 0; Trigger.wEndDay = 0; Trigger.wStartHour = stStart.wHour; Trigger.wStartMinute = stStart.wMinute; Trigger.Reserved2 = 0; Trigger.wRandomMinutesInterval = 0;
Trigger.rgFlags = (At.Flags & JOB_RUN_PERIODICALLY) ? 0 : TASK_TRIGGER_FLAG_HAS_END_DATE;
Trigger.MinutesInterval = Trigger.MinutesDuration = 0;
if (DaysOfMonth == 0 && At.DaysOfWeek == 0) { // First, zero out the end date flag
Trigger.rgFlags &= ~JOB_RUN_PERIODICALLY;
// This is a TASK_TIME_TRIGGER_ONCE job, and we are ready to commit
hr = pJob->m_Triggers.Add(Trigger); if (FAILED(hr)) { ERR_OUT("AddAtJob: m_Triggers.Add Once", hr); pJob->Release(); return hr; } }
if (DaysOfMonth > 0) { Trigger.TriggerType = TASK_TIME_TRIGGER_MONTHLYDATE; Trigger.Type.MonthlyDate.rgfDays = DaysOfMonth; Trigger.Type.MonthlyDate.rgfMonths = JOB_RGFMONTHS_MAX;
if (!(At.Flags & JOB_RUN_PERIODICALLY)) { CalcDomTriggerDates(DaysOfMonth, stNow, stStart, &stDomStart1, &stDomEnd1, &stDomStart2, &stDomEnd2);
Trigger.wBeginYear = stDomStart1.wYear; Trigger.wBeginMonth = stDomStart1.wMonth; Trigger.wBeginDay = stDomStart1.wDay; Trigger.wEndYear = stDomEnd1.wYear; Trigger.wEndMonth = stDomEnd1.wMonth; Trigger.wEndDay = stDomEnd1.wDay; }
hr = pJob->m_Triggers.Add(Trigger); if (FAILED(hr)) { ERR_OUT("AddAtJob: m_Triggers.Add Dom1", hr); pJob->Release(); return hr; }
if (stDomStart2.wDay != 0) { Trigger.wBeginYear = stDomStart2.wYear; Trigger.wBeginMonth = stDomStart2.wMonth; Trigger.wBeginDay = stDomStart2.wDay; Trigger.wEndYear = stDomEnd2.wYear; Trigger.wEndMonth = stDomEnd2.wMonth; Trigger.wEndDay = stDomEnd2.wDay;
Trigger.Reserved1 = pJob->m_Triggers.GetCount(); hr = pJob->m_Triggers.Add(Trigger); if (FAILED(hr)) { ERR_OUT("AddAtJob: m_Triggers.Add Dom2", hr); pJob->Release(); return hr; } } }
if (At.DaysOfWeek > 0) { Trigger.Reserved1 = pJob->m_Triggers.GetCount(); Trigger.TriggerType = TASK_TIME_TRIGGER_WEEKLY; Trigger.Type.Weekly.WeeksInterval = 1; //
// Convert AT_INFO DOW to Scheduler DOW:
// Scheduler rgfDaysOfTheWeek: Sunday = bit 0, Monday = bit 1.
// AT_INFO DaysOfWeek: Monday = bit 0, Sunday = bit 6.
//
Trigger.Type.Weekly.rgfDaysOfTheWeek = At.DaysOfWeek << 1; if (Trigger.Type.Weekly.rgfDaysOfTheWeek & 0x0080) { Trigger.Type.Weekly.rgfDaysOfTheWeek &= ~0x0080; Trigger.Type.Weekly.rgfDaysOfTheWeek |= 1; }
if (!(At.Flags & JOB_RUN_PERIODICALLY)) { CalcDowTriggerDate(stNow, stStart, &stDowStart, &stDowEnd);
Trigger.wBeginYear = stDowStart.wYear; Trigger.wBeginMonth = stDowStart.wMonth; Trigger.wBeginDay = stDowStart.wDay; Trigger.wEndYear = stDowEnd.wYear; Trigger.wEndMonth = stDowEnd.wMonth; Trigger.wEndDay = stDowEnd.wDay; }
hr = pJob->m_Triggers.Add(Trigger); if (FAILED(hr)) { ERR_OUT("AddAtJob: m_Triggers.Add", hr); pJob->Release(); return hr; } }
// get the AT task maximum run time from the registry
// if the call fails, the default of 72 will be used
DWORD dwMaxRunTime = 0; if (SUCCEEDED(GetAtTaskMaxHours(&dwMaxRunTime))) pJob->SetMaxRunTime(dwMaxRunTime);
//
// Set the same flags as in CJob::CreateTrigger. (We should really call
// CreateTrigger in this function instead of creating the trigger ourselves.)
//
pJob->SetFlag(JOB_I_FLAG_PROPERTIES_DIRTY); pJob->SetTriggersDirty();
//
// Save the new job. Obviously this is one place we definitely don't want
// the AT job flag cleared on save!
//
hr = pJob->SaveP(wszName, TRUE, SAVEP_VARIABLE_LENGTH_DATA | SAVEP_PRESERVE_NET_SCHEDULE);
if (FAILED(hr)) { if (hr == HRESULT_FROM_WIN32(ERROR_FILE_EXISTS)) { //
// Name collision; someone has renamed a task to match the next
// AT job. Recalc the max AT job ID.
//
GetNextAtID(&m_dwNextID); wcscpy(wszName, m_ptszFolderPath); wcscat(wszName, L"\\" TSZ_AT_JOB_PREFIX); _itow(m_dwNextID, wszID, 10); wcscat(wszName, wszID); wcscat(wszName, TSZ_DOTJOB);
//
// Now, retry the save.
//
hr = pJob->SaveP(wszName, TRUE, SAVEP_VARIABLE_LENGTH_DATA | SAVEP_PRESERVE_NET_SCHEDULE); if (FAILED(hr)) { ERR_OUT("CSchedule::AddAtJob: Save", hr); pJob->Release(); return hr; } } else { ERR_OUT("CSchedule::AddAtJob: Save", hr); pJob->Release(); return hr; } }
*ppJob = pJob; return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::GetAtTaskMaxHours
//
// Synopsis: Check a registry setting to see what max run time value a user
// has specified for AT tasks. If the key is not present or can't
// be opened then interpret as the normal task default of 72.
//
// Arguments: none
//
// Returns: bool
//
// Notes: This method is not exposed to external clients, thus it is not
// part of a public interface.
//-----------------------------------------------------------------------------
HRESULT CSchedule::GetAtTaskMaxHours(DWORD* pdwMaxHours) { //
// Open the schedule service key
//
long lErr; HKEY hSchedKey; lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SCH_SVC_KEY, 0, KEY_READ | KEY_WRITE, &hSchedKey); if (lErr != ERROR_SUCCESS) { ERR_OUT("RegOpenKeyEx of Scheduler key", lErr); return(HRESULT_FROM_WIN32(lErr)); }
//
// Get the AtTaskMaxHours setting
//
bool bNeedToUpdate = false; DWORD dwMaxHours = 0; DWORD cb = sizeof(DWORD); lErr = RegQueryValueEx(hSchedKey, SCH_ATTASKMAXHOURS_VALUE, NULL, NULL, (LPBYTE)&dwMaxHours, &cb); if (lErr != ERROR_SUCCESS) { if (lErr != ERROR_FILE_NOT_FOUND) { ERR_OUT("Read of AtTaskMaxHours registry value", lErr); RegCloseKey(hSchedKey); return(HRESULT_FROM_WIN32(lErr)); }
//
// Need to create the missing registry entry
//
dwMaxHours = DEFAULT_MAXRUNTIME_HOURS; bNeedToUpdate = true; }
//
// Correct out-of-bounds stored registry values
//
if (dwMaxHours > 999) { dwMaxHours = 999; bNeedToUpdate = true; }
if (bNeedToUpdate) { lErr = RegSetValueEx(hSchedKey, SCH_ATTASKMAXHOURS_VALUE, 0, REG_DWORD,(CONST BYTE *)&dwMaxHours, sizeof(DWORD)); if (lErr != ERROR_SUCCESS) { ERR_OUT("Update of AtTaskMaxHours registry value", lErr); RegCloseKey(hSchedKey); return(HRESULT_FROM_WIN32(lErr)); } }
//
// Convert the stored value to a value that has meaning to the scheduler
//
if (!dwMaxHours) dwMaxHours = INFINITE; // If value is zero, return infinite (-1) for max run time
else dwMaxHours *= 3600000; // The stored value is in hours, but the value passed back
// needs to be in milliseconds, so convert
RegCloseKey(hSchedKey);
// Set the value to be passed back
*pdwMaxHours = dwMaxHours;
return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::AddAtJob
//
// Synopsis: create a downlevel job
//
// Arguments: [At] - reference to an AT_INFO struct
// [pID] - returns the new ID (optional, can be NULL)
//
// Returns: HRESULTS
//
// Notes: This method is not exposed to external clients, thus it is not
// part of a public interface.
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::AddAtJob(const AT_INFO &At, DWORD * pID) { TRACE(CSchedule, AddAtJob); HRESULT hr = S_OK; CJob *pJob; WCHAR wszName[MAX_PATH + 1]; WCHAR wszID[SCH_SMBUF_LEN];
hr = AddAtJobCommon(At, pID, &pJob, wszName, wszID);
if (FAILED(hr)) { ERR_OUT("AddAtJob: AddAtJobCommon", hr); return hr; }
//
// Free the job object.
//
pJob->Release();
//
// Return the new job's ID and increment the ID counter
//
if (pID != NULL) { *pID = m_dwNextID; }
hr = IncrementAndSaveID();
return hr; }
//+---------------------------------------------------------------------------
//
// Function: IsMonthBitSet
//
// Synopsis: Returns nonzero if 1-based bit [wDay] in [dwDaysOfMonth] is
// set.
//
// History: 09-26-96 DavidMun Created
//
//----------------------------------------------------------------------------
inline BOOL IsMonthBitSet(DWORD dwDaysOfMonth, WORD wDay) { return dwDaysOfMonth & (1 << (wDay - 1)); }
//+---------------------------------------------------------------------------
//
// Function: CalcDomTriggerDates
//
// Synopsis: Calculate the dates for the start and end of the Day Of Month
// trigger(s).
//
// Arguments: [dwDaysOfMonth] - bit array, bit 0=day 1, etc. At least one
// bit must be set!
// [stNow] - current time
// [stStart] - same as [stNow] but has hour & minute
// values of actual job run time
// [pstStart1] - filled with start date of first trigger
// [pstEnd1] - filled with end date of first trigger
// [pstStart2] - filled with start date of second trigger;
// wDay is 0 if second trigger not needed
// [pstEnd2] - filled with end date of second trigger,
// wDay is 0 if second trigger not needed
//
// Modifies: All output arguments.
//
// History: 09-26-96 DavidMun Created
//
// Notes: Only the month, day, and year values in the output structs
// are used.
//
//----------------------------------------------------------------------------
VOID CalcDomTriggerDates( DWORD dwDaysOfMonth, const SYSTEMTIME &stNow, const SYSTEMTIME &stStart, SYSTEMTIME *pstStart1, SYSTEMTIME *pstEnd1, SYSTEMTIME *pstStart2, SYSTEMTIME *pstEnd2) { BOOL fDone = FALSE; WORD wStart1MonthDays;
// assert not all bits below 32 are zero
Win4Assert(dwDaysOfMonth & JOB_RGFDAYS_MAX);
//
// Find the start date for the first DOM trigger.
//
*pstStart1 = stNow;
if (IsFirstTimeEarlier(&stStart, &stNow)) { // already past run time today
IncrementDay(pstStart1); }
HRESULT hr = MonthDays(pstStart1->wMonth, pstStart1->wYear, &wStart1MonthDays); if (FAILED(hr)) { schAssert(!"Bad systemtime"); return; }
do { while (!IsMonthBitSet(dwDaysOfMonth, pstStart1->wDay) && pstStart1->wDay <= wStart1MonthDays) { pstStart1->wDay++; }
//
// now either:
// start1.wDay > wStart1MonthDays or
// bit at start1.wDay is 1
//
if (pstStart1->wDay > wStart1MonthDays) { // have to go on to next month to get the first start date
pstStart1->wDay = 1; IncrementMonth(pstStart1); MonthDays(pstStart1->wMonth, pstStart1->wYear, &wStart1MonthDays); } else { fDone = TRUE; } } while (!fDone);
//
// Now bit at pstStart1->wDay is on, and pstStart1->wDay is a valid day in
// pstStart1->wMonth, and wStart1MonthDays is the number of days in the
// month pstStart1->wMonth. Next we need to find end1.
//
// end1 is initialized to start1.
//
// If there are any days set before the start day, then end1.wMonth will
// be start1.wMonth + 1, and end1.wDay will be the last of the days that
// is set before start1.wDay, with the restriction that end1.wDay is not
// greater than the number of days in end1.wMonth.
//
*pstEnd1 = *pstStart1; WORD wDay;
if (pstStart1->wDay > 1) { WORD wEnd1Month; WORD wEnd1MonthDays;
wEnd1Month = pstEnd1->wMonth + 1;
if (wEnd1Month > 12) { wEnd1Month = 1; }
MonthDays(wEnd1Month, pstEnd1->wYear, &wEnd1MonthDays); WORD wMaxDay = min(pstStart1->wDay - 1, wEnd1MonthDays);
for (wDay = 1; wDay <= wMaxDay; wDay++) { if (IsMonthBitSet(dwDaysOfMonth, wDay)) { pstEnd1->wDay = wDay; } } }
//
// If any day bits were set before start1.wDay then end1.wDay will no
// longer == start1.wDay, and end1 will be referring to the next month.
//
// Otherwise, End1 will remain in the same month as Start1, but will
// need to be set to the last day bit set in the Start1 month.
//
if (pstEnd1->wDay < pstStart1->wDay) { IncrementMonth(pstEnd1); } else { for (wDay = pstStart1->wDay + 1; wDay <= wStart1MonthDays; wDay++) { if (IsMonthBitSet(dwDaysOfMonth, wDay)) { pstEnd1->wDay = wDay; } } }
//
// Now start1 and end1 are set. next, check if there's a need for the
// second trigger. There are two cases where a second trigger is
// required.
//
// Case a: second trigger must fill time between end of first trigger
// and start of first trigger. for example, job is to run on next
// 1, 30, 31 and start1 is 1/31. then end1 will be 2/1, and a second
// trigger must go from 3/30 to 3/30. Note this case can only occur
// if End1.wMonth == February.
//
// Case b: second trigger must fill time somewhere in the 29-31 day range.
// For example, job is to run on next 1-31, and current day is 4/1. so
// start1 is 4/1, end1 is 4/30, then start2 must be 5/31 to 5/31.
//
// As another example, job is to run on next 27, 28, 30, current day is
// 2/28. then start1 is 2/28, end1 is 3/27, start2 is 3/30, end2 is 3/30.
//
// Case b only occurs when there are bits set for days beyond the last day
// of pstStart1->wmonth.
//
//
//
// test if we need case a.
//
if (pstEnd1->wMonth == 2 && pstStart1->wDay > 29 && pstEnd1->wDay < pstStart1->wDay - 1) { //
// There's a gap between end1 and start1. we need the second trigger
// if there are any day bits set in that gap.
//
Win4Assert(pstStart1->wMonth == 1); Win4Assert(pstEnd1->wDay + 1 <= 30);
*pstStart2 = *pstEnd1; pstStart2->wMonth = 3; *pstEnd2 = *pstStart2;
SetDomTrigger2Days(dwDaysOfMonth, pstEnd1->wDay + 1, pstStart1->wDay - 1, pstStart2, pstEnd2); } else if (wStart1MonthDays < 31) { //
// we have case b if any bits after the last day in pstStart1->wMonth
// are set.
//
*pstStart2 = *pstEnd1; if (pstEnd1->wMonth == pstStart1->wMonth) { pstStart2->wMonth++; } *pstEnd2 = *pstStart2;
SetDomTrigger2Days(dwDaysOfMonth, wStart1MonthDays + 1, 31, pstStart2, pstEnd2); } else { // no second trigger
pstStart2->wDay = 0; pstEnd2->wDay = 0; } }
//+---------------------------------------------------------------------------
//
// Function: SetDomTrigger2Days
//
// Synopsis: Set the start and end dates for the second DOM trigger.
//
// Arguments: [dwDaysOfMonth] - bit array, bit 0=day 1, etc. At least
// one bit must be set!
// [wFirstDayToCheck] - 1 based
// [wLastDayToCheck] - 1 based, must be >= [wFirstDayToCheck]
// [pstStart2] - filled with start date of second
// trigger; wDay is 0 if no second
// trigger is required.
// [pstEnd2] - filled with end date of second trigger;
// wDay is 0 if no second trigger is
// required.
//
// Modifies: All out args.
//
// History: 09-26-96 DavidMun Created
//
// Notes: This is a helper function called only by
// CalcDomTriggerDates.
//
//----------------------------------------------------------------------------
VOID SetDomTrigger2Days( DWORD dwDaysOfMonth, WORD wFirstDayToCheck, WORD wLastDayToCheck, SYSTEMTIME *pstStart2, SYSTEMTIME *pstEnd2) { WORD wDay;
pstStart2->wDay = 0; pstEnd2->wDay = 0;
for (wDay = wFirstDayToCheck; wDay <= wLastDayToCheck; wDay++) { if (IsMonthBitSet(dwDaysOfMonth, wDay)) { //
// if the start of the second trigger hasn't been assigned
// yet, assign it. otherwise update the end of the second
// trigger to the current day.
//
if (!pstStart2->wDay) { pstStart2->wDay = wDay; } else { pstEnd2->wDay = wDay; } } }
//
// there may have been only one day on in the gap, so the start and
// end of trigger 2 are the same day.
//
if (pstStart2->wDay && !pstEnd2->wDay) { pstEnd2->wDay = pstStart2->wDay; } }
//+---------------------------------------------------------------------------
//
// Function: CalcDowTriggerDate
//
// Synopsis: Set the start and end dates for the Day of Week trigger.
//
// Arguments: [stNow] - Current time
// [stStart] - same as [stNow] but with hour and minute of
// actual run time
// [pstStart] - filled with start date
// [pstEnd] - filled with end date
//
// Modifies: *[pstStart], *[pstEnd]
//
// History: 09-26-96 DavidMun Created
//
//----------------------------------------------------------------------------
VOID CalcDowTriggerDate( const SYSTEMTIME &stNow, const SYSTEMTIME &stStart, SYSTEMTIME *pstStart, SYSTEMTIME *pstEnd) { *pstStart = stNow;
//
// If it's too late for the job to run today, make the start date
// tomorrow.
//
if (IsFirstTimeEarlier(&stStart, &stNow)) { IncrementDay(pstStart); }
//
// Make the end date 6 days later than the start date, that way we cover a
// full week and all runs will happen.
//
*pstEnd = *pstStart; pstEnd->wDay += 6;
WORD wLastDay; HRESULT hr = MonthDays(pstEnd->wMonth, pstEnd->wYear, &wLastDay);
if (FAILED(hr)) { schAssert(!"Bad systemtime"); } else { if (pstEnd->wDay > wLastDay) { //
// Wrap to the next month.
//
pstEnd->wDay -= wLastDay; IncrementMonth(pstEnd); } } }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::GetAtJob, private
//
// Synopsis: retrieves a downlevel job's AT info
//
// Arguments: [pwszFileName] - job object's file name
// [pAt] - pointer to an AT_INFO struct
// [pwszCommand] - buffer for the command string
// [pcchCommand] - on input, size of supplied buffer, on output,
// size needed if supplied buffer is too small.
//
// 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.
//-----------------------------------------------------------------------------
STDMETHODIMP CSchedule::GetAtJob(LPCTSTR pwszFileName, AT_INFO * pAt, LPWSTR pwszCommand, DWORD * pcchCommand) { TRACE(CSchedule, GetAtJob);
CJob * pJob = CJob::Create();
if (pJob == NULL) { return E_OUTOFMEMORY; }
HRESULT hr = pJob->LoadP(pwszFileName, 0, TRUE, TRUE);
if (FAILED(hr)) { ERR_OUT("GetAtJob: LoadP", hr); pJob->Release(); return hr; }
hr = pJob->GetAtInfo(pAt, pwszCommand, pcchCommand);
if (FAILED(hr)) { ERR_OUT("GetAtJob: GetAtInfo", hr); pJob->Release(); return hr; }
pJob->Release();
return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::IncrementAndSaveID
//
// Synopsis: Increment the NextJobID value and save it to the registry.
//
//-----------------------------------------------------------------------------
HRESULT CSchedule::IncrementAndSaveID(void) { EnterCriticalSection(&m_CriticalSection); long lErr; HKEY hSchedKey; lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SCH_SVC_KEY, 0, KEY_SET_VALUE, &hSchedKey); if (lErr != ERROR_SUCCESS) { ERR_OUT("RegOpenKeyEx of Scheduler key", lErr); LeaveCriticalSection(&m_CriticalSection); return(HRESULT_FROM_WIN32(lErr)); }
m_dwNextID++;
//
// update the registry entry
//
lErr = RegSetValueEx(hSchedKey, SCH_NEXTATJOBID_VALUE, 0, REG_DWORD, (CONST BYTE *)&m_dwNextID, sizeof(DWORD)); if (lErr != ERROR_SUCCESS) { ERR_OUT("Create of NextAtJobId registry value", lErr); m_dwNextID--; RegCloseKey(hSchedKey); LeaveCriticalSection(&m_CriticalSection); return(HRESULT_FROM_WIN32(lErr)); } RegCloseKey(hSchedKey); LeaveCriticalSection(&m_CriticalSection); return S_OK; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::ResetAtID
//
// Synopsis: Set the next at id value in the registry to 1
//
//-----------------------------------------------------------------------------
HRESULT CSchedule::ResetAtID(void) { HRESULT hr = S_OK; HKEY hSchedKey = NULL;
EnterCriticalSection(&m_CriticalSection);
m_dwNextID = 1;
do { LONG lErr;
//
// Open the schedule service key
//
lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SCH_SVC_KEY, 0, KEY_READ | KEY_WRITE, &hSchedKey);
if (lErr != ERROR_SUCCESS) { ERR_OUT("RegOpenKeyEx of Scheduler key", lErr); hr = HRESULT_FROM_WIN32(lErr); break; }
//
// Set the next At Job ID value to 1. If the value is not present,
// it will be created.
//
lErr = RegSetValueEx(hSchedKey, SCH_NEXTATJOBID_VALUE, 0, REG_DWORD, (CONST BYTE *) &m_dwNextID, sizeof(m_dwNextID));
if (lErr != ERROR_SUCCESS) { ERR_OUT("Create of NextAtJobId registry value", lErr); hr = HRESULT_FROM_WIN32(lErr); } } while (0);
if (hSchedKey) { RegCloseKey(hSchedKey); } LeaveCriticalSection(&m_CriticalSection);
return hr; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::_AtTaskExists, private
//
// Synopsis: Check for existing AT tasks
//
// Returns: S_OK - an AT task was found
// S_FALSE - no AT tasks were found
// E_*
//
//-----------------------------------------------------------------------------
HRESULT CSchedule::_AtTaskExists(void) { WIN32_FIND_DATA fd; HANDLE hFileFindContext;
hFileFindContext = FindFirstFile(g_wszAtJobSearchPath, &fd);
if (hFileFindContext == INVALID_HANDLE_VALUE) { return S_FALSE; }
CJob * pJob = CJob::Create(); if (pJob == NULL) { return E_OUTOFMEMORY; }
HRESULT hr = S_FALSE; DWORD rgFlags;
do { if (!IsValidAtFilename(fd.cFileName)) { continue; }
HRESULT hrLoad = LoadAtJob(pJob, fd.cFileName);
if (FAILED(hrLoad)) { hr = hrLoad; break; }
pJob->GetAllFlags(&rgFlags);
if (rgFlags & JOB_I_FLAG_NET_SCHEDULE) { hr = S_OK; break; } } while (FindNextFile(hFileFindContext, &fd));
FindClose(hFileFindContext); pJob->Release();
return hr; }
//+----------------------------------------------------------------------------
//
// Member: CSchedule::InitAtID
//
// Synopsis: Obtains the current AT task ID from the registry.
//
//-----------------------------------------------------------------------------
HRESULT CSchedule::InitAtID(void) { //
// Open the schedule service key
//
long lErr; HKEY hSchedKey; lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE, SCH_SVC_KEY, 0, KEY_READ | KEY_WRITE, &hSchedKey); if (lErr != ERROR_SUCCESS) { ERR_OUT("RegOpenKeyEx of Scheduler key", lErr); return(HRESULT_FROM_WIN32(lErr)); }
//
// Get the next At Job ID
//
DWORD cb = sizeof(DWORD); lErr = RegQueryValueEx(hSchedKey, SCH_NEXTATJOBID_VALUE, NULL, NULL, (LPBYTE)&m_dwNextID, &cb); if (lErr != ERROR_SUCCESS) { if (lErr != ERROR_FILE_NOT_FOUND) { ERR_OUT("Read of NextAtJobId registry value", lErr); RegCloseKey(hSchedKey); return(HRESULT_FROM_WIN32(lErr)); }
//
// Scan AT jobs for value if registry entry absent
//
GetNextAtID(&m_dwNextID);
//
// Create registry entry
//
lErr = RegSetValueEx(hSchedKey, SCH_NEXTATJOBID_VALUE, 0, REG_DWORD, (CONST BYTE *)&m_dwNextID, sizeof(DWORD)); if (lErr != ERROR_SUCCESS) { ERR_OUT("Create of NextAtJobId registry value", lErr); RegCloseKey(hSchedKey); return(HRESULT_FROM_WIN32(lErr)); } }
RegCloseKey(hSchedKey);
return S_OK; }
#endif // #if !defined(_CHICAGO_)
|