//+---------------------------------------------------------------------------- // // 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_)