May 15, 1995
Version 0.8
The System Agent feature of Microsoft® Plus! is a general-purpose scheduler application with which users can schedule programs to run at various times. System Agent can launch any Windows®-based or MS-DOS®-based program, without requiring any modification to the program being scheduled. However, there are things independent software vendors (ISVs) can do in their applications to add value for customers using those programs in conjunction with System Agent. Programs that have been modified to exploit System Agent are referred to as "SAGE-aware." (“SAGE” refers to System Agent’s underlying scheduling engine. SAGE.EXE is run whenever a user logs on; it in turn loads SAGE.DLL, which monitors the system and runs programs at the appointed time and which exposes scheduling services to programs. System Agent’s user interface is provided by a separate program, SYSAGENT.EXE, which uses the API provided by SAGE.DLL to edit the SAGE task data base.)
SAGE-aware programs have the following characteristics:
In addition, System Agent exposes an API that applications can use to directly manipulate SAGE's data base of scheduled programs. Using this API, applications can add themselves to SAGE's queue without requiring the user to manually schedule the program using System Agent's user interface. In addition, System Agent exposes APIs that applications can use to detect if System Agent is running, and to suspend System Agent so that it will not start up any programs until the application says so (or until the system is restarted).
SAGE-aware programs declare themselves as such by creating a key in \\HKLM\Software\Microsoft\Plus!\System Agent\SAGE. The name of the key can be anything the program wants, but it should contain the following values:
Program= | Name of the program's EXE file. This must be the same name under which the program's PerApp path is registered. |
Friendly Name= | Display name that System Agent will use in populating the drop-down list in its "Schedule a program" dialog. |
Settings= | 1-bit binary field indicating whether program has a Settings dialog. |
Result Codes | Optional key containing a set of value pairs mapping an exit code to a string describing the meaning of that exit code. For example, for SCANDSKW, the Result Codes key may contain a value such as: 0="ScanDisk completed successfully; no errors were found.". This is to allow SAGE to keep a human-comprehensible log of the results of the programs it runs. In addition to the value pairs, this key should also contain a String value named "Success", which indicates the highest value for an exit code that designates that the program completed successfully. The value names should be string values, specified in decimal; the allowable range is 0–32767. |
In addition, SAGE-aware applications must register a "Per Application Path", as defined by the Windows 95 Setup guidelines. SAGE will use the PerApp path to locate the program's executable, and it will initialize its environment with the path (if any) specified by the Path value of the app's key in the PerApp path section of the registry.
SAGE-aware applications must also support the /SAGERUN:n command line switch. SAGE will pass this switch on the command line when launching programs that have registered themselves as being SAGE-aware. This is to let the application know that it is being run by SAGE and thus that is should behave in a suitable fashion for unattended operation. The :n suffix allows SAGE to specify a group of application defined settings to be used by this scheduled instance of the program (as described below). If your program does not support multiple saved sets of settings, it may ignore this suffix.
SAGE-aware programs also may support a Settings dialog. If so (as indicated by the Settings= value of the program's registry key), they must support the following command line switches:
SAGESET:n | This tells the application to display its SAGE settings dialog, rather than just running normally. The application should initialize itself with settings set #n (if it exists). Applications that do not support multiple saved sets of settings may ignore the :n suffix. |
SAGERUN:n | This tells the application to run using settings set #n. If your program does not support multiple saved sets of settings, it may ignore this suffix. |
The application saves its SAGE settings in a subkey of the key in which it registers itself as a SAGE-aware program, as follows:
SetN | Key(s) containing a set of settings this program can be run with. The contents and format of this key is defined by each application. The allowable range for N is 0–32767. |
When System Agent is scheduling a new instance of a SAGE-aware program, it launches it using /SAGESET:n, with a value of n for which a SetN key does not already exist (for that program, at least). If/when the user confirms his or her choices in the settings dialog, the program should save those choices in the key named SetN. If the user cancels out of the settings dialog, the program simply exits without saving those settings. When SAGE runs the program, it passes it the /SAGERUN:n command line switch. If the program supports one or more sets of Save settings, it runs with setting set #n; otherwise it ignores the :n suffix and uses its default settings.
IMPORTANT System Agent may launch programs with both the /SAGERUN and /SAGESET command line switches simultaneously. If this happens, the program should display its SAGE Settings dialog—that is, the /SAGESET switch takes precedence over /SAGERUN.
Note also that the /SAGERUN:n command line switch implies that the program is being run by SAGE and thus, the program should behave accordingly and not stop for user input. All the settings required to run the program must be derived from what is stored in the registry—that is, when run with SAGERUN:n, the desired behavior of the program (what drive[s] to run on, and other preferences) must be fully specified by the settings stored in the SetN registry key, or the program must provide reasonable defaults so that it runs unattended, unless an unexpected condition arises that requires user input (regardless of whether or not :n is provided). Although the SAGERUN:n switch is intended to fully specify the behavior of a SAGE-aware program, the user is permitted to provide additional command line parameters to the program through the System Agent UI. If an application supports both a Settings dialog and command line parameters, it is up to the application to resolve any conflicts that may arise between the settings the user specifies in the Settings dialog and any command line switches she or he provides.
SAGE-aware applications should strive to return a meaningful exit code upon exiting from their WinMain function. In the initial release of Microsoft Plus!, these exit codes will be used by SAGE to display a result string in the System Agent user interface (UI) and write it to its log. The mapping between exit codes and result strings will be provided by each SAGE-aware application in its Result Codes registry key. In future versions of Plus!, SAGE may use the exit code to determine whether a program ran successfully or not, in order to determine whether other programs dependent on its outcome should be allowed to proceed or not. All exit codes indicating that the program completed successfully should be equal to or less than the value indicated by "Success", and numerically lower than those indicating failure conditions. For example, for ScanDisk, error code 0 might be defined to mean "No errors found"; error code 2 "Errors were found but all were fixed"; error code 10 might indicate that "Errors were found and only some were fixed"; and 255 might mean "Errors were found and none were fixed." In this example, a value for "Success" equal to 9 would be reasonable, but 1 would not.
The exit code 0xF9 is distinguished. When a SAGE-aware application is run with /SAGERUN and returns exit code F9 to SAGE, SAGE will try to re-run the application at a later time, possibly repeatedly unless/until its deadline (if any) passes. Essentially, this exit code says to SAGE, "I wasn't able to complete successfully, but try to run me again later." This option could be used, for example, by disk utilities that need to wait for a drive to become unlocked, or backup applets that discover that the destination server is down.
Error handling is handled by each SAGE-aware application. For errors that require user intervention, the application should stop and display a suitable dialog, rather than or in addition to simply exiting with an exit code. Otherwise the user will have no indication that an error occurred, unless she or he checks the SAGE log.
System Agent provides an option for SAGE to terminate a program if it's not finished by a designated time, or if the user returns to the system and starts using it. SAGE implements this by sending a WM_CLOSE message to the program's top-level window(s). SAGE-aware applications, when run with the SAGERUN:n command line switch, should exit gracefully and silently upon receiving this message. Exceptions to this might occur, however, if the application has unsaved data and must ask for confirmation from the user as to whether to save it, or if the application is in an error state (in which case it may wish to ignore the WM_CLOSE message, so that the user can be made aware of the error).
SAGE.DLL exports the following application programming interfaces (API)s, which applications can use.
int System_Agent_Detect(void)
If the System Agent thread exists, the System Agent's version number is returned; otherwise zero is returned.
Note This function is important because the existence of SAGE.DLL does not mean that the System Agent itself has been started. Note also that it is not necessary to call System_Agent_Initialize() before calling this function.
This function allows the client to completely enable and disable the System Agent. This feature should be used sparingly, because presumably the user has scheduled programs for a good reason. If the caller crashes while the System Agent is disabled, the System Agent will not be reenabled until the machine is restarted. Note that it is not necessary to call System_Agent_Initialize() before calling this function.
int System_Agent_Enable(int enablefunc)
enablefunc can have 1 of 3 values:
1 = ENABLE_AGENT | Enables System Agent scheduling |
2 = DISABLE_AGENT | Disables System Agent scheduling |
3 = GET_AGENT_STATUS | Returns the status of the System Agent without affecting the current state. Returns are ENABLE_AGENT or DISABLE_AGENT. |
16-bit applications can use the following code to suspend System Agent (for example, DisableSystemAgent() remembers the state of the System Agent) and then disable it:
#include <windows.h>
#include "string.h"
static int gSageStatus;
#define SAGE_ENABLE WM_USER + 6
#define SAGE_DISABLE WM_USER + 7
#define SAGE_GETSTATUS WM_USER + 8
#define ENABLE_AGENT 1
#define DISABLE_AGENT 2
#define GET_AGENT_STATUS 3
void DisableSystemAgent(void)
{
HWND h;
h = FindWindow("SAGEWINDOWCLASS","SYSTEM AGENT COM WINDOW");
if(!h)
return;
if(h)
{
gSageStatus = -1;
gSageStatus = (int) SendMessage(h,SAGE_GETSTATUS,0,0L);
if((gSageStatus != ENABLE_AGENT) &&
(gSageStatus != DISABLE_AGENT))
return; //something weird is going on
SendMessage(h,SAGE_DISABLE,0,0L);
}
}
RestoreSystemAgentState() restores the remembered state:
void RestoreSystemAgentState(void)
{
HWND h;
h = FindWindow("SAGEWINDOWCLASS","SYSTEM AGENT COM WINDOW");
if(!h)
return;
if(h)
{
switch(gSageStatus)
{
case DISABLE_AGENT:
SendMessage(h,SAGE_DISABLE,0,0L);
break;
case ENABLE_AGENT:
SendMessage(h,SAGE_ENABLE,0,0L);
break;
default:
break; //something weird happened
}
}
}
System Agent exports a rich API through SAGE.DLL that programs can use to directly query or modify the SAGE task data base.
#define MAXPATH 267
#define MAXSETTINGS 256
#define MAXCOMMANDLINE (MAXPATH+MAXSETTINGS)
#define MAXCOMMENT 256
#define CB_RESERVED 33
#pragma Pack(1)
typedef struct TaskInfo
{
unsigned long StructureSize;
unsigned long Task_Identifier;
unsigned long Sub_Task_Identifier;
unsigned long Status;
unsigned long Result;
unsigned long Time_Granularity;
unsigned long StopAfterTime;
unsigned long ReservedLong;
unsigned long User_Idle;
unsigned long Powered;
unsigned long CreatorId;
SYSTEMTIME BeginTime;
SYSTEMTIME EndTime;
SYSTEMTIME LastRunStart;
SYSTEMTIME LastRunEndScheduled;
SYSTEMTIME LastTerminationTime;
SYSTEMTIME ReservedTime1;
SYSTEMTIME LastAlarmTime;
SYSTEMTIME ReservedTime2;
SYSTEMTIME ReservedTime3;
STARTUPINFO StartupInfo;
DWORD dwProcessId;
DWORD dwThreadId;
DWORD LockingProcess;
unsigned long LockTime;
DWORD fdwCreate;
DWORD taskflags;
char SystemTask;
char TerminateAtRangeEnd;
char StartupTask;
char AlarmEnabled;
char RunNow;
char TerminateNoIdle;
char Disabled;
char TerminateNow;
char RestartNoIdle;
char CommandLine[MAXCOMMANDLINE];
char Comment[MAXCOMMENT];
char WorkingDirectory[MAXPATH];
char Reserved[CB_RESERVED];
}TaskInfo;
StructureSize
This is the size of the TaskInfo structure in bytes. This must be correct for API calls. Failure to fill in this field will lead to an ERROR_TASK_INVALID return code. Value is 0x558.
Task_Identifier
This is a unique value identifying this task. This value is passed into some of the APIs to identify the task.
Sub_Task_Identifier
This value is reserved for future use. Clients should leave this value zero.
Status
This field is set only by the System Agent (see System_Agent_Change_Task). It contains a value indicating status for the task. Currently defined values are:
#define STATUS_NOTRUNNING 0L // Task is not running.
#define STATUS_RUNNING 1L // Task is currently running.
#define STATUS_COMPLETE 2L // Task is not running, but has run in the
// currently scheduled period.
#define STATUS_QUEUEDEXEC 3L // Task is just about to execute.
Result
This is the result code returned from the last execution of the task.
Time_Granularity
If non-zero, this field indicates that the task should be executed periodically during its scheduled time at an interval of Time_Granularity seconds. If the task is still running when the next Time_Granularity seconds have elapsed, the task is not run; otherwise it is.
StopAfterTime
If non-zero, this field indicates that the task should be terminated after running for StopAfterTime seconds of execution.
ReservedLong
This field is reserved for future use and should be set to zero.
User_Idle
If non-zero, this task indicates that the task should not be executed unless User_Idle seconds have elapsed with no keyboard or mouse activity. Thus, if a task is scheduled to run between 4:00 and 5:00 with User_Idle set to 10 minutes (600 seconds), and the user last moved his mouse at 3:55, the task will execute at 4:05 unless the user interacts with the computer between those times, in which case the time countdown will be restarted at 10 minutes.
Powered
This field indicates that the task should not be run if the computer is currently being run on batteries. This works only on fully APM-compliant Plug and Play machines. Valid values are zero or one (one indicates the task should not run on batteries).
CreatorId
This field is reserved for use by the task creator. The task creator is the application that called System_Agent_Add_Task. This can be any value.
BeginTime
The StartTime and EndTime together define the basic schedule for a task. The StartTime field indicates the beginning of the task's execution period. This can be an exact time (in the case of a run-once task), or it can be a repeating schedule such as Daily, Monthly, Weekly, or Hourly. To specify a repeating schedule, set the units at which you wish to repeat to -1. All fields of higher precedence must also be set to -1. Precedence order is: wYear, wMonth, wDay, wHour, wMinute, wSeconds. The wMillisecond field is not honored when scheduling tasks.
Note wDayOfWeek does not have precedence and should be set to -1 unless you are specifically scheduling a task to run on a fixed day of the week.
Thus, to run daily at 4:30am, set:
To run every Wednesday at 1:00pm, set:
See EndTime for more details.
EndTime
This field specifies the ending time for the period in which the task is run. It has the same format as the StartTime, and should match its repeating schedule type. This field defines the latest time at which the task will be started if the task runs only when the machine is idle, and it defines the end of the period in which it will be started if it has a Time_Granularity set. Also, this field defines the time that task will be terminated if TerminateAtRangeEnd is set. If no EndTime definition is needed, all fields may be set to -1.
LastRunStart
This field indicates the time at which this task was last executed. This field should not be changed by the client.
LastRunEndScheduled
This field indicates the specific time the task's period is/was scheduled to end according to the schedule defined by StartTime and EndTime. This field does not indicate at what time the task actually terminated (see LastTerminationTime), nor will be terminated, but rather at what time the period for this task is scheduled to end.
LastTerminationTime
This field contains the specific time this task was last terminated.
ReservedTime1
This field is used internally by System Agent, and should not be used or modified by the client.
LastAlarmTime
This field indicates the specific time the last scheduled program notification was displayed for this task. This field should not be used by the client.
ReservedTime2
ReservedTime3
These fields are reserved for future use. They should remain zero and should not be used by the client.
StartupInfo
This field defines the STARTUPINFO structure that should be passed to CreateProcess when the task is started. See CreateProcess.
dwProcessId
This field defines the Process Id for the task, if it is running. See CreateProcess. This field should not be modified by the client.
dwThreadId
This field defines the Thread Id for the task, if it is running. See CreateProcess. This field should not be modified by the client.
LockingProcess
This field indicates the Process ID of the program who owns the lock on this task, if there is one. This field should not be modified by the client.
LockTime
This field records the GetTickCount return value when the task was locked. This field is used for data validation, and should not be used by the client.
fdwCreate
This field indicates the fdwCreate flags passed to CreateProcess. If zero, System Agent uses CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP | NORMAL_PRIORITY_CLASS. See CreateProcess.
taskflags
This field is a bitfield defining certain characteristics of the task:
bit 1 = Logging | 1 => log execution info to the System Agent log file. |
bit 3 = TaskHasRun | 1 => the task has run at least once before; 0 means it has never run (read only). |
bit 6 = NoIdleTerminated | 1 => task was terminated because of loss of idle. |
bit 7 = DontRun | 1 => don't run during this period after change task (see below). |
Other flags are used internally by System Agent and should not be used by the client.
SystemTask
0 means standard task. 1 means task is a system task, and will not show up in the System Agent window.
TerminateAtRangeEnd
If this field is set to 1, the task will be terminated at the EndTime if it is still running.
StartupTask
If this field is set to 1, the task will be run only when System Agent engine is started.
AlarmEnabled
If this field is set to 1, and a task does not run during its scheduled period for any reason, System Agent will put up a dialog box to tell the user. Notification will only occur if SAGE is running. Tasks can fail to run because the machine was off, the system was never idle, or the system was running on batteries during the scheduled time. If the task runs during a subsequent period and the dialog has not been not yet posted, the alarm is cancelled.
RunNow
If this field is set to 1, the task will execute immediately.
TerminateNoIdle
If this field is set to 1, the task will be terminated if it is running and the user uses the keyboard or mouse.
Disabled
If this field is set to 1, the task is disabled, and will not be scheduled to run.
TerminateNow
If this field is set to 1, the task will be terminated if it is currently running.
RestartNoIdle
If this field is set to 1, the task will restart after User_Idle seconds if it was terminated because TerminateNoIdle was set and the user use the mouse or keyboard.
CommandLine
This field defines the full command line for the task. The task may be a .COM, .BAT, .EXE, or associated file type. This is the command line that is passed to CreateProcess. It must be zero-terminated.
Comment
This is the description field shown in the System Agent window. If this field is all zeros, the System Agent will use the CommandLine instead.
WorkingDirectory
This is the working directory as defined by CreateProcess.
Reserved
This field is reserved for use by the System Agent and future API enhancements. It should not be used or modified.
SAGE.DLL exports the following functions:
int System_Agent_Inititialize(void)
This function should be called once to initialize memory structures. See example code below. Returns zero if successful.
int System_Agent_End(void)
This function frees up memory allocated during API processing. This function should be called once. Returns zero if successful.
int System_Agent_Get_Task_List(TaskInfo **TaskList, BOOL *changed)
This function gets a pointer to an array of tasks, and returns the number of tasks in the system agent's task list. TaskList is a pointer to a pointer to a task list—the memory itself is allocated by System Agent on behalf of the application. The changed parameter receives a Boolean value, with TRUE indicating that the task list has changed since the last time this function was called. A negative return value indicates an error code (see below).
int System_Agent_Add_Task(TaskInfo *Task,unsigned long *Task_Identifier)
Task is a TaskInfo structure that describes the task (see above). Unused fields should be initialized to zero (see example code).
You must at least fill in these fields:
And you may fill in these fields:
Other fields should be zero and not used by the caller.
See the definition for these fields above.
*TaskIdentifier is filled in with the unique ID for this new task. Returns zero if successful, an error code if not.
int System_Agent_Remove_Task(unsigned long Task_Identifier)
This function removes the specified task from the System Agent's task list. Returns zero if successful, otherwise an error code.
int System_Agent_Lock_Task(unsigned long Task_Identifier,DWORD dwProcessId,BOOL volatile)
int System_Agent_Unlock_Task(unsigned long Task_Identifier, DWORD dwProcessId,BOOL reenable)
This function unlocks a task that was locked with System_Agent_Lock_Task.
int System_Agent_Change_Task(TaskInfo *Task,unsigned long Task_Identifier)
This function allows the client to modify an existing task. However, the client must have an active lock (see System_Agent_Lock_Task) in order for this function to succeed. Also, some fields will not be modified unless the lock is volatile [status,result,dwProcessId,dwThreadId].
All system agent APIs may return one of the following error codes. Other codes may be defined in the future, so applications should not depend upon certain error codes being returned.
#define ERROR_CANNOT_ADD_TASK -12 // Unable to add a task, perhaps
// because out of memory.
#define ERROR_AGENT_BUSY -13 // System has timed out the API call.
#define ERROR_TASK_NOT_PRESENT -14 // Task_Identifer reference not in task
// list.
#define ERROR_AGENT_NOT_PRESENT -15 // System Agent is not active.
#define ERROR_CANNOT_LOAD_DLL -16 // Error loading the DLL.
#define ERROR_CANNOT_LOCK_TASK -17 // Task cannot be locked at this time.
#define ERROR_TASK_ALREADY_LOCKED -18 // Attempt to lock task failed; another
// task owns lock.
#define ERROR_CANNOT_UNLOCK_TASK -19 // Unable to unlock task, perhaps
// because not locked.
#define ERROR_TASK_LOCKED -20 // Task is locked by a different task,
// access denied.
#define ERROR_WRONG_VERSION -21 // Version of System Agent is not
// compatible.
#define ERROR_TASK_NOT_LOCKED -22 // Attempt to modify a task that is not
// locked.
#define ERROR_TASK_VOLATILE -23 // Cannot volatile lock because task is
// running.
#define ERROR_TASK_INVALID -24 // Data in task is invalid or corrupt.
#define ERROR_STALE_DATA -25 // Attempt to modify task with data got
// before lock.
The following is the basic pseudo-code sequence for scheduling and displaying SAGE tasks:
{
if(error = Connect_System_Agent()) // See example source for
// this function below.
goto exit1;
...
if(gTaskList = System_Agent_Get_Task_List())
{
error = CANNOT_GET_TASK_LIST;
goto exit2;
}
DisplayTaskStuff(gTaskList); // User-supplied function for
// displaying task list.
//...
memset(&gMyTask,0,sizeof(TaskInfo));
// Fill in info about task..SEE BELOW!
if(error = System_Agent_Add_Task(&gMyTask,&gMyTaskIdentifer))
// Add a user-defined task
goto exit2;
//...
exit2:
if(System_Agent_End())
error("Unable to terminate connection to System Agent");
exit1:
if(error)
printf("error = %d",error);
}
There are a few things to note about this code sequence. The user-supplied function Connect_System_Agent must call System_Agent_Initialize(). Example code for this is supplied below. If this API was called, System_Agent_End() must also be called to clean up global memory allocations.
The following is example code for initializing the System Agent API:
/***************** Routine to interface to System Agent ********************/
typedef long (*PFNDLL)();
PFNDLL gpfnSystem_Agent_Initialize = NULL;
PFNDLL gpfnSystem_Agent_End = NULL;
PFNDLL gpfnSystem_Agent_Get_Task_List = NULL;
PFNDLL gpfnSystem_Agent_Add_Task = NULL;
PFNDLL gpfnSystem_Agent_Remove_Task = NULL;
PFNDLL gpfnSystem_Agent_Lock_Task = NULL;
PFNDLL gpfnSystem_Agent_Unlock_Task = NULL;
PFNDLL gpfnSystem_Agent_Change_Task = NULL;
PFNDLL gpfnSystem_Agent_Detect = NULL;
PFNDLL gpfnSystem_Agent_Enable = NULL;
static HANDLE hLib = 0;
int Connect_System_Agent(void)
{
int retval;
if(!hLib)
{
if (!(hLib = LoadLibrary ("SAGE.DLL")))
{
return(ERROR_CANNOT_LOAD_DLL);
}
if(!(gpfnSystem_Agent_End =
(PFNDLL) GetProcAddress (hLib,"System_Agent_End")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Initialize =
(PFNDLL) GetProcAddress (hLib,"System_Agent_Initialize")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Add_Task =
(PFNDLL) GetProcAddress (hLib,"System_Agent_Add_Task")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Get_Task_List =
(PFNDLL) GetProcAddress (hLib,"System_Agent_Get_Task_List")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Change_Task =
(PFNDLL) GetProcAddress (hLib,"System_Agent_Change_Task")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Remove_Task =
(PFNDLL) GetProcAddress (hLib,"System_Agent_Remove_Task")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Enable =
(PFNDLL) GetProcAddress(hLib,"System_Agent_Enable")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Lock_Task =
(PFNDLL) GetProcAddress(hLib,"System_Agent_Lock_Task")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Unlock_Task =
(PFNDLL) GetProcAddress(hLib,"System_Agent_Unlock_Task")))
goto Unable_To_Load;
if(!(gpfnSystem_Agent_Detect =
(PFNDLL) GetProcAddress(hLib,"System_Agent_Detect")))
goto Unable_To_Load;
}
retval = (System_Agent_Detect() == 0); // Zero means agent is not present
if(!retval)
{
retval = (gpfnSystem_Agent_Initialize)();
// Agent is present, so initialize interface.
}
if(retval)
{
if(hLib)
{
FreeLibrary(hLib);
}
hLib = NULL;
}
return(retval);
Unable_To_Load:
if(hLib)
{
FreeLibrary(hLib);
}
hLib = NULL;
return(ERROR_CANNOT_LOAD_DLL);
}
int System_Agent_End(void)
{
int retval;
if(!hLib)
{
return(0);
}
retval = (gpfnSystem_Agent_End)();
if(hLib)
{
FreeLibrary(hLib);
}
hLib = NULL;
return(retval);
}
unsigned long System_Agent_Get_Task_List(TaskInfo **TL,BOOL *changed)
{
return((gpfnSystem_Agent_Get_Task_List)(TL,changed));
}
int System_Agent_Remove_Task(unsigned long Task_Identifier)
{
return((gpfnSystem_Agent_Remove_Task)(Task_Identifier));
}
int System_Agent_Add_Task(TaskInfo *task, unsigned long *Task_Identifier)
{
return((gpfnSystem_Agent_Add_Task)(task,Task_Identifier));
}
int System_Agent_Change_Task(TaskInfo *task, unsigned long Task_Identifier)
{
return((gpfnSystem_Agent_Change_Task)(task,Task_Identifier));
}
int System_Agent_Enable(BOOL eord)
{
return((gpfnSystem_Agent_Enable)(eord));
}
int System_Agent_Lock_Task(unsigned long Task_Identifier,DWORD dwProcessId,BOOL volatile)
{
return((gpfnSystem_Agent_Lock_Task)(Task_Identifier,dwProcessId,volatile));
}
int System_Agent_Unlock_Task(unsigned long Task_Identifier,DWORD dwProcessId,BOOL reenable)
{
return((gpfnSystem_Agent_Unlock_Task)(Task_Identifier,dwProcessId,reenable));
}
int System_Agent_Detect(void)
{
return((gpfnSystem_Agent_Detect)());
}
/*****************************************************************************/