您的位置:首页 > 理论基础 > 计算机网络

如何创建Windows网络计划任务

2010-03-01 01:19 363 查看
在我们的日常工作中,我们有时希望程序在指定的时间执行,以为成特定的任务。如对数据库的自动备份、磁盘文件的自动清理等。WINDOWS计划任务就是实现这个功能的一个好工具。

WINDOWS计划任务分为两种,一种是本地的计划任务(Task Schedule),该类任务我们可以在WINDOWS"控制面板"下的"计划任务"中利用"计划任务向导"进行创建。另一种就是网络的计划任务(Net Schedule),该类任务与在WINDOWS命令行下的"at"命令等价。通过我查阅资料了解到,两类计划任务可谓各有优缺点。本地计划任务在功能上更加强大,相比起网络计划任务,它提供了更多的接口让用户进行设置。不过创建该类计划任务时必须输入系统中存在的用户名和密码才行,如果不输入或者输入了错误的用户名和密码,计划任务虽然能够创建成功,但是却不能正确的执行。相比本地计划任务,网络计划任务则没有上述限制,只要你当前的用户属于管理员组,就可以成功的创建网络计划任务,并且该任务会在所设定的时间正确的执行。当然,无论对于哪种类的计划任务,在你创建计划任务的时候必须打开"TaskScheduler"服务才行,否则无论创建何种类的计划任务都是无法成功的。

既然了解了计划任务的基本概念,那下面就让我们来看看微软为我们提供了哪些接口来实现对计划任务进行编程。

1. 直接使用调用WINDOWS中的"at"命令或者是"schtasks"命令,该方法应该是最简单的。

2. WMI(本地计划任务),该方法也还算比较简单。

3. COM对象ITaskScheduler及其相关对象(本地计划任务)。

4. COM对象ITaskService及其相关对象(本地任务计划) PS:该对象只在WINDOWS VISTA 和 WINDOWS 7下有效。

5. 使用网络计划任务API(网络计划任务)

在这些解决方案中,我选择了最后一种解决方案,因为这是唯一的一种不需要输入用户名和密码的计划任务。虽然该类计划任务在功能上比较弱,如其只能直接支持按周和月的方式定义计划任务,但我们仍能通过其它变通的方法实现需要的功能。

下面让我们来看看利用API的方式创建计划任务会用到哪些函数和数据结构。

NET_API_STATUS NetScheduleJobAdd(LPCWSTR ServerName, LPBYTE Buffer, LPDWORD jobID)

函数描述:该函数创建将在系统未来时间中执行的计划任务。该函数需要目标计算机开启Task Scheduler服务。

安全要求:只有管理员组的用户才能在在远程计算机上执行该函数。

参数描述:Servername

[in] 指向远程主机名称的UNICODE字符串,该字符串必须以"//"(不包含引号)开头,如果该参数为NULL,则执行该函数的计算机为当前正在运行该程序的计算机。

参数描述:Buffer

[in] 指向AT_INFO结构体的指针,该指针用来描述任务的信息。

参数描述:JobId

[out] 新创建作业的ID号,该ID号仅仅在函数执行成功时有效 。

返回值 :如果函数执行成功,则返回的值为NERR_Success。

:如果函数执行失败,则返回相应的系统错误执行代码。

NET_API_STATUS NetScheduleJobGetInfo(LPCWSTR ServerName, DWORD JobID, LPBYTE *PointerToBuffer)

函数描述:获取指定计算机上指定任务的信息,该函数需要目标计算机开启Task Scheduler服务。

安全要求:只有管理员组的用户才能在在远程计算机上执行该函数。

参数描述:ServerName

[in] 同NetScheduleJobAdd。

参数描述:JobID

[in] 想要获取信息任务的ID号。

参数描述:PointerToBuffer

[out] 指向获取任务信息的AT_INFO结构体指针,请注意该缓冲区由系统分配,在你不需要该信息后,必须使用NetApiBufferFree函数释放。

返回值 :如果函数执行成功,则返回的值为NERR_Success。

:如果函数执行失败,则返回相应的系统错误执行代码。

下面让我们来看看与该函数相关的AT_INFO结构体,这是使用该函数的重点

AT_INFO

结构描述:该结构体包含作业的信息。当使用NetShceduleJobAdd函数创建一个新的任务时,该函数使用此结构体定义该任务的特性。当使用NetScheduleJobGetInfo函数获取任务信息时,使用该结构体接受信息。

成员描述:

DWORD_PTR JobTime

定义任务将在执行任务的那一天中的什么时刻执行。该时刻为执行该任务的计算机的本地时间。该时刻以午夜(00:00)为起点开始计算。该成员的单位为毫秒。如将该值设置为1000 * 60 * 60 * 17,则任务将在17:00执行。请注意任务只能精确到分钟执行。

DWORD DaysOfMonth

定义代表月中天数的32位值,如在指定的位上设置为1,该任务会在月中指定的一天上执行。第一位对应月的第一天。按照以上解释,本来我认为可以在多位上置1,以使任务可以在指定的日期上执行。但是实际情况却不行,任务只会在最低位上置1的时间执行,如你将2,3位置位,则任务只会在该月的第二天执行,该原因我一直没有找到,希望哪位牛人在看到本文后能告诉我这是为什么。DaysOfWeek则没有上述的问题。

UCHAR DaysOfWeek

定义代表周中天数的8位值,如在指定位上设置为1,该任务会在周中指定的一天上执行,第一位对应周中的第一天。

UCHAR Flags

该变量包含一系列的标志位以用来描述任务的属性。

当您调用NetScheduleJobAdd函数时,您可以为该变量设置下列几个标志位。

JOB_RUN_PERIODICALLY

如果该标志被置位,任务会周期性的执行。反之该任务只会在指定的时间执行一次。当在该任务所设置的所有时间执行完一次后,该任务会自动删除。

JOB_ADD_CURRENT_DATE

如果该标志被置位,则将当天加入到任务执行日期中。

JOB_NONINTERACTIVE

如果该标志被置位,该任务会在后台执行,反之则会在前台执行。如果任务执行的程序为GUI程序,如果将该标志位设置为1时,则程序的GUI界面不会出现。

当您调用NetScheduleJobGetInfo函数时,您可以为改变设置以下几个标志位。

JOB_RUN_PERIODICALLY

同上

JOB_EXEC_ERROR

获取任务在最新一次执行时是否成功。

JOB_RUNS_TODAY

任务是否在今天执行过。

JOB_NONINTERACTIVE

同上。

LPWSTR Command

指向可执行文件的路径的UNICODE字符串。

下面让我们来看一个具体的例子,如何给系统添加一项任务。程序用VB语言实现。

首先是新建一个模块,命名为mdlTaskScheduler,然后添加下列代码在其中

Declare Function NetScheduleJobAdd Lib "netapi32.dll" (ByVal servername As String, Buffer As Any, Jobid As Long) As Long
Private Declare Function GetComputerName Lib "kernel32" Alias "GetComputerNameA" (ByVal lpBuffer As String, nSize As Long) As Long
Type AT_INFO
JobTime     As Long
DaysOfMonth As Long
DaysOfWeek  As Byte
Flags       As Byte
dummy       As Integer
Command     As String
End Type
Public Const JOB_RUN_PERIODICALLY = &H1
Public Const JOB_NONINTERACTIVE = &H10
Public Const NERR_Success = 0
Public Function MachineName() As String
Dim sBuffer As String * 255
If GetComputerName(sBuffer, 255&) <> 0 Then
MachineName = Left$(sBuffer, InStr(sBuffer, vbNullChar) - 1)
End If
End Function


然后在主界面中加入以下代码

Private Sub AddTaskSchedule(Byval strTime As String, Byval iTaskDateType As Long, Byref iTaskDates() As Long, Byval bPeriod As Boolean, Byval bInteractive As Boolean, Byval strCommand As String)
Dim atiInfo As AT_INFO
If SetATINFO(atiInfo, strTime, iTaskDateType, iTaskDates, bPeriod, bInteractive, strCommand) = False   Then
Exit Sub
End If

Dim lResult As Long
Dim lJobID As Long
Dim i As Integer
Dim strComputerName As String
strComputerName = StrConv("//" & MachineName, vbUnicode)
lResult = NetScheduleJobAdd(strComputerName, atiInfo, lJobID)

If lResult = NERR_Success Then
MsgBox "任务 " & lJobID & " 添加成功"
End If
End Sub
Private Function SetATINFO(ByRef aiInfo As AT_INFO, Byval strTime As String, Byval iTaskDateType As Long, Byref iTaskDates() As Long, Byval bPeriod As Boolean, Byval bInteractive As Boolean, Byval strCommand As String) As Boolean
If strCommand  = "" Then
MsgBox "请输入任务路径"
SetATINFO = False
Exit Function
End If
Dim i As Integer
With aiInfo
.JobTime = (Hour(strTime) * 3600 + Minute(strTime) * 60) * 1000

If iTaskDateType = 1 Then ' 每天执行
For i = 0 To Ubound(iTaskDates) - 1
.DaysOfWeek = .DaysOfWeek + 2 ^ i
Next
.DaysOfMonth = 0
ElseIf opnType(1).Value Then ' 按周执行
For i = 1 To Ubound(iTaskDates)
.DaysOfWeek = .DaysOfWeek + 2 ^ (i - 1) * CInt(chkWeekType(i).Value)
Next
.DaysOfMonth = 0

If .DaysOfWeek = 0 Then
MsgBox "请选择星期"
SetATINFO = False
Exit Function
End If
Else ' 按月执行
.DaysOfWeek = 0
For i = 1 To Ubound(iTaskDates)
.DaysOfMonth = .DaysOfMonth + 2 ^ (i - 1) * CInt(chkMonthType(i).Value)
Next

If .DaysOfMonth = 0 Then
MsgBox "请选择日期"
SetATINFO = False
Exit Function
End If
End If

If bPeriod Then
.Flags = .Flags Or JOB_RUN_PERIODICALLY
End If
If bInteractive = False Then
.Flags = .Flags Or JOB_NONINTERACTIVE
End If

.Command = StrConv(strCommand, vbUnicode)
End With

SetATINFO = True
End Function


知道了如何添加任务后,让我们再来看看如何删除任务,删除任务需要调用下面的函数。

NET_API_STATUS NetScheduleJobDel(LPCWSTR ServerName, DWORD MinJobID, DWORD MaxJobID)
函数描述:删除运行在计算机上指定范围内的所有任务。该函数需要目标计算机开启Task Scheduler服务。

安全要求:只有管理员组的用户才能在在远程计算机上执行该函数。

参数描述:ServerName

[in] 同上。

参数描述:MinJobID

[in] 定义要被删除的最小任务ID号。

参数描述:MaxJobID

[in] 定义要被删除的最大任务ID号。

返回值 :如果函数执行成功,则返回的值为NERR_Success。

:如果函数执行失败,则返回相应的系统错误执行代码。

备注 :如果您想删除计算机上的所有任务,请将MinJobID的值设置为-1,将MaxJobID的值设置为0。如果您想删除指定的一项任务,请将MinJobID和MaxJobID设为相同的值。

下面让我们来看看删除任务的具体例子。

首先在模块mdlTaskScheduler中加入以下代码。

Declare Function NetScheduleJobDel Lib "netapi32.dll" (ByVal servername As String, ByVal MinJobId As Long, ByVal MaxJobId As Long) As Long


然后在主界面中加入以下代码。

Private Sub DeleteTaskSchedule(Byval lJobMinID As Long, lJobMaxID As Long)
Dim strComputerName As String
strComputerName = StrConv("//" & MachineName, vbUnicode)
Dim lResult As Long
lResult = NetScheduleJobDel(strComputerName, lJobMinID, lJobMaxID)
End Sub


最后在让我们来看看如何获取已经存在任务的各种信息,虽然获取信息可以用上面介绍的NetScheduleJobGetInfo函数获取,但是该函数一次只能获取一条任务信息,所以我在这里使用的是NetScheduleJobEnum来获取任务信息。下面让我们来研究一下该函数。

NET_API_STATUS NetScheduleJobEnum(LPCWSTR ServerName,LPBYTE *PointerToBuffer, LPBYTE *PointerToBuffer,

DWORD PreferredMaximumLength, LPDWORD EntriesRead, LPDWORD TotalEntries, LPDWORD ResumeHandle)

函数描述:获取指定计算机上的计划任务列表,该函数需要目标计算机开启Task Scheduler服务。

安全要求:只有管理员组的用户才能在在远程计算机上执行该函数。

参数描述:ServerName

[in] 同上。

参数描述:PointerToBuffer

[out] 指向获取任务信息缓冲区的指针。获取的信息以AT_ENUM结构数组的形势存放在该缓冲区中。该缓冲区中的内存由系统分配

,所以别忘了在不需要的时候使用NetApiBufferFree函数释放该缓冲区。请注意即使函数调用失败,但当返回的结果为ERROR_MORE_DATA时,也必须调用该函数清理缓冲区。

参数描述:PreferredMaximumLength

[in] 定义系统最大分配接收任务信息缓冲区的大小,单位为字节。一般的情况下请定义该值为MAX_PREFERRED_LENGTH,以保证系统会分配足够大的缓冲区。如果该值设置的太小,导致该缓冲区不能获取所有的任务信息,函数会返回ERROR_MORE_DATA。

参数描述:EnteriesRead

[out] 本次函数调用获取的任务信息实际数量

参数描述:TotalEntries

[out] 从当前位置开始将会获得的任务信息数量。

参数描述:ResumeHandle

[in/out] 从列表中的什么位置开始获取任务信息。在第一次调用函数的时候请将该值设置为0,。如果将该值设置为NULL,则不存储该值。

返回值 :如果函数执行成功,则返回的值为NERR_Success。

:如果函数执行失败,则返回相应的系统错误执行代码。

下面让我们来看一下AT_ENUM结构体,其实AT_ENUM结构体和AT_INFO结构体在获取任务信息时的各个变量以及变量中的标志位是相同的。

结构描述:获取任务信息

成员描述:

DWORD JobId

获取任务的ID号。

DWORD_PTR JobTime

同AT_INFO。

DWORD DaysOfMonth

同AT_INFO。

UCHAR DaysOfWeek

同AT_INFO。

UCHAR Flags

该变量包含一系列的标志位以用来描述任务的属性。

JOB_RUN_PERIODICALLY

同AT_INFO

JOB_EXEC_ERROR

同AT_INFO

JOB_RUNS_TODAY

同AT_INFO

JOB_NONINTERACTIVE

同AT_INFO

下面就让我们来看一个具体的获取任务信息的例子。首先在mdlTaskScheduler中加入以下代码。

Declare Function NetScheduleJobEnum Lib "netapi32.Dll " (ByVal servername As String, PointerToBuffer As Any, ByVal PreferredMaximumLength As Long, entriesread As Long, totalentries As Long, resumehandle As Long) As Long

Declare Function NetApiBufferFree Lib "netapi32.Dll " (ByVal Buffer As Long) As Long
Declare Sub RtlMoveMemory Lib "Kernel32.Dll " (Destination As Any, Source As Any, ByVal Length As Long)


然后在主界面中加入以下代码。

Private Sub GetTaskSchdule()
Dim strComputerName As String
strComputerName = StrConv("//" & MachineName, vbUnicode)

Dim lResult As Long
Do
Dim lBuffer As Long
Dim lRead   As Long
Dim lTotal  As Long
Dim lHandle As Long

lResult = NetScheduleJobEnum(strComputerName, lBuffer, MAX_PREFERRED_LENGTH, _
lRead, lTotal, lHandle)
If (lResult = NERR_Success) Or (lResult = ERROR_MORE_DATA) Then
Dim i As Long
Dim aeInfo As AT_ENUM
For i = 0 To lTotal - 1
RtlMoveMemory aeInfo, ByVal lBuffer + i * Len(aeInfo), Len(aeInfo)
' 处理获取的任务信息
Next
End If

If lBuffer <> 0 Then
NetApiBufferFree lBuffer
End If
Loop While lResult = ERROR_MORE_DATA
End Sub


关于使用API创建网络计划任务的内容就是这些了,这是我第一次写文章,文章中难免有一些疏漏之处。如果有什么不明白的话,

请在我的博客后面留言,如果需要该程序的完整源代码,请将邮件地址留下。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: