Hi,
I'm trying to implement a watered down version of the windows task scheduler using ITaskScheduler. I can't use AT.exe because it doesn't let you name the task, I can't use the WMI task scheduler because it's based on AT. I also can't use schtasks because this needs to work on both win2k and win2k3 servers.
I've already written the program, and it's been quite a while since i've done any c/c++ programming. The program works fine when I run it from the console on a server, or when i run it from the console on my own computer.
When i plug it in to a web interface however, it doesn't run. I've wrapped it in a bat file so that i can redirect the output to a TXT file, and when I run it I get absolutely nothing from the program output to the file, but i do get all the output from the bat file.
Now, if i comment out the majority of the Task Scheduler program (ie everything EXCEPT the call to NewWorkItem() ), I get output from the program, including "Failed calling NewWorkItem, error = 0x80070005". I've looked everywhere for that error, but have had no success at all!
Currently I'm running windows 2000, and the test webserver is running IIS 6.0 on a windows 2003 machine. The web interface is in ASP.NET using VB.NET on the server side. If there's any more information required, please ask and i'll post it.
Here's the code:
#include <windows.h>
#include <initguid.h>
#include <ole2.h>
#include <mstask.h>
#include <msterr.h>
#include <objidl.h>
#include <wchar.h>
#include <stdio.h>
#include <atlbase.h>
#define DELIM ":"
#define APPLICATION_NAME L"d:\\apps\\cow\\scripts\\autoshutdown\\autoshutdown.cmd"
#define WORKING_DIRECTORY L"d:\\apps\\cow\\scripts\\autoshutdown"
#define ACCOUNT L"myAccount"
#define PASSWORD L"myPassword"
void printUsage( char *error )
{
if ( error != NULL )
printf( "%s\n\n", error );
printf( "Usage: TaskScheduler [options]\n" );
printf( "\nRequired Options:\n" );
printf( "\t/name:<name of the task>\n" );
printf( "\t/server:<UNC path of the server to run the task on>\n" );
printf( "\t/type:<iis | oracle>\n" );
printf( "\t/nature:<recycle | shutdown>\n" );
printf( "\t/shu:<normal | force>\n" );
printf( "\t/stime:<starting time HHmm>\n" );
printf( "\t/sdate:<starting date YYYYMMDD>\n" );
printf( "\t/run:<once | weekly-(umtwrfs)>\n" );
printf( "\nOther Options:\n" );
printf( "\t/v Will run the application in verbose mode\n" );
// printf( "\t\n" );
// printf( "\t\n" );
// printf( "\t\n" );
printf( "\n/? Will display this help screen\n" );
exit(1);
}
void doRunStuff( TASK_TRIGGER_TYPE *oTriggerType, TRIGGER_TYPE_UNION *oType, WEEKLY *oWeek, char* info)
{
if( strcmp( info, "once" ) == 0 )
{
*oTriggerType = TASK_TIME_TRIGGER_ONCE;
}
else if( strncmp( info, "weekly", 6 ) == 0 )
{
*oTriggerType = TASK_TIME_TRIGGER_WEEKLY;
oWeek->WeeksInterval = 1;
oWeek->rgfDaysOfTheWeek = 0;
for( unsigned int i = 6; i < strlen( info ); i++ )
{
switch ( info[i] )
{
case 'u': oWeek->rgfDaysOfTheWeek |= TASK_SUNDAY; break;
case 'm': oWeek->rgfDaysOfTheWeek |= TASK_MONDAY; break;
case 't': oWeek->rgfDaysOfTheWeek |= TASK_TUESDAY; break;
case 'w': oWeek->rgfDaysOfTheWeek |= TASK_WEDNESDAY; break;
case 'r': oWeek->rgfDaysOfTheWeek |= TASK_THURSDAY; break;
case 'f': oWeek->rgfDaysOfTheWeek |= TASK_FRIDAY; break;
case 's': oWeek->rgfDaysOfTheWeek |= TASK_SATURDAY; break;
}
}
oType->Weekly = *oWeek;
}
}
int main(int argc, char **argv)
{
printf("Started the program\n");
/////////////////////////////////////////////////////////////////
// Grab the arguments before we do anything else.
/////////////////////////////////////////////////////////////////
char *tmpArg = NULL;
char *startTime = NULL;
char *startDate = NULL;
CComBSTR taskName = NULL;
CComBSTR serverName = NULL;
CComBSTR serverType = NULL;
CComBSTR nature = NULL;
CComBSTR typeOfShutdown = NULL;
CComBSTR verbose = " ";
CComBSTR params = NULL;
WORD day = NULL;
WORD month = NULL;
WORD year = NULL;
WORD hour = NULL;
WORD min = NULL;
TASK_TRIGGER_TYPE oTriggerType;
TRIGGER_TYPE_UNION oType;
WEEKLY oWeek;
TASK_TRIGGER Trigger;
if ( argc <= 1 )
{
printUsage( "No arguments specified." );
}
for( int i = 1; i < argc; i++ )
{
tmpArg = strtok( strlwr( argv[i] ), DELIM );
if( strcmp( "/?", tmpArg ) == 0 ) printUsage( NULL );
else if( strcmp( tmpArg, "/name" ) == 0 ) taskName = strtok( NULL, DELIM );
else if( strcmp( tmpArg, "/server" ) == 0 ) serverName = strtok( NULL, DELIM );
else if( strcmp( tmpArg, "/type" ) == 0 ) serverType = strtok( NULL, DELIM );
else if( strcmp( tmpArg, "/nature" ) == 0 ) nature = strtok( NULL, DELIM );
else if( strcmp( tmpArg, "/shu" ) == 0 ) typeOfShutdown = strtok( NULL, DELIM );
else if( strcmp( tmpArg, "/stime" ) == 0 ) startTime = strtok( NULL, DELIM );
else if( strcmp( tmpArg, "/sdate" ) == 0 ) startDate = strtok( NULL, DELIM );
else if( strcmp( tmpArg, "/run" ) == 0 ) doRunStuff( &oTriggerType, &oType, &oWeek, strtok( NULL, DELIM ) );
else if( strcmp( tmpArg, "/v" ) == 0 ) verbose = L"-v";
else printUsage( strcat( tmpArg, " is an invalid argument." ) );
}
printf( "Read in all the arguments\n");
if( taskName == NULL ) printUsage( "Please specify a Task Name" );
if( serverName == NULL ) printUsage( "Please specify a Server Name" );
if( serverType == NULL ) printUsage( "Please specify a Server Type" );
if( nature == NULL ) printUsage( "Please specify the nature of the task" );
if( typeOfShutdown == NULL ) printUsage( "Please specify a type of shutdown" );
if( startTime == NULL ) printUsage( "Please specify a starting time" );
if( startDate == NULL ) printUsage( "Please specify a starting date" );
printf("validated arguments\n");
day = atoi( startDate + 6 );
startDate[6] = '\0';
month = atoi( startDate + 4 );
startDate[4] = '\0';
year = atoi( startDate );
min = atoi( startTime + 2 );
startTime[2] = '\0';
hour = atoi( startTime );
tmpArg = new char[256];
sprintf( tmpArg, "%S %S %S %S", typeOfShutdown, nature, serverType, verbose );
params = tmpArg;
delete( tmpArg );
printf("creating trigger\n");
ZeroMemory(&Trigger, sizeof(TASK_TRIGGER));
Trigger.cbTriggerSize = sizeof(TASK_TRIGGER);
Trigger.wBeginDay = day;
Trigger.wBeginMonth = month;
Trigger.wBeginYear = year;
Trigger.wStartHour = hour;
Trigger.wStartMinute = min;
Trigger.rgFlags = TASK_TRIGGER_FLAG_KILL_AT_DURATION_END;
Trigger.TriggerType = oTriggerType;
Trigger.Type = oType;
printf("trigger created\n");
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();
printf( "Failed calling CoCreateInstance, error = 0x%x\n", hr );
return 1;
}
}
else
{
printf( "Failed calling CoInitialize, error = 0x%x\n", hr );
return 1;
}
/////////////////////////////////////////////////////////////////
// Call ITaskScheduler::NewWorkItem to create new task.
/////////////////////////////////////////////////////////////////
ITask *pITask;
IPersistFile *pIPersistFile;
/*REMOVE ME
hr = pITS->SetTargetComputer( serverName );
if (FAILED(hr))
{
CoUninitialize();
printf("Failed calling SetTargetComputer, error = 0x%x\n",hr);
pITS->Release();
return 1;
}
REMOVE ME*/
hr = pITS->NewWorkItem(taskName, // Name of task
CLSID_CTask, // Class identifier
IID_ITask, // Interface identifier
(IUnknown**)&pITask); // Address of task interface
pITS->Release(); // Release object
if (FAILED(hr))
{
CoUninitialize();
printf("Failed calling NewWorkItem, error = 0x%x\n",hr);
return 1;
}
/*REMOVE ME
hr = pITask->SetApplicationName( APPLICATION_NAME );
if ( FAILED( hr ) )
{
pITask->Release();
CoUninitialize();
printf("Failed calling SetApplicationName, error = 0x%x\n", hr );
return 1;
}
hr = pITask->SetWorkingDirectory( WORKING_DIRECTORY );
if( FAILED( hr ) )
{
pITask->Release();
CoUninitialize();
printf("Failed calling SetWorkingDirectory, error = 0x%x\n", hr );
return 1;
}
hr = pITask->SetAccountInformation( ACCOUNT, PASSWORD );
if( FAILED( hr ) )
{
pITask->Release();
CoUninitialize();
printf("Failed calling SetAccountInformation, error = 0x%x\n", hr );
return 1;
}
hr = pITask->SetParameters( params );
if ( FAILED( hr ) )
{
pITask->Release();
CoUninitialize();
printf("Failed calling SetParameters, error = 0x%x\n", hr );
return 1;
}
WORD iTrigger;
ITaskTrigger *pITT;
hr = pITask->CreateTrigger( &iTrigger, &pITT );
if( FAILED(hr) )
{
pITask->Release();
pITT->Release();
CoUninitialize();
printf("Failed calling CreateTrigger, error = 0x%x\n", hr );
return 1;
}
hr = pITT->SetTrigger( &Trigger );
if( FAILED(hr) )
{
pITask->Release();
pITT->Release();
CoUninitialize();
printf("Failed calling SetTrigger, error = 0x%x\n", hr );
return 1;
}
// Release the ITaskTrigger when we're done with it.
pITT->Release();
/////////////////////////////////////////////////////////////////
// Call IUnknown::QueryInterface to get a pointer to
// IPersistFile and IPersistFile::Save to save
// the new task to disk.
/////////////////////////////////////////////////////////////////
hr = pITask->QueryInterface(IID_IPersistFile,
(void **)&pIPersistFile);
pITask->Release();
if (FAILED(hr))
{
CoUninitialize();
printf("Failed calling QueryInterface, error = 0x%x\n",hr);
return 1;
}
hr = pIPersistFile->Save(NULL,
TRUE);
pIPersistFile->Release();
if (FAILED(hr))
{
CoUninitialize();
printf("Failed calling Save, error = 0x%x\n",hr);
return 1;
}
REMOVE ME*/
printf( "Successfully created task %S on %S.\nThe task is to %S %S the %S server.", taskName, serverName, typeOfShutdown, nature, serverType );
return 0;
}
When i run this through the web interface i get the following output:
Started the program
Read in all the arguments
validated arguments
creating trigger
trigger created
Failed calling NewWorkItem, error = 0x80070005
if i remove the comment blocks that say "REMOVE ME" I don't get any output, as if the program never ran.
I'm pretty sure this is a web development issue, as my program works fine from the console. Unfortunately i haven't done much web development (and obviously it's been a long time since i've done c++).
so what exactly is this? Its just a taskscheduler that you can maintain from the internet with an asp.net page?
I looked through the first page of Google results for "0x80070005" and they have a common theme -- "Access denied." In other words, you can't create a new work item with the ASPNET or IUSR_MACHINENAME account. You need more privileges.
Quote from: quasi-modo on December 17, 2004, 05:42 PM
so what exactly is this? Its just a taskscheduler that you can maintain from the internet with an asp.net page?
Yeah, well kinda. It'll never be on the internet, just sitting on our intranet...
Quote from: MyndFyre on December 19, 2004, 03:22 AM
I looked through the first page of Google results for "0x80070005" and they have a common theme -- "Access denied." In other words, you can't create a new work item with the ASPNET or IUSR_MACHINENAME account. You need more privileges.
What's really blowing my mind is that if I un-comment everything, it doesn't run at all, I also had, and continue to have that page set to run as my own username (just to stay away from worrying about priviledges for now)