Problems integrating Windows Sheduled Tasks into application

Tech Tip: Click here to run a free scan for Windows Errors and optimize PC performance

From: TVR Fan (nosuch_at_address.com)
Date: 03/26/04


Date: Fri, 26 Mar 2004 11:43:00 -0000

I'm writing a wizard that will add a job to the Windows Scheduled Tasks. The
first page is my own page where the user types a job name and selects an
application command. When the user preses Next, the Wizard generates the
actual command needed by Windows Scheduled Tasks, creates the new task and,
gets the handles to the tasks 'Schedule' and 'Settings' pages and adds them
and a final page to the wizard. The final page is mine again and provides
the Finish button, whereapon the new task is saved.

Unfortunately, when the task is saved, the schedule settings are not and I
get an access violation. The task has been added to the Windows Scheduled
Task but no schedules are set for it.

I've quite possibly implemented this in a brain dead way and won't take
offence at any terse replies, just so long as they help! :-)

Here's the code (with error checking removed for conciseness):

// The class CWizardSchedule controls the wizard and has the following
member
// variables
 CWPSched01Welcome *m_pwpWelcomeDlg ; // a simple welcome screen
 CWPSched10Task *m_pwpTaskDlg ; // For entering application
specific details
 HPROPSHEETPAGE m_hpageScheduleDlg ; // this dialog is retrieved from
the task
 HPROPSHEETPAGE m_hpageSettingsDlg ; // this dialog is retrieved from
the task
 CWPSched99Finished *m_pwpFinishedDlg ; // A summary page with the
'Finished' button

// The Run() function actually sets up and calls the wizard
BOOL CWizardSchedule::Run(CWnd *pParentWindow)
{
 CProperty*** wizardDlg ;
 int iDlgReturn ;
 ITask *pITask = NULL ;

 // Initialise the options.
 m_hpageScheduleDlg = NULL ;
 m_hpageSettingsDlg = NULL ;
 m_pwpFinishedDlg = new CWPSched99Finished ;

 m_pwpTaskDlg->m_phpageScheduleDlg = &m_hpageScheduleDlg ;
 m_pwpTaskDlg->m_phpageSettingsDlg = &m_hpageSettingsDlg ;
 m_pwpTaskDlg->m_pwpFinishedDlg = m_pwpFinishedDlg ;

 // Set the task interface pointers in the task pages so
 // they point to a persistant object (the object is created
 // within m_pwpTaskDlg)
 m_pwpTaskDlg->m_ppITask = &pITask ;
 m_pwpFinishedDlg->m_ppITask = &pITask ;

 // Add pages to the wizard
 wizardDlg.AddPage ( m_pwpWelcomeDlg ) ;
 wizardDlg.AddPage ( m_pwpTaskDlg ) ;

 // The task dialog will need to initialise the schedule and settings
dialogs
 // once it has a task name.
 // It will also add the finished page at that time.

 //
 // Run the wizard
 //
 wizardDlg.SetWizardMode () ;
 iDlgReturn = wizardDlg.DoModal () ; // <-- this is the first highlited
point
                                      // in my code when it goes Bang. It's
in /
                                      // around the DestroyWindow() call at
the
                                      // end of CProperty***::DoModal()

 if ( iDlgReturn == ID_WIZFINISH )
 {
  // probably call the Persist interface
 }

 return true ;
}

// The CWPSched10Task::OnWizardNext() is where most of the com work is done
LRESULT CWPSched10Task::OnWizardNext()
{

 // Checking of essential fields removed

 if ( m_phpageScheduleDlg != NULL && *m_phpageScheduleDlg == NULL )
 {
  //=====================================================================
  // We must create a new schedule object and add its pages to the wizard
  // Checking of the return codes is removed for clarity
  // Much of this code is lifted directly from the MSDN documentation
  //=====================================================================
  HRESULT hr = ERROR_SUCCESS;

  // Call CoCreateInstance() to get a task scheduler object
  ITaskScheduler *pITS;
  hr = CoCreateInstance(CLSID_CTaskScheduler,
         NULL,
         CLSCTX_INPROC_SERVER,
         IID_ITaskScheduler,
         (void **) &pITS);

  // Call ITaskScheduler::NewWorkItem() or AddWorkItem() to get the task
  // object
  hr = pITS->NewWorkItem(m_stringJobTitle, // Name of task
        CLSID_CTask, // Class identifier
        IID_ITask, // Interface identifier
        (IUnknown**)m_ppITask); // Address of task interface

  pITS->Release(); // Release object

  // Add the command and parameters
  (*m_ppITask)->SetApplicationName ( m_strExecutable ) ;
  (*m_ppITask)->SetParameters ( m_strParams ) ;

  // Call ITask::QueryInterface() to get the IProvideTaskPage interface
  IProvideTaskPage *pIProvTaskPage;
  hr = (*m_ppITask)->QueryInterface(IID_IProvideTaskPage,
          (void **)&pIProvTaskPage);

  // Release the ITask interface. Don't do this as the task interface needs
to persist
  // until the wizard closes.
  //pITask->Release();

  // Call IProvideTaskPage::GetPage() for the Schedule page
  hr = pIProvTaskPage->GetPage(TASKPAGE_SCHEDULE,
           TRUE, // persist changes
           m_phpageScheduleDlg );

  // Release the IProvideTaskPage interface.
  pIProvTaskPage->Release();

  // Call IProvideTaskPage::GetPage() for the Settings page
  hr = pIProvTaskPage->GetPage(TASKPAGE_SETTINGS,
           TRUE, // persist changes
           m_phpageSettingsDlg );

  // Release the IProvideTaskPage interface.
  pIProvTaskPage->Release();

  // Add the pages to the wizard
  CProperty*** *pWizardDlg = (CProperty****)GetParent() ;
  BOOL bResult ;
  bResult = PropSheet_InsertPage (pWizardDlg->m_hWnd, 2,
*m_phpageScheduleDlg );
  bResult = PropSheet_InsertPage (pWizardDlg->m_hWnd, 3,
*m_phpageSettingsDlg );

  // Add the 'finished' page
  pWizardDlg->AddPage ( m_pwpFinishedDlg ) ;
 }

 return CPropertyPage::OnWizardNext();
}

// CWPSched99Finished::OnWizardFinish() is where I try to save the
// task. It all appears to work but crashes with an access violation
// deep within windows and although the task is saved, any schedule
// that the user has defined is not.
// Again, error checking has been removed for clarity.
BOOL CWPSched99Finished::OnWizardFinish()
{
 HRESULT hr = S_OK;
 IPersistFile *pIPersistFile = NULL ;

 // Just trying to see whether any schedules actually exist. It appears that
 // none do, even when the user defines one.
 WORD wCount ;
 (*m_ppITask)->GetTriggerCount ( &wCount ) ;

 /////////////////////////////////////////////////////////////////
 // Call IUnknown::QueryInterface to get a pointer to
 // IPersistFile and IPersistFile::Save to save
 // the new task to disk.
 /////////////////////////////////////////////////////////////////

 hr = (*m_ppITask)->QueryInterface(IID_IPersistFile,
         (void **)&pIPersistFile);

 hr = pIPersistFile->Save(NULL,
         TRUE);

 // Free the interfaces
 pIPersistFile->Release();
 (*m_ppITask)->Release() ;

 return CPropertyPage::OnWizardFinish();
}

The error I get is "Unhandled exception in PC_BaXMDI.exe (MSTASK.DLL):
0xC0000005: Access Violation"

Any help would be hugely welcomed!

---- Al.


Quantcast