Help! A call to ITask::Run in Windows returns success but doesn't always start the task

M

Martin

I occasionally create a number of tasks via the ITaskScheduler
interface and then Run them immediately. For some reason, even though
Windows returns a successful response to the Run request, a task
occasionally fails to start up. No error code is ever given.

I've pulled together the essence of the VC++ program below. Take a look
and see if you have any ideas (other than suggesting the SDK is
flawed)! Note that you have to fill in the username and password at
the bottom before running it, as well as install a do-nothing program
named test1.exe into one of the directories in your path.

By default the program below, when compiled, will create 50 scheduled
tasks and try to run each one immediately. Note afterwards that some
of the tasks never run. Why??? By the way, I have tried this on a
slow system and a fast system, both running Windows XP with SP2.

/Martin

// testsched.cpp : Defines the entry point for the console application.

#include <stdio.h>
#include <mstask.h>
#include <sys/timeb.h>

const int MAX_NAME_LENGTH = 1024;

/********************************************************************
*
* FUNCTION: PersistTask
*
* DESCRIPTION: Calls QueryInterface to get a pointer to IPersistFile
* then IPersistFile::Save to save the task info. to disk.
*
* LIMITATIONS: Task name should be fewer than 1024 characters.
*
********************************************************************/

HRESULT PersistTask(ITask * const pITask)
{
HRESULT hr;
IPersistFile *pIPersistFile;


hr = pITask->QueryInterface(IID_IPersistFile,
(void **)&pIPersistFile);
if (FAILED(hr))
{
return hr;
}
hr = pIPersistFile->Save(NULL,
TRUE);
pIPersistFile->Release();
return hr;
}

/********************************************************************
*
* FUNCTION: CreateSchedItem
*
* DESCRIPTION: This function creates a new task for the scheduler.
* To run it or set up a trigger use one of the other
* functions below.
*
* LIMITATIONS: Task name should be fewer than 1024 characters.
*
********************************************************************/

HRESULT CreateSchedItem(const char *lpszTaskName,
const char *lpszAppName,
const char *lpszParms,
const char *lpszUserName,
const char *lpszPassword)

{
HRESULT hr = S_OK;
ITaskScheduler *pITS;

/////////////////////////////////////////////////////////////////
// Call CoInitialize to initialize the COM library and then
// CoCreateInstance to get the Task Scheduler object.
/////////////////////////////////////////////////////////////////

hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_CTaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskScheduler,
(void **) &pITS);
if (FAILED(hr))
{
CoUninitialize();
return hr;
}
}
else
{
return hr;
}

/////////////////////////////////////////////////////////////////
// Call ITaskScheduler::NewWorkItem to create new task.
/////////////////////////////////////////////////////////////////
wchar_t *lpwc = new wchar_t[MAX_NAME_LENGTH];
mbstowcs(lpwc, lpszTaskName, MAX_NAME_LENGTH);

ITask *pITask;
hr = pITS->NewWorkItem(lpwc, // Name of task
CLSID_CTask, // Class identifier
IID_ITask, // Interface
identifier
(IUnknown**)&pITask); // Address of task
interface

if (FAILED(hr))
{
CoUninitialize();
delete []lpwc;
return hr;
}

pITS->Release(); // release the scheduler
/////////////////////////////////////////////////////////////////
// Call ITask::SetApplicationName to associate an application with
the task
/////////////////////////////////////////////////////////////////

mbstowcs(lpwc, lpszAppName, MAX_NAME_LENGTH);
hr = pITask->SetApplicationName(lpwc);

if (FAILED(hr))
{
CoUninitialize();
delete []lpwc;
return hr;
}

/////////////////////////////////////////////////////////////////
// Call ITask::SetParameters to associate an application with the
task
/////////////////////////////////////////////////////////////////

mbstowcs(lpwc, lpszParms, MAX_NAME_LENGTH);
hr = pITask->SetParameters(lpwc);

if (FAILED(hr))
{
CoUninitialize();
delete []lpwc;
return hr;
}

/////////////////////////////////////////////////////////////////
// Call ITask::SetAccountInformation
/////////////////////////////////////////////////////////////////

wchar_t *lpwc2 = new wchar_t[MAX_NAME_LENGTH];

mbstowcs(lpwc, lpszUserName, MAX_NAME_LENGTH);
mbstowcs(lpwc2, lpszPassword, MAX_NAME_LENGTH);

hr = pITask->SetAccountInformation(lpwc, lpwc2);

delete [] lpwc; // Don't need name any more.
delete [] lpwc2; // Ditto
if (FAILED(hr))
{
CoUninitialize();
return hr;
}


hr = PersistTask(pITask);
pITask->Release();

CoUninitialize();
return hr;
}

/********************************************************************
*
* FUNCTION: RunSchedItem
*
* DESCRIPTION: This function runs the task immediately.
*
* LIMITATIONS: Task name should be fewer than 1024 characters.
*
********************************************************************/

HRESULT RunSchedItem(const char *lpszTaskName)

{
HRESULT hr = S_OK;
ITaskScheduler *pITS;

/////////////////////////////////////////////////////////////////
// Call CoInitialize to initialize the COM library and then
// CoCreateInstance to get the Task Scheduler object.
/////////////////////////////////////////////////////////////////

hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_CTaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskScheduler,
(void **) &pITS);
if (FAILED(hr))
{
CoUninitialize();
return hr;
}
}
else
{
return hr;
}
///////////////////////////////////////////////////////////////////
// Call ITaskScheduler::Activate to get the Task object.
///////////////////////////////////////////////////////////////////
wchar_t *lpwc = new wchar_t[MAX_NAME_LENGTH];
mbstowcs(lpwc, lpszTaskName, MAX_NAME_LENGTH);

ITask *pITask;
hr = pITS->Activate(lpwc,
IID_ITask,
(IUnknown**) &pITask);

delete [] lpwc; // Don't need name any more.
pITS->Release();
if (FAILED(hr))
{
CoUninitialize();
return hr;
}


///////////////////////////////////////////////////////////////////
// Call ITask::Run to start execution of "Test Task".
///////////////////////////////////////////////////////////////////

hr = pITask->Run();

pITask->Release();
CoUninitialize();
return hr;
}

/********************************************************************
*
* FUNCTION: main
*
* DESCRIPTION: Call CreateSchedItem and RunSchedItem numerous times
*
********************************************************************/
int main(int argc, char* argv[])
{
struct _timeb tstruct;
int icount, iresult;
char ts[20];
// if argument passed in, use it for number of loops else set to 50.
if (argc > 1) icount = atoi(argv[1]);
else icount = 50;
while (icount > 0)
{
_ftime( &tstruct); // Use UTC time in the task name to help figure
things out.
sprintf(ts,"Test
%02u-%02u-%02u-%03u",(tstruct.time%86400)/3600,(tstruct.time%3600)/60,tstruct.time%60,tstruct.millitm);
iresult = CreateSchedItem(ts,"Test1.exe","","***Fill in
Username***","***Fill in password***");
if (iresult != 0) return iresult;
iresult = RunSchedItem(ts);
if (iresult != 0) return iresult;
icount -= 1;
}
return 0;
}
 
D

David Candy

Maybe you just don't read the SDK

Remarks
Run is an asynchronous operation. A return code of S_OK means that the request to run the work item has been made; it does not mean that the work item has started running. There may be a delay of a few seconds after Run returns before the work item actually starts running.

To determine whether the work item is running, call IScheduledWorkItem::GetStatus.



Use TS's Log to see why it's not running.


--
--------------------------------------------------------------------------------------------------
http://webdiary.smh.com.au/archives/_comment/001075.html
=================================================
Martin said:
I occasionally create a number of tasks via the ITaskScheduler
interface and then Run them immediately. For some reason, even though
Windows returns a successful response to the Run request, a task
occasionally fails to start up. No error code is ever given.

I've pulled together the essence of the VC++ program below. Take a look
and see if you have any ideas (other than suggesting the SDK is
flawed)! Note that you have to fill in the username and password at
the bottom before running it, as well as install a do-nothing program
named test1.exe into one of the directories in your path.

By default the program below, when compiled, will create 50 scheduled
tasks and try to run each one immediately. Note afterwards that some
of the tasks never run. Why??? By the way, I have tried this on a
slow system and a fast system, both running Windows XP with SP2.

/Martin

// testsched.cpp : Defines the entry point for the console application.

#include <stdio.h>
#include <mstask.h>
#include <sys/timeb.h>

const int MAX_NAME_LENGTH = 1024;

/********************************************************************
*
* FUNCTION: PersistTask
*
* DESCRIPTION: Calls QueryInterface to get a pointer to IPersistFile
* then IPersistFile::Save to save the task info. to disk.
*
* LIMITATIONS: Task name should be fewer than 1024 characters.
*
********************************************************************/

HRESULT PersistTask(ITask * const pITask)
{
HRESULT hr;
IPersistFile *pIPersistFile;


hr = pITask->QueryInterface(IID_IPersistFile,
(void **)&pIPersistFile);
if (FAILED(hr))
{
return hr;
}
hr = pIPersistFile->Save(NULL,
TRUE);
pIPersistFile->Release();
return hr;
}

/********************************************************************
*
* FUNCTION: CreateSchedItem
*
* DESCRIPTION: This function creates a new task for the scheduler.
* To run it or set up a trigger use one of the other
* functions below.
*
* LIMITATIONS: Task name should be fewer than 1024 characters.
*
********************************************************************/

HRESULT CreateSchedItem(const char *lpszTaskName,
const char *lpszAppName,
const char *lpszParms,
const char *lpszUserName,
const char *lpszPassword)

{
HRESULT hr = S_OK;
ITaskScheduler *pITS;

/////////////////////////////////////////////////////////////////
// Call CoInitialize to initialize the COM library and then
// CoCreateInstance to get the Task Scheduler object.
/////////////////////////////////////////////////////////////////

hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_CTaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskScheduler,
(void **) &pITS);
if (FAILED(hr))
{
CoUninitialize();
return hr;
}
}
else
{
return hr;
}

/////////////////////////////////////////////////////////////////
// Call ITaskScheduler::NewWorkItem to create new task.
/////////////////////////////////////////////////////////////////
wchar_t *lpwc = new wchar_t[MAX_NAME_LENGTH];
mbstowcs(lpwc, lpszTaskName, MAX_NAME_LENGTH);

ITask *pITask;
hr = pITS->NewWorkItem(lpwc, // Name of task
CLSID_CTask, // Class identifier
IID_ITask, // Interface
identifier
(IUnknown**)&pITask); // Address of task
interface

if (FAILED(hr))
{
CoUninitialize();
delete []lpwc;
return hr;
}

pITS->Release(); // release the scheduler
/////////////////////////////////////////////////////////////////
// Call ITask::SetApplicationName to associate an application with
the task
/////////////////////////////////////////////////////////////////

mbstowcs(lpwc, lpszAppName, MAX_NAME_LENGTH);
hr = pITask->SetApplicationName(lpwc);

if (FAILED(hr))
{
CoUninitialize();
delete []lpwc;
return hr;
}

/////////////////////////////////////////////////////////////////
// Call ITask::SetParameters to associate an application with the
task
/////////////////////////////////////////////////////////////////

mbstowcs(lpwc, lpszParms, MAX_NAME_LENGTH);
hr = pITask->SetParameters(lpwc);

if (FAILED(hr))
{
CoUninitialize();
delete []lpwc;
return hr;
}

/////////////////////////////////////////////////////////////////
// Call ITask::SetAccountInformation
/////////////////////////////////////////////////////////////////

wchar_t *lpwc2 = new wchar_t[MAX_NAME_LENGTH];

mbstowcs(lpwc, lpszUserName, MAX_NAME_LENGTH);
mbstowcs(lpwc2, lpszPassword, MAX_NAME_LENGTH);

hr = pITask->SetAccountInformation(lpwc, lpwc2);

delete [] lpwc; // Don't need name any more.
delete [] lpwc2; // Ditto
if (FAILED(hr))
{
CoUninitialize();
return hr;
}


hr = PersistTask(pITask);
pITask->Release();

CoUninitialize();
return hr;
}

/********************************************************************
*
* FUNCTION: RunSchedItem
*
* DESCRIPTION: This function runs the task immediately.
*
* LIMITATIONS: Task name should be fewer than 1024 characters.
*
********************************************************************/

HRESULT RunSchedItem(const char *lpszTaskName)

{
HRESULT hr = S_OK;
ITaskScheduler *pITS;

/////////////////////////////////////////////////////////////////
// Call CoInitialize to initialize the COM library and then
// CoCreateInstance to get the Task Scheduler object.
/////////////////////////////////////////////////////////////////

hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(CLSID_CTaskScheduler,
NULL,
CLSCTX_INPROC_SERVER,
IID_ITaskScheduler,
(void **) &pITS);
if (FAILED(hr))
{
CoUninitialize();
return hr;
}
}
else
{
return hr;
}
///////////////////////////////////////////////////////////////////
// Call ITaskScheduler::Activate to get the Task object.
///////////////////////////////////////////////////////////////////
wchar_t *lpwc = new wchar_t[MAX_NAME_LENGTH];
mbstowcs(lpwc, lpszTaskName, MAX_NAME_LENGTH);

ITask *pITask;
hr = pITS->Activate(lpwc,
IID_ITask,
(IUnknown**) &pITask);

delete [] lpwc; // Don't need name any more.
pITS->Release();
if (FAILED(hr))
{
CoUninitialize();
return hr;
}


///////////////////////////////////////////////////////////////////
// Call ITask::Run to start execution of "Test Task".
///////////////////////////////////////////////////////////////////

hr = pITask->Run();

pITask->Release();
CoUninitialize();
return hr;
}

/********************************************************************
*
* FUNCTION: main
*
* DESCRIPTION: Call CreateSchedItem and RunSchedItem numerous times
*
********************************************************************/
int main(int argc, char* argv[])
{
struct _timeb tstruct;
int icount, iresult;
char ts[20];
// if argument passed in, use it for number of loops else set to 50.
if (argc > 1) icount = atoi(argv[1]);
else icount = 50;
while (icount > 0)
{
_ftime( &tstruct); // Use UTC time in the task name to help figure
things out.
sprintf(ts,"Test
%02u-%02u-%02u-%03u",(tstruct.time%86400)/3600,(tstruct.time%3600)/60,tstruct.time%60,tstruct.millitm);
iresult = CreateSchedItem(ts,"Test1.exe","","***Fill in
Username***","***Fill in password***");
if (iresult != 0) return iresult;
iresult = RunSchedItem(ts);
if (iresult != 0) return iresult;
icount -= 1;
}
return 0;
}
 
M

Martin

I realize that Run is asynchronous and that S_OK means that the task
has only been queued. The problem is that the tasks never run. Much
later Task Scheduler shows Last Run Time = "Never", Next Run Time =
"Never", Last Result = 0 and an empty Status. No errors are reported.
If I right-click on the task and select "Run" it works fine.

Try the code and see. I've only tried on machines with Windows XP and
SP2 but it seems to be a general problem on the machines I have tried
it on.

I've scrutinized MSDN up and down, Googled left and right but nada so
far. I'm reluctant to cough up $250 to pester Microsoft as I figure
I'll only have to write a follow-up task using GetStatus that checks up
on the Scheduler for tasks that it has "forgotten" about.

Generally I like Windows but these quirks are making me think twice
about it.
 
M

Martin

I've never looked at Task Scheduler's Advanced menu before. I spent
some time over lunch looking at the log and some of the other advanced
features.

None of the missed tasks appeared in the logs. I thought I also found a
few other tasks that weren't there but they may have been purged when
the logfile wrapped. In my last test of adding 40 jobs I found all but
the unscheduled tasks in the log (twice actually, once when the task
started and once when it completed).

I also played with the "Notify Me of Missed Tasks" option on the
Advanced Menu. It made me think, "hey, maybe they recognize that tasks
do get missed and I can get a program notification to help me with
automation". I checked the box, reran my test program, and noted that
as usual, a few tasks were missed but no notification popped up. This,
despite my being an administrator on the machine being tested. A
Google search of the "Notify Me of Missed Tasks" option indicated that
others had also experienced this; there were also a number of
grumblings, like mine, about the failures of the Task Scheduler.

Sigh! This last search did reveal a couple of third-party schedulers
that might be more robust. Now I just need to convince my clients to
pay for them. Or write my own notification for missed tasks.
 
D

David Candy

TS has memory limits and needs 10 minutes then starts a scavenger routine. Maybe this fits (but you normally get error messages). 40 tasks seem excessive unless you have really lazy customers, and they are blood sucking scum at that (see I've had customes that are so lazy).
Task Scheduler returns a "HRESULT: 0x8007007A" error message after you save many jobs
View products that this article applies to.
Article ID : 264522
Last Review : August 10, 2004
Revision : 5.0

This article was previously published under Q264522
SYMPTOMS
When you try to schedule a job with user credentials so that the task runs as if it were started by the user, you may receive the following error message:
The new task has been created, but may not run because the account information could not be set. The specific error is:
0x8007007a: The data area passed to a system call is too small.

CAUSE
This behavior occurs because the buffer that stores the account information for all scheduled tasks is limited; the buffer puts a limit on the number of jobs that you can schedule with user credentials. There is no limit on the number of jobs that you can create without user credentials.
RESOLUTION
To work around this behavior, stop and start the task scheduler service, wait for 10 to 15 minutes, and then schedule jobs.

There is a scavenger tool in task scheduler that runs 10 minutes after the service is started. This tool frees the memory that is still being used by deleted or scheduled jobs.
 
M

Martin

I ran into the 0x8007007a problem last week after returning from a
week's vacation. A couple of weeks worth of scheduled tasks had
surpassed Microsoft's limit and my tasks were all halting on 0x8007007a
(it actually started the day I got back). I was surprised that Task
Scheduler seemed so limited (I only had about 1000 tasks in the
scheduler) and made me question the robustness of Task Scheduler. It
seems pretty rough around the edges.

The problem I have now only seems to happen with pITask->Run. I have
never had problems with triggered tasks (i.e. scheduled). I could
probably do away with Run altogether and simply fork off a shell to
execute the job. It just seems tidier to treat all jobs through the
same mechanism regardless of whether they await a scheduled time or can
be run immediately.

/Martin
 
Top