win32-taskscheduler 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +12 -0
- data/README +21 -15
- data/Rakefile +54 -0
- data/doc/taskscheduler.txt +419 -0
- data/examples/taskscheduler_example.rb +56 -0
- data/lib/win32/taskscheduler.rb +1677 -0
- data/test/{tc_taskscheduler.rb → test_taskscheduler.rb} +35 -5
- data/win32-taskscheduler.gemspec +36 -0
- metadata +19 -15
- data/ext/extconf.rb +0 -14
- data/ext/win32/taskscheduler.c +0 -2036
- data/ext/win32/taskscheduler.h +0 -103
@@ -0,0 +1,56 @@
|
|
1
|
+
#######################################################################
|
2
|
+
# taskscheduler_example.rb
|
3
|
+
#
|
4
|
+
# A test script for general futzing. You can run this example via the
|
5
|
+
# 'example' rake task.
|
6
|
+
#
|
7
|
+
# Modify as you see fit.
|
8
|
+
#######################################################################
|
9
|
+
require 'win32/taskscheduler'
|
10
|
+
require 'fileutils'
|
11
|
+
require 'pp'
|
12
|
+
include Win32
|
13
|
+
|
14
|
+
puts 'VERSION: ' + TaskScheduler::VERSION
|
15
|
+
|
16
|
+
ts = TaskScheduler.new
|
17
|
+
|
18
|
+
trigger = {
|
19
|
+
"start_year" => 2009,
|
20
|
+
"start_month" => 4,
|
21
|
+
"start_day" => 11,
|
22
|
+
"start_hour" => 7,
|
23
|
+
"start_minute" => 14,
|
24
|
+
"trigger_type" => TaskScheduler::DAILY,
|
25
|
+
"type" => { "days_interval" => 1 }
|
26
|
+
}
|
27
|
+
|
28
|
+
unless ts.enum.grep(/foo/).length > 0
|
29
|
+
ts.new_work_item("foo", trigger)
|
30
|
+
ts.application_name = "notepad.exe"
|
31
|
+
ts.save
|
32
|
+
puts "Task Added"
|
33
|
+
end
|
34
|
+
|
35
|
+
ts.activate("foo")
|
36
|
+
ts.priority = TaskScheduler::IDLE
|
37
|
+
ts.working_directory = "C:\\"
|
38
|
+
|
39
|
+
puts "App name: " + ts.application_name
|
40
|
+
puts "Creator: " + ts.creator
|
41
|
+
puts "Exit code: " + ts.exit_code.to_s
|
42
|
+
puts "Flags: " + ts.flags.to_s
|
43
|
+
puts "Max run time: " + ts.max_run_time.to_s
|
44
|
+
puts "Next run time: " + ts.next_run_time.to_s
|
45
|
+
puts "Parameters: " + ts.parameters
|
46
|
+
puts "Priority: " + ts.priority.to_s
|
47
|
+
puts "Status: " + ts.status
|
48
|
+
puts "Trigger count: " + ts.trigger_count.to_s
|
49
|
+
puts "Trigger string: " + ts.trigger_string(0)
|
50
|
+
puts "Working directory: " + ts.working_directory
|
51
|
+
puts "Trigger: "
|
52
|
+
|
53
|
+
pp ts.trigger(0)
|
54
|
+
|
55
|
+
ts.delete("foo")
|
56
|
+
puts "Task deleted"
|
@@ -0,0 +1,1677 @@
|
|
1
|
+
require 'windows/com'
|
2
|
+
require 'windows/unicode'
|
3
|
+
require 'windows/error'
|
4
|
+
require 'windows/process'
|
5
|
+
require 'windows/msvcrt/buffer'
|
6
|
+
include Windows::COM
|
7
|
+
include Windows::Unicode
|
8
|
+
include Windows::Process
|
9
|
+
include Windows::Error
|
10
|
+
include Windows::MSVCRT::Buffer
|
11
|
+
|
12
|
+
module Win32
|
13
|
+
class TaskScheduler
|
14
|
+
|
15
|
+
# The version of the win32-taskscheduler library
|
16
|
+
VERSION = '0.2.0'
|
17
|
+
|
18
|
+
# The error class raised if any task scheduler specific calls fail.
|
19
|
+
class Error < StandardError; end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
TASK_TIME_TRIGGER_ONCE = 0
|
24
|
+
TASK_TIME_TRIGGER_DAILY = 1
|
25
|
+
TASK_TIME_TRIGGER_WEEKLY = 2
|
26
|
+
TASK_TIME_TRIGGER_MONTHLYDATE = 3
|
27
|
+
TASK_TIME_TRIGGER_MONTHLYDOW = 4
|
28
|
+
TASK_EVENT_TRIGGER_ON_IDLE = 5
|
29
|
+
TASK_EVENT_TRIGGER_AT_SYSTEMSTART = 6
|
30
|
+
TASK_EVENT_TRIGGER_AT_LOGON = 7
|
31
|
+
|
32
|
+
TASK_SUNDAY = 0x1
|
33
|
+
TASK_MONDAY = 0x2
|
34
|
+
TASK_TUESDAY = 0x4
|
35
|
+
TASK_WEDNESDAY = 0x8
|
36
|
+
TASK_THURSDAY = 0x10
|
37
|
+
TASK_FRIDAY = 0x20
|
38
|
+
TASK_SATURDAY = 0x40
|
39
|
+
TASK_FIRST_WEEK = 1
|
40
|
+
TASK_SECOND_WEEK = 2
|
41
|
+
TASK_THIRD_WEEK = 3
|
42
|
+
TASK_FOURTH_WEEK = 4
|
43
|
+
TASK_LAST_WEEK = 5
|
44
|
+
TASK_JANUARY = 0x1
|
45
|
+
TASK_FEBRUARY = 0x2
|
46
|
+
TASK_MARCH = 0x4
|
47
|
+
TASK_APRIL = 0x8
|
48
|
+
TASK_MAY = 0x10
|
49
|
+
TASK_JUNE = 0x20
|
50
|
+
TASK_JULY = 0x40
|
51
|
+
TASK_AUGUST = 0x80
|
52
|
+
TASK_SEPTEMBER = 0x100
|
53
|
+
TASK_OCTOBER = 0x200
|
54
|
+
TASK_NOVEMBER = 0x400
|
55
|
+
TASK_DECEMBER = 0x800
|
56
|
+
|
57
|
+
TASK_FLAG_INTERACTIVE = 0x1
|
58
|
+
TASK_FLAG_DELETE_WHEN_DONE = 0x2
|
59
|
+
TASK_FLAG_DISABLED = 0x4
|
60
|
+
TASK_FLAG_START_ONLY_IF_IDLE = 0x10
|
61
|
+
TASK_FLAG_KILL_ON_IDLE_END = 0x20
|
62
|
+
TASK_FLAG_DONT_START_IF_ON_BATTERIES = 0x40
|
63
|
+
TASK_FLAG_KILL_IF_GOING_ON_BATTERIES = 0x80
|
64
|
+
TASK_FLAG_RUN_ONLY_IF_DOCKED = 0x100
|
65
|
+
TASK_FLAG_HIDDEN = 0x200
|
66
|
+
TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET = 0x400
|
67
|
+
TASK_FLAG_RESTART_ON_IDLE_RESUME = 0x800
|
68
|
+
TASK_FLAG_SYSTEM_REQUIRED = 0x1000
|
69
|
+
TASK_FLAG_RUN_ONLY_IF_LOGGED_ON = 0x2000
|
70
|
+
TASK_TRIGGER_FLAG_HAS_END_DATE = 0x1
|
71
|
+
TASK_TRIGGER_FLAG_KILL_AT_DURATION_END = 0x2
|
72
|
+
TASK_TRIGGER_FLAG_DISABLED = 0x4
|
73
|
+
|
74
|
+
TASK_MAX_RUN_TIMES = 1440
|
75
|
+
TASKS_TO_RETRIEVE = 5
|
76
|
+
|
77
|
+
CLSCTX_INPROC_SERVER = 0x1
|
78
|
+
CLSID_CTask = [0x148BD520,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
79
|
+
CLSID_CTaskScheduler = [0x148BD52A,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
80
|
+
IID_ITaskScheduler = [0x148BD527,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
81
|
+
IID_ITask = [0x148BD524,0xA2AB,0x11CE,0xB1,0x1F,0x00,0xAA,0x00,0x53,0x05,0x03].pack('LSSC8')
|
82
|
+
IID_IPersistFile = [0x0000010b,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46].pack('LSSC8')
|
83
|
+
|
84
|
+
public
|
85
|
+
|
86
|
+
# Shorthand constants
|
87
|
+
|
88
|
+
IDLE = IDLE_PRIORITY_CLASS
|
89
|
+
NORMAL = NORMAL_PRIORITY_CLASS
|
90
|
+
HIGH = HIGH_PRIORITY_CLASS
|
91
|
+
REALTIME = REALTIME_PRIORITY_CLASS
|
92
|
+
BELOW_NORMAL = BELOW_NORMAL_PRIORITY_CLASS
|
93
|
+
ABOVE_NORMAL = ABOVE_NORMAL_PRIORITY_CLASS
|
94
|
+
|
95
|
+
ONCE = TASK_TIME_TRIGGER_ONCE
|
96
|
+
DAILY = TASK_TIME_TRIGGER_DAILY
|
97
|
+
WEEKLY = TASK_TIME_TRIGGER_WEEKLY
|
98
|
+
MONTHLYDATE = TASK_TIME_TRIGGER_MONTHLYDATE
|
99
|
+
MONTHLYDOW = TASK_TIME_TRIGGER_MONTHLYDOW
|
100
|
+
|
101
|
+
ON_IDLE = TASK_EVENT_TRIGGER_ON_IDLE
|
102
|
+
AT_SYSTEMSTART = TASK_EVENT_TRIGGER_AT_SYSTEMSTART
|
103
|
+
AT_LOGON = TASK_EVENT_TRIGGER_AT_LOGON
|
104
|
+
FIRST_WEEK = TASK_FIRST_WEEK
|
105
|
+
SECOND_WEEK = TASK_SECOND_WEEK
|
106
|
+
THIRD_WEEK = TASK_THIRD_WEEK
|
107
|
+
FOURTH_WEEK = TASK_FOURTH_WEEK
|
108
|
+
LAST_WEEK = TASK_LAST_WEEK
|
109
|
+
SUNDAY = TASK_SUNDAY
|
110
|
+
MONDAY = TASK_MONDAY
|
111
|
+
TUESDAY = TASK_TUESDAY
|
112
|
+
WEDNESDAY = TASK_WEDNESDAY
|
113
|
+
THURSDAY = TASK_THURSDAY
|
114
|
+
FRIDAY = TASK_FRIDAY
|
115
|
+
SATURDAY = TASK_SATURDAY
|
116
|
+
JANUARY = TASK_JANUARY
|
117
|
+
FEBRUARY = TASK_FEBRUARY
|
118
|
+
MARCH = TASK_MARCH
|
119
|
+
APRIL = TASK_APRIL
|
120
|
+
MAY = TASK_MAY
|
121
|
+
JUNE = TASK_JUNE
|
122
|
+
JULY = TASK_JULY
|
123
|
+
AUGUST = TASK_AUGUST
|
124
|
+
SEPTEMBER = TASK_SEPTEMBER
|
125
|
+
OCTOBER = TASK_OCTOBER
|
126
|
+
NOVEMBER = TASK_NOVEMBER
|
127
|
+
DECEMBER = TASK_DECEMBER
|
128
|
+
|
129
|
+
INTERACTIVE = TASK_FLAG_INTERACTIVE
|
130
|
+
DELETE_WHEN_DONE = TASK_FLAG_DELETE_WHEN_DONE
|
131
|
+
DISABLED = TASK_FLAG_DISABLED
|
132
|
+
START_ONLY_IF_IDLE = TASK_FLAG_START_ONLY_IF_IDLE
|
133
|
+
KILL_ON_IDLE_END = TASK_FLAG_KILL_ON_IDLE_END
|
134
|
+
DONT_START_IF_ON_BATTERIES = TASK_FLAG_DONT_START_IF_ON_BATTERIES
|
135
|
+
KILL_IF_GOING_ON_BATTERIES = TASK_FLAG_KILL_IF_GOING_ON_BATTERIES
|
136
|
+
RUN_ONLY_IF_DOCKED = TASK_FLAG_RUN_ONLY_IF_DOCKED
|
137
|
+
HIDDEN = TASK_FLAG_HIDDEN
|
138
|
+
RUN_IF_CONNECTED_TO_INTERNET = TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET
|
139
|
+
RESTART_ON_IDLE_RESUME = TASK_FLAG_RESTART_ON_IDLE_RESUME
|
140
|
+
SYSTEM_REQUIRED = TASK_FLAG_SYSTEM_REQUIRED
|
141
|
+
RUN_ONLY_IF_LOGGED_ON = TASK_FLAG_RUN_ONLY_IF_LOGGED_ON
|
142
|
+
|
143
|
+
FLAG_HAS_END_DATE = TASK_TRIGGER_FLAG_HAS_END_DATE
|
144
|
+
FLAG_KILL_AT_DURATION_END = TASK_TRIGGER_FLAG_KILL_AT_DURATION_END
|
145
|
+
FLAG_DISABLED = TASK_TRIGGER_FLAG_DISABLED
|
146
|
+
|
147
|
+
MAX_RUN_TIMES = TASK_MAX_RUN_TIMES
|
148
|
+
|
149
|
+
# Returns a new TaskScheduler object. If a work_item (and possibly the
|
150
|
+
# the trigger) are passed as arguments then a new work item is created and
|
151
|
+
# associated with that trigger, although you can still activate other tasks
|
152
|
+
# with the same handle.
|
153
|
+
#
|
154
|
+
# This is really just a bit of convenience. Passing arguments to the
|
155
|
+
# constructor is the same as calling TaskScheduler.new plus
|
156
|
+
# TaskScheduler#new_work_item.
|
157
|
+
#
|
158
|
+
def initialize(work_item=nil, trigger=nil)
|
159
|
+
@pITS = nil
|
160
|
+
@pITask = nil
|
161
|
+
|
162
|
+
hr = CoInitialize(0)
|
163
|
+
|
164
|
+
if SUCCEEDED(hr)
|
165
|
+
ptr = 0.chr * 4
|
166
|
+
|
167
|
+
hr = CoCreateInstance(
|
168
|
+
CLSID_CTaskScheduler,
|
169
|
+
nil,
|
170
|
+
CLSCTX_INPROC_SERVER,
|
171
|
+
IID_ITaskScheduler,
|
172
|
+
ptr
|
173
|
+
)
|
174
|
+
|
175
|
+
if FAILED(hr)
|
176
|
+
raise Error, get_last_error
|
177
|
+
end
|
178
|
+
|
179
|
+
@pITS = ptr.unpack('L').first
|
180
|
+
else
|
181
|
+
raise Error, get_last_error
|
182
|
+
end
|
183
|
+
|
184
|
+
if work_item
|
185
|
+
if trigger
|
186
|
+
raise TypeError unless trigger.is_a?(Hash)
|
187
|
+
new_work_item(work_item, trigger)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
self
|
192
|
+
end
|
193
|
+
|
194
|
+
# Returns an array of scheduled task names.
|
195
|
+
#
|
196
|
+
def enum
|
197
|
+
raise Error, 'null pointer' if @pITS.nil?
|
198
|
+
|
199
|
+
lpVtbl = 0.chr * 4
|
200
|
+
table = 0.chr * 24
|
201
|
+
|
202
|
+
memcpy(lpVtbl, @pITS, 4)
|
203
|
+
memcpy(table, lpVtbl.unpack('L').first, 24)
|
204
|
+
table = table.unpack('L*')
|
205
|
+
|
206
|
+
enum = Win32::API::Function.new(table[5], 'PP', 'L')
|
207
|
+
|
208
|
+
ptr = 0.chr * 4
|
209
|
+
hr = enum.call(@pITS, ptr)
|
210
|
+
|
211
|
+
raise Error, get_last_error if hr != S_OK
|
212
|
+
|
213
|
+
pIEnum = ptr.unpack('L').first
|
214
|
+
lpVtbl = 0.chr * 4
|
215
|
+
table = 0.chr * 16
|
216
|
+
|
217
|
+
memcpy(lpVtbl, pIEnum, 4)
|
218
|
+
memcpy(table, lpVtbl.unpack('L').first, 16)
|
219
|
+
table = table.unpack('L*')
|
220
|
+
|
221
|
+
_next = Win32::API::Function.new(table[3], 'PLPP', 'L')
|
222
|
+
release = Win32::API::Function.new(table[2], 'P', 'L')
|
223
|
+
|
224
|
+
array = []
|
225
|
+
fetched_tasks = 0.chr * 4
|
226
|
+
pnames = 0.chr * 4
|
227
|
+
|
228
|
+
while (_next.call(pIEnum, TASKS_TO_RETRIEVE, pnames, fetched_tasks) >= S_OK) &&
|
229
|
+
(fetched_tasks.unpack('L').first != 0)
|
230
|
+
|
231
|
+
tasks = fetched_tasks.unpack('L').first
|
232
|
+
names = 0.chr * 4 * tasks
|
233
|
+
memcpy(names, pnames.unpack('L').first, 4 * tasks)
|
234
|
+
|
235
|
+
for i in 0 ... tasks
|
236
|
+
str = 0.chr * 256
|
237
|
+
wcscpy(str, names[i*4, 4].unpack('L').first)
|
238
|
+
array.push(wide_to_multi(str))
|
239
|
+
CoTaskMemFree(names[i*4, 4].unpack('L').first)
|
240
|
+
end
|
241
|
+
|
242
|
+
CoTaskMemFree(pnames.unpack('L').first)
|
243
|
+
end
|
244
|
+
|
245
|
+
release.call(pIEnum)
|
246
|
+
|
247
|
+
array
|
248
|
+
end
|
249
|
+
|
250
|
+
alias :tasks :enum
|
251
|
+
|
252
|
+
# Activate the specified task.
|
253
|
+
#
|
254
|
+
def activate(task)
|
255
|
+
raise Error, 'null pointer' if @pITS.nil?
|
256
|
+
raise TypeError unless task.is_a?(String)
|
257
|
+
|
258
|
+
task = multi_to_wide(task)
|
259
|
+
|
260
|
+
lpVtbl = 0.chr * 4
|
261
|
+
table = 0.chr * 28
|
262
|
+
|
263
|
+
memcpy(lpVtbl, @pITS, 4)
|
264
|
+
memcpy(table, lpVtbl.unpack('L').first, 28)
|
265
|
+
table = table.unpack('L*')
|
266
|
+
|
267
|
+
activate = Win32::API::Function.new(table[6], 'PPPP', 'L')
|
268
|
+
|
269
|
+
ptr = 0.chr * 4
|
270
|
+
hr = activate.call(@pITS, task, IID_ITask, ptr)
|
271
|
+
|
272
|
+
if hr != S_OK
|
273
|
+
raise Error, get_last_error
|
274
|
+
end
|
275
|
+
|
276
|
+
@pITask = ptr.unpack('L').first
|
277
|
+
|
278
|
+
self
|
279
|
+
end
|
280
|
+
|
281
|
+
# Delete the specified task name.
|
282
|
+
#
|
283
|
+
def delete(task)
|
284
|
+
raise Error, 'null pointer' if @pITS.nil?
|
285
|
+
raise TypeError unless task.is_a?(String)
|
286
|
+
|
287
|
+
task = multi_to_wide(task)
|
288
|
+
|
289
|
+
lpVtbl = 0.chr * 4
|
290
|
+
table = 0.chr * 32
|
291
|
+
|
292
|
+
memcpy(lpVtbl, @pITS, 4)
|
293
|
+
memcpy(table, lpVtbl.unpack('L').first, 32)
|
294
|
+
table = table.unpack('L*')
|
295
|
+
|
296
|
+
delete = Win32::API::Function.new(table[7], 'PP', 'L')
|
297
|
+
|
298
|
+
hr = delete.call(@pITS,task)
|
299
|
+
|
300
|
+
if hr != S_OK
|
301
|
+
raise Error, get_last_error
|
302
|
+
end
|
303
|
+
|
304
|
+
self
|
305
|
+
end
|
306
|
+
|
307
|
+
# Execute the current task.
|
308
|
+
#
|
309
|
+
def run
|
310
|
+
raise Error, 'null pointer' if @pITask.nil?
|
311
|
+
|
312
|
+
lpVtbl = 0.chr * 4
|
313
|
+
table = 0.chr * 52
|
314
|
+
|
315
|
+
memcpy(lpVtbl, @pITask, 4)
|
316
|
+
memcpy(table, lpVtbl.unpack('L').first, 52)
|
317
|
+
table = table.unpack('L*')
|
318
|
+
|
319
|
+
run = Win32::API::Function.new(table[12], 'P', 'L')
|
320
|
+
|
321
|
+
hr = run.call(@pITask)
|
322
|
+
|
323
|
+
if hr != S_OK
|
324
|
+
raise Error,get_last_error
|
325
|
+
end
|
326
|
+
|
327
|
+
self
|
328
|
+
end
|
329
|
+
|
330
|
+
# Saves the current task. Tasks must be saved before they can be activated.
|
331
|
+
# The .job file itself is typically stored in the C:\WINDOWS\Tasks folder.
|
332
|
+
#
|
333
|
+
# If +file+ (an absolute path) is specified then the job is saved to that
|
334
|
+
# file instead. A '.job' extension is recommended but not enforced.
|
335
|
+
#
|
336
|
+
# Note that calling TaskScheduler#save also resets the TaskScheduler object
|
337
|
+
# so that there is no currently active task.
|
338
|
+
#
|
339
|
+
def save(file = nil)
|
340
|
+
raise Error, 'null pointer' if @pITask.nil?
|
341
|
+
file = multi_to_wide(file) if file
|
342
|
+
|
343
|
+
lpVtbl = 0.chr * 4
|
344
|
+
table = 0.chr * 12
|
345
|
+
|
346
|
+
memcpy(lpVtbl, @pITask, 4)
|
347
|
+
memcpy(table, lpVtbl.unpack('L').first, 12)
|
348
|
+
table = table.unpack('L*')
|
349
|
+
|
350
|
+
queryinterface = Win32::API::Function.new(table[0],'PPP','L')
|
351
|
+
release = Win32::API::Function.new(table[2],'P','L')
|
352
|
+
|
353
|
+
ptr = 0.chr * 4
|
354
|
+
|
355
|
+
hr = queryinterface.call(@pITask, IID_IPersistFile, ptr)
|
356
|
+
|
357
|
+
if hr != S_OK
|
358
|
+
raise Error, get_last_error
|
359
|
+
end
|
360
|
+
|
361
|
+
pIPersistFile = ptr.unpack('L').first
|
362
|
+
|
363
|
+
lpVtbl = 0.chr * 4
|
364
|
+
table = 0.chr * 28
|
365
|
+
|
366
|
+
memcpy(lpVtbl, pIPersistFile,4)
|
367
|
+
memcpy(table, lpVtbl.unpack('L').first, 28)
|
368
|
+
table = table.unpack('L*')
|
369
|
+
|
370
|
+
save = Win32::API::Function.new(table[6],'PPL','L')
|
371
|
+
release = Win32::API::Function.new(table[2],'P','L')
|
372
|
+
|
373
|
+
hr = save.call(pIPersistFile,file,1)
|
374
|
+
|
375
|
+
if hr != S_OK
|
376
|
+
raise Error,get_last_error
|
377
|
+
end
|
378
|
+
|
379
|
+
release.call(pIPersistFile)
|
380
|
+
|
381
|
+
CoUninitialize()
|
382
|
+
hr = CoInitialize(nil)
|
383
|
+
|
384
|
+
if hr >= 0
|
385
|
+
ptr = 0.chr * 4
|
386
|
+
|
387
|
+
hr = CoCreateInstance(
|
388
|
+
CLSID_CTaskScheduler,
|
389
|
+
nil,
|
390
|
+
CLSCTX_INPROC_SERVER,
|
391
|
+
IID_ITaskScheduler,
|
392
|
+
ptr
|
393
|
+
)
|
394
|
+
|
395
|
+
if hr != S_OK
|
396
|
+
CoUninitialize()
|
397
|
+
raise Error, get_last_error
|
398
|
+
end
|
399
|
+
|
400
|
+
@pITS = ptr.unpack('L').first
|
401
|
+
else
|
402
|
+
raise Error,get_last_error
|
403
|
+
end
|
404
|
+
|
405
|
+
release.call(@pITask)
|
406
|
+
@pITask = nil
|
407
|
+
|
408
|
+
self
|
409
|
+
end
|
410
|
+
|
411
|
+
# Terminate the current task.
|
412
|
+
#
|
413
|
+
def terminate
|
414
|
+
raise Error, 'null pointer' if @pITask.nil?
|
415
|
+
|
416
|
+
lpVtbl = 0.chr * 4
|
417
|
+
table = 0.chr * 56
|
418
|
+
|
419
|
+
memcpy(lpVtbl,@pITask,4)
|
420
|
+
memcpy(table,lpVtbl.unpack('L').first,56)
|
421
|
+
table = table.unpack('L*')
|
422
|
+
|
423
|
+
teriminate = Win32::API::Function.new(table[13],'P','L')
|
424
|
+
hr = teriminate.call(@pITask)
|
425
|
+
|
426
|
+
if hr != S_OK
|
427
|
+
raise Error,get_last_error
|
428
|
+
end
|
429
|
+
|
430
|
+
self
|
431
|
+
end
|
432
|
+
|
433
|
+
# Set the host on which the various TaskScheduler methods will execute.
|
434
|
+
#
|
435
|
+
def machine=(host)
|
436
|
+
raise Error, 'null pointer' if @pITS.nil?
|
437
|
+
raise TypeError unless host.is_a?(String)
|
438
|
+
|
439
|
+
host_w = multi_to_wide(host)
|
440
|
+
|
441
|
+
lpVtbl = 0.chr * 4
|
442
|
+
table = 0.chr * 16
|
443
|
+
|
444
|
+
memcpy(lpVtbl, @pITS, 4)
|
445
|
+
memcpy(table, lpVtbl.unpack('L').first, 16)
|
446
|
+
table = table.unpack('L*')
|
447
|
+
|
448
|
+
setTargetComputer = Win32::API::Function.new(table[3], 'PP', 'L')
|
449
|
+
|
450
|
+
hr = setTargetComputer.call(@pITS, host_w)
|
451
|
+
|
452
|
+
if hr != S_OK
|
453
|
+
raise Error, get_last_error
|
454
|
+
end
|
455
|
+
|
456
|
+
host
|
457
|
+
end
|
458
|
+
|
459
|
+
alias :host= :machine=
|
460
|
+
|
461
|
+
# Sets the +user+ and +password+ for the given task. If the user and
|
462
|
+
# password are set properly then true is returned.
|
463
|
+
#
|
464
|
+
# In some cases the job may be created, but the account information was
|
465
|
+
# bad. In this case the task is created but a warning is generated and
|
466
|
+
# false is returned.
|
467
|
+
#
|
468
|
+
def set_account_information(user, password)
|
469
|
+
raise Error, 'null pointer' if @pITS.nil?
|
470
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
471
|
+
|
472
|
+
lpVtbl = 0.chr * 4
|
473
|
+
table = 0.chr * 124
|
474
|
+
|
475
|
+
memcpy(lpVtbl, @pITask, 4)
|
476
|
+
memcpy(table, lpVtbl.unpack('L').first, 124)
|
477
|
+
table = table.unpack('L*')
|
478
|
+
|
479
|
+
setAccountInformation = Win32::API::Function.new(table[30],'PPP','L')
|
480
|
+
|
481
|
+
if (user.nil? || user=="") && (password.nil? || password=="")
|
482
|
+
hr = setAccountInformation.call(@pITask, "", nil)
|
483
|
+
else
|
484
|
+
user = multi_to_wide(user)
|
485
|
+
password = multi_to_wide(password)
|
486
|
+
hr = setAccountInformation.call(@pITask, user, password)
|
487
|
+
end
|
488
|
+
|
489
|
+
bool = true
|
490
|
+
|
491
|
+
case hr
|
492
|
+
when S_OK
|
493
|
+
return true
|
494
|
+
when 0x80070005 # E_ACCESSDENIED
|
495
|
+
raise Error, 'access denied'
|
496
|
+
when 0x80070057 # E_INVALIDARG
|
497
|
+
raise Error, 'invalid argument'
|
498
|
+
when 0x8007000E # E_OUTOFMEMORY
|
499
|
+
raise Error, 'out of memory'
|
500
|
+
when 0x80041312 # SCHED_E_NO_SECURITY_SERVICES
|
501
|
+
raise Error, 'no security services on this platform'
|
502
|
+
when 0x80041314 # SCHED_E_UNSUPPORTED_ACCOUNT_OPTION
|
503
|
+
raise Error, 'unsupported account option'
|
504
|
+
when 0x8004130F # SCHED_E_ACCOUNT_INFORMATION_NOT_SET
|
505
|
+
warn 'job created, but password was invalid'
|
506
|
+
bool = false
|
507
|
+
else
|
508
|
+
raise Error, 'unknown error'
|
509
|
+
end
|
510
|
+
bool
|
511
|
+
end
|
512
|
+
|
513
|
+
# Returns the user associated with the task or nil if no user has yet
|
514
|
+
# been associated with the task.
|
515
|
+
#
|
516
|
+
def account_information
|
517
|
+
raise Error, 'null pointer' if @pITS.nil?
|
518
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
519
|
+
|
520
|
+
lpVtbl = 0.chr * 4
|
521
|
+
table = 0.chr * 128
|
522
|
+
|
523
|
+
memcpy(lpVtbl, @pITask, 4)
|
524
|
+
memcpy(table,lpVtbl.unpack('L').first, 128)
|
525
|
+
table = table.unpack('L*')
|
526
|
+
|
527
|
+
getAccountInformation = Win32::API::Function.new(table[31], 'PP', 'L')
|
528
|
+
|
529
|
+
ptr = 0.chr * 4
|
530
|
+
hr = getAccountInformation.call(@pITask, ptr)
|
531
|
+
|
532
|
+
if hr == 0x8004130F # SCHED_E_ACCOUNT_INFORMATION_NOT_SET
|
533
|
+
user = nil
|
534
|
+
elsif hr >= 0 && hr != 0x80041312 # SCHED_E_NO_SECURITY_SERVICES
|
535
|
+
str = 0.chr * 256
|
536
|
+
wcscpy(str, ptr.unpack('L').first)
|
537
|
+
CoTaskMemFree(ptr.unpack('L').first)
|
538
|
+
user = wide_to_multi(str)
|
539
|
+
else
|
540
|
+
CoTaskMemFree(p.unpack('L').first)
|
541
|
+
raise Error,get_last_error(hr)
|
542
|
+
end
|
543
|
+
|
544
|
+
user
|
545
|
+
end
|
546
|
+
|
547
|
+
# Returns the name of the application associated with the task.
|
548
|
+
#
|
549
|
+
def application_name
|
550
|
+
raise Error, 'null pointer' if @pITS.nil?
|
551
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
552
|
+
|
553
|
+
lpVtbl = 0.chr * 4
|
554
|
+
table = 0.chr * 136
|
555
|
+
|
556
|
+
memcpy(lpVtbl, @pITask,4)
|
557
|
+
memcpy(table, lpVtbl.unpack('L').first, 136)
|
558
|
+
table = table.unpack('L*')
|
559
|
+
|
560
|
+
getApplicationName = Win32::API::Function.new(table[33],'PP','L')
|
561
|
+
|
562
|
+
ptr = 0.chr * 4
|
563
|
+
hr = getApplicationName.call(@pITask, ptr)
|
564
|
+
|
565
|
+
if hr >= S_OK
|
566
|
+
str = 0.chr * 256
|
567
|
+
wcscpy(str, ptr.unpack('L').first)
|
568
|
+
app = wide_to_multi(str)
|
569
|
+
CoTaskMemFree(ptr.unpack('L').first)
|
570
|
+
else
|
571
|
+
raise Error, get_last_error
|
572
|
+
end
|
573
|
+
|
574
|
+
app
|
575
|
+
end
|
576
|
+
|
577
|
+
# Sets the application name associated with the task.
|
578
|
+
#
|
579
|
+
def application_name=(app)
|
580
|
+
raise Error, 'null pointer' if @pITS.nil?
|
581
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
582
|
+
raise TypeError unless app.is_a?(String)
|
583
|
+
|
584
|
+
app_w = multi_to_wide(app)
|
585
|
+
|
586
|
+
lpVtbl = 0.chr * 4
|
587
|
+
table = 0.chr * 132
|
588
|
+
memcpy(lpVtbl,@pITask,4)
|
589
|
+
memcpy(table,lpVtbl.unpack('L').first,132)
|
590
|
+
table = table.unpack('L*')
|
591
|
+
setApplicationName = Win32::API::Function.new(table[32],'PP','L')
|
592
|
+
|
593
|
+
hr = setApplicationName.call(@pITask,app_w)
|
594
|
+
|
595
|
+
if hr != S_OK
|
596
|
+
raise Error,get_last_error(hr)
|
597
|
+
end
|
598
|
+
app
|
599
|
+
end
|
600
|
+
|
601
|
+
# Returns the command line parameters for the task.
|
602
|
+
#
|
603
|
+
def parameters
|
604
|
+
raise Error, 'null pointer' if @pITS.nil?
|
605
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
606
|
+
|
607
|
+
lpVtbl = 0.chr * 4
|
608
|
+
table = 0.chr * 144
|
609
|
+
|
610
|
+
memcpy(lpVtbl, @pITask, 4)
|
611
|
+
memcpy(table, lpVtbl.unpack('L').first, 144)
|
612
|
+
table = table.unpack('L*')
|
613
|
+
|
614
|
+
getParameters = Win32::API::Function.new(table[35], 'PP', 'L')
|
615
|
+
ptr = 0.chr * 4
|
616
|
+
hr = getParameters.call(@pITask, ptr)
|
617
|
+
|
618
|
+
if hr >= S_OK
|
619
|
+
str = 0.chr * 256
|
620
|
+
wcscpy(str, ptr.unpack('L').first)
|
621
|
+
param = wide_to_multi(str)
|
622
|
+
CoTaskMemFree(ptr.unpack('L').first)
|
623
|
+
else
|
624
|
+
raise Error, get_last_error
|
625
|
+
end
|
626
|
+
|
627
|
+
param
|
628
|
+
end
|
629
|
+
|
630
|
+
# Sets the parameters for the task. These parameters are passed as command
|
631
|
+
# line arguments to the application the task will run. To clear the command
|
632
|
+
# line parameters set it to an empty string.
|
633
|
+
#
|
634
|
+
def parameters=(param)
|
635
|
+
raise Error, 'null pointer(ts_set_parameters)' if @pITS.nil?
|
636
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
637
|
+
raise TypeError unless param.is_a?(String)
|
638
|
+
|
639
|
+
param_w = multi_to_wide(param)
|
640
|
+
|
641
|
+
lpVtbl = 0.chr * 4
|
642
|
+
table = 0.chr * 140
|
643
|
+
|
644
|
+
memcpy(lpVtbl,@pITask,4)
|
645
|
+
memcpy(table,lpVtbl.unpack('L').first,140)
|
646
|
+
table = table.unpack('L*')
|
647
|
+
|
648
|
+
setParameters = Win32::API::Function.new(table[34],'PP','L')
|
649
|
+
hr = setParameters.call(@pITask,param_w)
|
650
|
+
|
651
|
+
if hr != S_OK
|
652
|
+
raise Error, get_last_error(hr)
|
653
|
+
end
|
654
|
+
param
|
655
|
+
end
|
656
|
+
|
657
|
+
# Returns the working directory for the task.
|
658
|
+
#
|
659
|
+
def working_directory
|
660
|
+
raise Error,"fatal error: null pointer(ts_get_parameters)" if @pITS.nil?
|
661
|
+
raise Error,"No currently active task" if @pITask.nil?
|
662
|
+
|
663
|
+
lpVtbl = 0.chr * 4
|
664
|
+
table = 0.chr * 152
|
665
|
+
|
666
|
+
memcpy(lpVtbl, @pITask,4)
|
667
|
+
memcpy(table, lpVtbl.unpack('L').first,152)
|
668
|
+
table = table.unpack('L*')
|
669
|
+
|
670
|
+
getWorkingDirectory = Win32::API::Function.new(table[37],'PP','L')
|
671
|
+
|
672
|
+
ptr = 0.chr * 4
|
673
|
+
hr = getWorkingDirectory.call(@pITask, ptr)
|
674
|
+
|
675
|
+
if hr >= S_OK
|
676
|
+
str = 0.chr * 256
|
677
|
+
wcscpy(str, ptr.unpack('L').first)
|
678
|
+
dir = wide_to_multi(str)
|
679
|
+
CoTaskMemFree(ptr.unpack('L').first)
|
680
|
+
else
|
681
|
+
raise Error, get_last_error
|
682
|
+
end
|
683
|
+
|
684
|
+
dir
|
685
|
+
end
|
686
|
+
|
687
|
+
# Sets the working directory for the task.
|
688
|
+
#
|
689
|
+
def working_directory=(dir)
|
690
|
+
raise Error, 'null pointer' if @pITS.nil?
|
691
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
692
|
+
raise TypeError unless dir.is_a?(String)
|
693
|
+
|
694
|
+
dir_w = multi_to_wide(dir)
|
695
|
+
|
696
|
+
lpVtbl = 0.chr * 4
|
697
|
+
table = 0.chr * 148
|
698
|
+
|
699
|
+
memcpy(lpVtbl, @pITask,4)
|
700
|
+
memcpy(table, lpVtbl.unpack('L').first, 148)
|
701
|
+
table = table.unpack('L*')
|
702
|
+
|
703
|
+
setWorkingDirectory = Win32::API::Function.new(table[36], 'PP', 'L')
|
704
|
+
hr = setWorkingDirectory.call(@pITask, dir_w)
|
705
|
+
|
706
|
+
if hr != S_OK
|
707
|
+
raise Error, get_last_error(hr)
|
708
|
+
end
|
709
|
+
|
710
|
+
dir
|
711
|
+
end
|
712
|
+
|
713
|
+
# Returns the task's priority level. Possible values are 'idle',
|
714
|
+
# 'normal', 'high', 'realtime', 'below_normal', 'above_normal',
|
715
|
+
# and 'unknown'.
|
716
|
+
#
|
717
|
+
def priority
|
718
|
+
raise Error, 'null pointer(ts_get_priority)' if @pITS.nil?
|
719
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
720
|
+
|
721
|
+
lpVtbl = 0.chr * 4
|
722
|
+
table = 0.chr * 160
|
723
|
+
|
724
|
+
memcpy(lpVtbl, @pITask, 4)
|
725
|
+
memcpy(table, lpVtbl.unpack('L').first, 160)
|
726
|
+
table = table.unpack('L*')
|
727
|
+
|
728
|
+
getPriority = Win32::API::Function.new(table[39], 'PP', 'L')
|
729
|
+
|
730
|
+
ptr = 0.chr * 4
|
731
|
+
hr = getPriority.call(@pITask, ptr)
|
732
|
+
|
733
|
+
if hr >= S_OK
|
734
|
+
pri = ptr.unpack('L').first
|
735
|
+
if (pri & IDLE_PRIORITY_CLASS) != 0
|
736
|
+
priority = 'idle'
|
737
|
+
elsif (pri & NORMAL_PRIORITY_CLASS) != 0
|
738
|
+
priority = 'normal'
|
739
|
+
elsif (pri & HIGH_PRIORITY_CLASS) != 0
|
740
|
+
priority = 'high'
|
741
|
+
elsif (pri & REALTIME_PRIORITY_CLASS) != 0
|
742
|
+
priority = 'realtime'
|
743
|
+
elsif (pri & BELOW_NORMAL_PRIORITY_CLASS) != 0
|
744
|
+
priority = 'below_normal'
|
745
|
+
elsif (pri & ABOVE_NORMAL_PRIORITY_CLASS) != 0
|
746
|
+
priority = 'above_normal'
|
747
|
+
else
|
748
|
+
priority = 'unknown'
|
749
|
+
end
|
750
|
+
else
|
751
|
+
raise Error, get_last_error
|
752
|
+
end
|
753
|
+
|
754
|
+
priority
|
755
|
+
end
|
756
|
+
|
757
|
+
# Sets the priority of the task. The +priority+ should be a numeric
|
758
|
+
# priority constant value.
|
759
|
+
#
|
760
|
+
def priority=(priority)
|
761
|
+
raise Error, 'null pointer' if @pITS.nil?
|
762
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
763
|
+
raise TypeError unless priority.is_a?(Numeric)
|
764
|
+
|
765
|
+
lpVtbl = 0.chr * 4
|
766
|
+
table = 0.chr * 156
|
767
|
+
|
768
|
+
memcpy(lpVtbl, @pITask, 4)
|
769
|
+
memcpy(table, lpVtbl.unpack('L').first, 156)
|
770
|
+
table = table.unpack('L*')
|
771
|
+
|
772
|
+
setPriority = Win32::API::Function.new(table[38], 'PL', 'L')
|
773
|
+
hr = setPriority.call(@pITask, priority)
|
774
|
+
|
775
|
+
if hr != S_OK
|
776
|
+
raise Error, get_last_error(hr)
|
777
|
+
end
|
778
|
+
|
779
|
+
priority
|
780
|
+
end
|
781
|
+
|
782
|
+
# Creates a new work item (scheduled job) with the given +trigger+. The
|
783
|
+
# trigger variable is a hash of options that define when the scheduled
|
784
|
+
# job should run.
|
785
|
+
#
|
786
|
+
def new_work_item(task, trigger)
|
787
|
+
raise TypeError unless trigger.is_a?(Hash)
|
788
|
+
raise Error, 'null pointer' if @pITS.nil?
|
789
|
+
|
790
|
+
trigger = transform_and_validate(trigger)
|
791
|
+
|
792
|
+
if @pITask
|
793
|
+
lpVtbl = 0.chr * 4
|
794
|
+
table = 0.chr * 12
|
795
|
+
|
796
|
+
memcpy(lpVtbl, @pITask, 4)
|
797
|
+
memcpy(table, lpVtbl.unpack('L').first, 12)
|
798
|
+
table = table.unpack('L*')
|
799
|
+
|
800
|
+
release = Win32::API::Function.new(table[2], 'P', 'L')
|
801
|
+
release.call(@pITask)
|
802
|
+
|
803
|
+
@pITask = nil
|
804
|
+
end
|
805
|
+
|
806
|
+
task = multi_to_wide(task)
|
807
|
+
lpVtbl = 0.chr * 4
|
808
|
+
table = 0.chr * 36
|
809
|
+
|
810
|
+
memcpy(lpVtbl, @pITS, 4)
|
811
|
+
memcpy(table, lpVtbl.unpack('L').first, 36)
|
812
|
+
table = table.unpack('L*')
|
813
|
+
|
814
|
+
newWorkItem = Win32::API::Function.new(table[8], 'PPPPP', 'L')
|
815
|
+
|
816
|
+
ptr = 0.chr * 4
|
817
|
+
|
818
|
+
hr = newWorkItem.call(@pITS, task, CLSID_CTask, IID_ITask, ptr)
|
819
|
+
|
820
|
+
if FAILED(hr)
|
821
|
+
raise Error, get_last_error
|
822
|
+
end
|
823
|
+
|
824
|
+
@pITask = ptr.unpack('L').first
|
825
|
+
lpVtbl = 0.chr * 4
|
826
|
+
table = 0.chr * 16
|
827
|
+
|
828
|
+
memcpy(lpVtbl, @pITask, 4)
|
829
|
+
memcpy(table, lpVtbl.unpack('L').first, 16)
|
830
|
+
table = table.unpack('L*')
|
831
|
+
|
832
|
+
createTrigger = Win32::API::Function.new(table[3], 'PPP', 'L')
|
833
|
+
p1 = 0.chr * 4
|
834
|
+
p2 = 0.chr * 4
|
835
|
+
|
836
|
+
hr = createTrigger.call(@pITask, p1, p2)
|
837
|
+
|
838
|
+
if hr != S_OK
|
839
|
+
raise Error, get_last_error
|
840
|
+
end
|
841
|
+
|
842
|
+
pITaskTrigger = p2.unpack('L').first
|
843
|
+
lpVtbl = 0.chr * 4
|
844
|
+
table = 0.chr * 16
|
845
|
+
|
846
|
+
memcpy(lpVtbl, pITaskTrigger, 4)
|
847
|
+
memcpy(table, lpVtbl.unpack('L').first, 16)
|
848
|
+
table = table.unpack('L*')
|
849
|
+
|
850
|
+
release = Win32::API::Function.new(table[2], 'P', 'L')
|
851
|
+
setTrigger = Win32::API::Function.new(table[3], 'PP', 'L')
|
852
|
+
|
853
|
+
type1 = 0
|
854
|
+
type2 = 0
|
855
|
+
tmp = trigger['type']
|
856
|
+
tmp = nil unless tmp.is_a?(Hash)
|
857
|
+
|
858
|
+
case trigger['trigger_type']
|
859
|
+
when TASK_TIME_TRIGGER_DAILY
|
860
|
+
if tmp && tmp['days_interval']
|
861
|
+
type1 = [tmp['days_interval'],0].pack('SS').unpack('L').first
|
862
|
+
end
|
863
|
+
when TASK_TIME_TRIGGER_WEEKLY
|
864
|
+
if tmp && tmp['weeks_interval'] && tmp['days_of_week']
|
865
|
+
type1 = [tmp['weeks_interval'],tmp['days_of_week']].pack('SS').unpack('L').first
|
866
|
+
end
|
867
|
+
when TASK_TIME_TRIGGER_MONTHLYDATE
|
868
|
+
if tmp && tmp['months'] && tmp['days']
|
869
|
+
type2 = [tmp['months'],0].pack('SS').unpack('L').first
|
870
|
+
type1 = tmp['days']
|
871
|
+
end
|
872
|
+
when TASK_TIME_TRIGGER_MONTHLYDOW
|
873
|
+
if tmp && tmp['weeks'] && tmp['days_of_week'] && tmp['months']
|
874
|
+
type1 = [tmp['weeks'],tmp['days_of_week']].pack('SS').unpack('L').first
|
875
|
+
type2 = [tmp['months'],0].pack('SS').unpack('L').first
|
876
|
+
end
|
877
|
+
when TASK_TIME_TRIGGER_ONCE
|
878
|
+
# Do nothing. The Type member of the TASK_TRIGGER struct is ignored.
|
879
|
+
else
|
880
|
+
raise Error, 'Unknown trigger type'
|
881
|
+
end
|
882
|
+
|
883
|
+
pTrigger = [
|
884
|
+
48,
|
885
|
+
0,
|
886
|
+
trigger['start_year'] || 0,
|
887
|
+
trigger['start_month'] || 0,
|
888
|
+
trigger['start_day'] || 0,
|
889
|
+
trigger['end_year'] || 0,
|
890
|
+
trigger['end_month'] || 0,
|
891
|
+
trigger['end_day'] || 0,
|
892
|
+
trigger['start_hour'] || 0,
|
893
|
+
trigger['start_minute'] || 0,
|
894
|
+
trigger['minutes_duration'] || 0,
|
895
|
+
trigger['minutes_interval'] || 0,
|
896
|
+
trigger['flags'] || 0,
|
897
|
+
trigger['trigger_type'] || 0,
|
898
|
+
type1,
|
899
|
+
type2,
|
900
|
+
0,
|
901
|
+
trigger['random_minutes_interval'] || 0
|
902
|
+
].pack('S10L4LLSS')
|
903
|
+
|
904
|
+
hr = setTrigger.call(pITaskTrigger, pTrigger)
|
905
|
+
|
906
|
+
if hr != S_OK
|
907
|
+
raise Error, get_last_error
|
908
|
+
end
|
909
|
+
|
910
|
+
release.call(pITaskTrigger)
|
911
|
+
|
912
|
+
self
|
913
|
+
end
|
914
|
+
|
915
|
+
alias :new_task :new_work_item
|
916
|
+
|
917
|
+
# Returns the number of triggers associated with the active task.
|
918
|
+
#
|
919
|
+
def trigger_count
|
920
|
+
raise Error, "null pointer" if @pITS.nil?
|
921
|
+
raise Error, "No currently active task" if @pITask.nil?
|
922
|
+
|
923
|
+
lpVtbl = 0.chr * 4
|
924
|
+
table = 0.chr * 24
|
925
|
+
|
926
|
+
memcpy(lpVtbl, @pITask,4)
|
927
|
+
memcpy(table, lpVtbl.unpack('L').first, 24)
|
928
|
+
table = table.unpack('L*')
|
929
|
+
|
930
|
+
getTriggerCount = Win32::API::Function.new(table[5], 'PP', 'L')
|
931
|
+
ptr = 0.chr * 4
|
932
|
+
hr = getTriggerCount.call(@pITask, ptr)
|
933
|
+
|
934
|
+
if hr >= S_OK
|
935
|
+
count = ptr.unpack('L').first
|
936
|
+
else
|
937
|
+
raise Error, get_last_error
|
938
|
+
end
|
939
|
+
|
940
|
+
count
|
941
|
+
end
|
942
|
+
|
943
|
+
# Returns a string that describes the current trigger at the specified
|
944
|
+
# index for the active task.
|
945
|
+
#
|
946
|
+
# Example: "At 7:14 AM every day, starting 4/11/2009"
|
947
|
+
#
|
948
|
+
def trigger_string(index)
|
949
|
+
raise Error, 'null pointer' if @pITS.nil?
|
950
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
951
|
+
raise TypeError unless index.is_a?(Numeric)
|
952
|
+
|
953
|
+
lpVtbl = 0.chr * 4
|
954
|
+
table = 0.chr * 32
|
955
|
+
|
956
|
+
memcpy(lpVtbl, @pITask, 4)
|
957
|
+
memcpy(table, lpVtbl.unpack('L').first, 32)
|
958
|
+
table = table.unpack('L*')
|
959
|
+
|
960
|
+
getTriggerString = Win32::API::Function.new(table[7], 'PLP', 'L')
|
961
|
+
ptr = 0.chr * 4
|
962
|
+
hr = getTriggerString.call(@pITask, index, ptr)
|
963
|
+
|
964
|
+
if hr == S_OK
|
965
|
+
str = 0.chr * 256
|
966
|
+
wcscpy(str, ptr.unpack('L').first)
|
967
|
+
trigger = wide_to_multi(str)
|
968
|
+
CoTaskMemFree(ptr.unpack('L').first)
|
969
|
+
else
|
970
|
+
raise Error, get_last_error
|
971
|
+
end
|
972
|
+
|
973
|
+
trigger
|
974
|
+
end
|
975
|
+
|
976
|
+
# Deletes the trigger at the specified index.
|
977
|
+
#
|
978
|
+
def delete_trigger(index)
|
979
|
+
raise Error, 'null pointer' if @pITS.nil?
|
980
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
981
|
+
|
982
|
+
lpVtbl = 0.chr * 4
|
983
|
+
table = 0.chr * 20
|
984
|
+
|
985
|
+
memcpy(lpVtbl, @pITask, 4)
|
986
|
+
memcpy(table, lpVtbl.unpack('L').first, 20)
|
987
|
+
table = table.unpack('L*')
|
988
|
+
|
989
|
+
deleteTrigger = Win32::API::Function.new(table[4], 'PL', 'L')
|
990
|
+
hr = deleteTrigger.call(@pITask,index)
|
991
|
+
|
992
|
+
if hr != S_OK
|
993
|
+
raise Error, get_last_error
|
994
|
+
end
|
995
|
+
|
996
|
+
index
|
997
|
+
end
|
998
|
+
|
999
|
+
# Returns a hash that describes the trigger at the given index for the
|
1000
|
+
# current task.
|
1001
|
+
#
|
1002
|
+
def trigger(index)
|
1003
|
+
raise Error, 'null pointer' if @pITS.nil?
|
1004
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1005
|
+
|
1006
|
+
lpVtbl = 0.chr * 4
|
1007
|
+
table = 0.chr * 28
|
1008
|
+
|
1009
|
+
memcpy(lpVtbl, @pITask, 4)
|
1010
|
+
memcpy(table, lpVtbl.unpack('L').first, 28)
|
1011
|
+
table = table.unpack('L*')
|
1012
|
+
|
1013
|
+
getTrigger = Win32::API::Function.new(table[6], 'PLP', 'L')
|
1014
|
+
ptr = 0.chr * 4
|
1015
|
+
hr = getTrigger.call(@pITask, index, ptr)
|
1016
|
+
|
1017
|
+
if hr != S_OK
|
1018
|
+
raise Error, get_last_error
|
1019
|
+
end
|
1020
|
+
|
1021
|
+
pITaskTrigger = ptr.unpack('L').first
|
1022
|
+
lpVtbl = 0.chr * 4
|
1023
|
+
table = 0.chr * 20
|
1024
|
+
|
1025
|
+
memcpy(lpVtbl, pITaskTrigger, 4)
|
1026
|
+
memcpy(table, lpVtbl.unpack('L').first, 20)
|
1027
|
+
table = table.unpack('L*')
|
1028
|
+
|
1029
|
+
release = Win32::API::Function.new(table[2], 'P', 'L')
|
1030
|
+
getTrigger = Win32::API::Function.new(table[4], 'PP', 'L')
|
1031
|
+
|
1032
|
+
pTrigger = [48].pack('S') + 0.chr * 46
|
1033
|
+
hr = getTrigger.call(pITaskTrigger, pTrigger)
|
1034
|
+
|
1035
|
+
if hr != S_OK
|
1036
|
+
error = get_last_error
|
1037
|
+
release.call(pITaskTrigger)
|
1038
|
+
raise Error, error
|
1039
|
+
end
|
1040
|
+
|
1041
|
+
tr = pTrigger.unpack('S10L4LLSS')
|
1042
|
+
|
1043
|
+
trigger = {}
|
1044
|
+
trigger['start_year'] = tr[2]
|
1045
|
+
trigger['start_month'] = tr[3]
|
1046
|
+
trigger['start_day'] = tr[4]
|
1047
|
+
trigger['end_year'] = tr[5]
|
1048
|
+
trigger['end_month'] = tr[6]
|
1049
|
+
trigger['end_day'] = tr[7]
|
1050
|
+
trigger['start_hour'] = tr[8]
|
1051
|
+
trigger['start_minute'] = tr[9]
|
1052
|
+
trigger['minutes_duration'] = tr[10]
|
1053
|
+
trigger['minutes_interval'] = tr[11]
|
1054
|
+
trigger['flags'] = tr[12]
|
1055
|
+
trigger['trigger_type'] = tr[13]
|
1056
|
+
trigger['random_minutes_interval'] = tr[17]
|
1057
|
+
|
1058
|
+
case tr[13]
|
1059
|
+
when TASK_TIME_TRIGGER_DAILY
|
1060
|
+
tmp = {}
|
1061
|
+
tmp['days_interval'] = [tr[14]].pack('L').unpack('SS').first
|
1062
|
+
trigger['type'] = tmp
|
1063
|
+
when TASK_TIME_TRIGGER_WEEKLY
|
1064
|
+
tmp = {}
|
1065
|
+
tmp['weeks_interval'],tmp['days_of_week'] = [tr[14]].pack('L').unpack('SS')
|
1066
|
+
trigger['type'] = tmp
|
1067
|
+
when TASK_TIME_TRIGGER_MONTHLYDATE
|
1068
|
+
tmp = {}
|
1069
|
+
tmp['days'] = tr[14]
|
1070
|
+
tmp['months'] = [tr[15]].pack('L').unpack('SS').first
|
1071
|
+
trigger['type'] = tmp
|
1072
|
+
when TASK_TIME_TRIGGER_MONTHLYDOW
|
1073
|
+
tmp = {}
|
1074
|
+
tmp['weeks'],tmp['days_of_week'] = [tr[14]].pack('L').unpack('SS')
|
1075
|
+
tmp['months'] = [tr[15]].pack('L').unpack('SS').first
|
1076
|
+
trigger['type'] = tmp
|
1077
|
+
when TASK_TIME_TRIGGER_ONCE
|
1078
|
+
tmp = {}
|
1079
|
+
tmp['once'] = nil
|
1080
|
+
trigger['type'] = tmp
|
1081
|
+
else
|
1082
|
+
raise Error, 'Unknown trigger type'
|
1083
|
+
end
|
1084
|
+
|
1085
|
+
release.call(pITaskTrigger)
|
1086
|
+
|
1087
|
+
trigger
|
1088
|
+
end
|
1089
|
+
|
1090
|
+
# Sets the trigger for the currently active task.
|
1091
|
+
#
|
1092
|
+
def trigger=(trigger)
|
1093
|
+
raise Error, 'null pointer' if @pITS.nil?
|
1094
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1095
|
+
raise TypeError unless trigger.is_a?(Hash)
|
1096
|
+
|
1097
|
+
lpVtbl = 0.chr * 4
|
1098
|
+
table = 0.chr * 16
|
1099
|
+
|
1100
|
+
memcpy(lpVtbl, @pITask, 4)
|
1101
|
+
memcpy(table, lpVtbl.unpack('L').first, 16)
|
1102
|
+
table = table.unpack('L*')
|
1103
|
+
|
1104
|
+
createTrigger = Win32::API::Function.new(table[3], 'PPP', 'L')
|
1105
|
+
|
1106
|
+
p1 = 0.chr * 4
|
1107
|
+
p2 = 0.chr * 4
|
1108
|
+
|
1109
|
+
hr = createTrigger.call(@pITask, p1, p2)
|
1110
|
+
|
1111
|
+
if hr != S_OK
|
1112
|
+
raise Error, get_last_error
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
pITaskTrigger = p2.unpack('L').first
|
1116
|
+
lpVtbl = 0.chr * 4
|
1117
|
+
table = 0.chr * 16
|
1118
|
+
|
1119
|
+
memcpy(lpVtbl, pITaskTrigger, 4)
|
1120
|
+
memcpy(table, lpVtbl.unpack('L').first, 16)
|
1121
|
+
table = table.unpack('L*')
|
1122
|
+
|
1123
|
+
release = Win32::API::Function.new(table[2], 'P', 'L')
|
1124
|
+
setTrigger = Win32::API::Function.new(table[3], 'PP', 'L')
|
1125
|
+
|
1126
|
+
type1 = 0
|
1127
|
+
type2 = 0
|
1128
|
+
tmp = trigger['type']
|
1129
|
+
tmp = nil unless tmp.is_a?(Hash)
|
1130
|
+
|
1131
|
+
case trigger['trigger_type']
|
1132
|
+
when TASK_TIME_TRIGGER_DAILY
|
1133
|
+
if tmp && tmp['days_interval']
|
1134
|
+
type1 = [tmp['days_interval'],0].pack('SS').unpack('L').first
|
1135
|
+
end
|
1136
|
+
when TASK_TIME_TRIGGER_WEEKLY
|
1137
|
+
if tmp && tmp['weeks_interval'] && tmp['days_of_week']
|
1138
|
+
type1 = [tmp['weeks_interval'],tmp['days_of_week']].pack('SS').unpack('L').first
|
1139
|
+
end
|
1140
|
+
when TASK_TIME_TRIGGER_MONTHLYDATE
|
1141
|
+
if tmp && tmp['months'] && tmp['days']
|
1142
|
+
type2 = [tmp['months'],0].pack('SS').unpack('L').first
|
1143
|
+
type1 = tmp['days']
|
1144
|
+
end
|
1145
|
+
when TASK_TIME_TRIGGER_MONTHLYDOW
|
1146
|
+
if tmp && tmp['weeks'] && tmp['days_of_week'] && tmp['months']
|
1147
|
+
type1 = [tmp['weeks'],tmp['days_of_week']].pack('SS').unpack('L').first
|
1148
|
+
type2 = [tmp['months'],0].pack('SS').unpack('L').first
|
1149
|
+
end
|
1150
|
+
when TASK_TIME_TRIGGER_ONCE
|
1151
|
+
# Do nothing. The Type member of the TASK_TRIGGER struct is ignored.
|
1152
|
+
else
|
1153
|
+
raise Error, 'Unknown trigger type'
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
pTrigger = [
|
1157
|
+
48,
|
1158
|
+
0,
|
1159
|
+
trigger['start_year'] || 0,
|
1160
|
+
trigger['start_month'] || 0,
|
1161
|
+
trigger['start_day'] || 0,
|
1162
|
+
trigger['end_year'] || 0,
|
1163
|
+
trigger['end_month'] || 0,
|
1164
|
+
trigger['end_day'] || 0,
|
1165
|
+
trigger['start_hour'] || 0,
|
1166
|
+
trigger['start_minute'] || 0,
|
1167
|
+
trigger['minutes_duration'] || 0,
|
1168
|
+
trigger['minutes_interval'] || 0,
|
1169
|
+
trigger['flags'] || 0,
|
1170
|
+
trigger['trigger_type'] || 0,
|
1171
|
+
type1,
|
1172
|
+
type2,
|
1173
|
+
0,
|
1174
|
+
trigger['random_minutes_interval'] || 0
|
1175
|
+
].pack('S10L4LLSS')
|
1176
|
+
|
1177
|
+
hr = setTrigger.call(pITaskTrigger, pTrigger)
|
1178
|
+
|
1179
|
+
if hr != S_OK
|
1180
|
+
raise Error, get_last_error
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
release.call(pITaskTrigger)
|
1184
|
+
|
1185
|
+
trigger
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
# Adds a trigger at the specified index.
|
1189
|
+
#
|
1190
|
+
def add_trigger(index, trigger)
|
1191
|
+
raise Error, 'null pointer' if @pITS.nil?
|
1192
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1193
|
+
raise TypeError unless trigger.is_a?(Hash)
|
1194
|
+
|
1195
|
+
lpVtbl = 0.chr * 4
|
1196
|
+
table = 0.chr * 28
|
1197
|
+
|
1198
|
+
memcpy(lpVtbl, @pITask, 4)
|
1199
|
+
memcpy(table, lpVtbl.unpack('L').first, 28)
|
1200
|
+
table = table.unpack('L*')
|
1201
|
+
|
1202
|
+
getTrigger = Win32::API::Function.new(table[6], 'PLP', 'L')
|
1203
|
+
ptr = 0.chr * 4
|
1204
|
+
hr = getTrigger.call(@pITask, index, ptr)
|
1205
|
+
|
1206
|
+
if hr != S_OK
|
1207
|
+
raise Error, get_last_error
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
pITaskTrigger = ptr.unpack('L').first
|
1211
|
+
lpVtbl = 0.chr * 4
|
1212
|
+
table = 0.chr * 16
|
1213
|
+
|
1214
|
+
memcpy(lpVtbl, pITaskTrigger,4)
|
1215
|
+
memcpy(table, lpVtbl.unpack('L').first,16)
|
1216
|
+
table = table.unpack('L*')
|
1217
|
+
|
1218
|
+
release = Win32::API::Function.new(table[2], 'P', 'L')
|
1219
|
+
setTrigger = Win32::API::Function.new(table[3], 'PP', 'L')
|
1220
|
+
|
1221
|
+
type1 = 0
|
1222
|
+
type2 = 0
|
1223
|
+
tmp = trigger['type']
|
1224
|
+
tmp = nil unless tmp.is_a?(Hash)
|
1225
|
+
|
1226
|
+
case trigger['trigger_type']
|
1227
|
+
when TASK_TIME_TRIGGER_DAILY
|
1228
|
+
if tmp && tmp['days_interval']
|
1229
|
+
type1 = [tmp['days_interval'],0].pack('SS').unpack('L').first
|
1230
|
+
end
|
1231
|
+
when TASK_TIME_TRIGGER_WEEKLY
|
1232
|
+
if tmp && tmp['weeks_interval'] && tmp['days_of_week']
|
1233
|
+
type1 = [tmp['weeks_interval'],tmp['days_of_week']].pack('SS').unpack('L').first
|
1234
|
+
end
|
1235
|
+
when TASK_TIME_TRIGGER_MONTHLYDATE
|
1236
|
+
if tmp && tmp['months'] && tmp['days']
|
1237
|
+
type2 = [tmp['months'],0].pack('SS').unpack('L').first
|
1238
|
+
type1 = tmp['days']
|
1239
|
+
end
|
1240
|
+
when TASK_TIME_TRIGGER_MONTHLYDOW
|
1241
|
+
if tmp && tmp['weeks'] && tmp['days_of_week'] && tmp['months']
|
1242
|
+
type1 = [tmp['weeks'],tmp['days_of_week']].pack('SS').unpack('L').first
|
1243
|
+
type2 = [tmp['months'],0].pack('SS').unpack('L').first
|
1244
|
+
end
|
1245
|
+
when TASK_TIME_TRIGGER_ONCE
|
1246
|
+
# Do nothing. The Type member of the TASK_TRIGGER struct is ignored.
|
1247
|
+
else
|
1248
|
+
raise Error, 'Unknown trigger type'
|
1249
|
+
end
|
1250
|
+
|
1251
|
+
pTrigger = [
|
1252
|
+
48,
|
1253
|
+
0,
|
1254
|
+
trigger['start_year'] || 0,
|
1255
|
+
trigger['start_month'] || 0,
|
1256
|
+
trigger['start_day'] || 0,
|
1257
|
+
trigger['end_year'] || 0,
|
1258
|
+
trigger['end_month'] || 0,
|
1259
|
+
trigger['end_day'] || 0,
|
1260
|
+
trigger['start_hour'] || 0,
|
1261
|
+
trigger['start_minute'] || 0,
|
1262
|
+
trigger['minutes_duration'] || 0,
|
1263
|
+
trigger['minutes_interval'] || 0,
|
1264
|
+
trigger['flags'] || 0,
|
1265
|
+
trigger['trigger_type'] || 0,
|
1266
|
+
type1,
|
1267
|
+
type2,
|
1268
|
+
0,
|
1269
|
+
trigger['random_minutes_interval'] || 0
|
1270
|
+
].pack('S10L4LLSS')
|
1271
|
+
|
1272
|
+
hr = setTrigger.call(pITaskTrigger, pTrigger)
|
1273
|
+
|
1274
|
+
if hr != S_OK
|
1275
|
+
raise Error, get_last_error
|
1276
|
+
end
|
1277
|
+
|
1278
|
+
release.call(pITaskTrigger)
|
1279
|
+
true
|
1280
|
+
end
|
1281
|
+
|
1282
|
+
# Returns the flags (integer) that modify the behavior of the work item. You
|
1283
|
+
# must OR the return value to determine the flags yourself.
|
1284
|
+
#
|
1285
|
+
def flags
|
1286
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1287
|
+
|
1288
|
+
lpVtbl = 0.chr * 4
|
1289
|
+
table = 0.chr * 120
|
1290
|
+
|
1291
|
+
memcpy(lpVtbl, @pITask, 4)
|
1292
|
+
memcpy(table, lpVtbl.unpack('L').first, 120)
|
1293
|
+
table = table.unpack('L*')
|
1294
|
+
|
1295
|
+
getFlags = Win32::API::Function.new(table[29], 'PP', 'L')
|
1296
|
+
ptr = 0.chr * 4
|
1297
|
+
hr = getFlags.call(@pITask, ptr)
|
1298
|
+
|
1299
|
+
if hr != S_OK
|
1300
|
+
raise Error, get_last_error
|
1301
|
+
end
|
1302
|
+
|
1303
|
+
flags = ptr.unpack('L').first
|
1304
|
+
end
|
1305
|
+
|
1306
|
+
# Sets an OR'd value of flags that modify the behavior of the work item.
|
1307
|
+
#
|
1308
|
+
def flags=(flags)
|
1309
|
+
raise Error, 'null pointer' if @pITS.nil?
|
1310
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1311
|
+
|
1312
|
+
lpVtbl = 0.chr * 4
|
1313
|
+
table = 0.chr * 116
|
1314
|
+
|
1315
|
+
memcpy(lpVtbl, @pITask, 4)
|
1316
|
+
memcpy(table, lpVtbl.unpack('L').first, 116)
|
1317
|
+
table = table.unpack('L*')
|
1318
|
+
|
1319
|
+
setFlags = Win32::API::Function.new(table[28], 'PL', 'L')
|
1320
|
+
hr = setFlags.call(@pITask, flags)
|
1321
|
+
|
1322
|
+
if hr != S_OK
|
1323
|
+
raise Error, get_last_error
|
1324
|
+
end
|
1325
|
+
|
1326
|
+
flags
|
1327
|
+
end
|
1328
|
+
|
1329
|
+
# Returns the status of the currently active task. Possible values are
|
1330
|
+
# 'ready', 'running', 'not scheduled' or 'unknown'.
|
1331
|
+
#
|
1332
|
+
def status
|
1333
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1334
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1335
|
+
|
1336
|
+
lpVtbl = 0.chr * 4
|
1337
|
+
table = 0.chr * 68
|
1338
|
+
|
1339
|
+
memcpy(lpVtbl,@pITask,4)
|
1340
|
+
memcpy(table,lpVtbl.unpack('L').first,68)
|
1341
|
+
table = table.unpack('L*')
|
1342
|
+
|
1343
|
+
getStatus = Win32::API::Function.new(table[16], 'PP', 'L')
|
1344
|
+
ptr = 0.chr * 4
|
1345
|
+
hr = getStatus.call(@pITask, ptr)
|
1346
|
+
|
1347
|
+
if hr != S_OK
|
1348
|
+
raise Error,get_last_error
|
1349
|
+
end
|
1350
|
+
|
1351
|
+
st = ptr.unpack('L').first
|
1352
|
+
|
1353
|
+
case st
|
1354
|
+
when 0x00041300 # SCHED_S_TASK_READY
|
1355
|
+
status = 'ready'
|
1356
|
+
when 0x00041301 # SCHED_S_TASK_RUNNING
|
1357
|
+
status = 'running'
|
1358
|
+
when 0x00041305 # SCHED_S_TASK_NOT_SCHEDULED
|
1359
|
+
status = 'not scheduled'
|
1360
|
+
else
|
1361
|
+
status = 'unknown'
|
1362
|
+
end
|
1363
|
+
|
1364
|
+
status
|
1365
|
+
end
|
1366
|
+
|
1367
|
+
# Returns the exit code from the last scheduled run.
|
1368
|
+
#
|
1369
|
+
def exit_code
|
1370
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1371
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1372
|
+
|
1373
|
+
lpVtbl = 0.chr * 4
|
1374
|
+
table = 0.chr * 72
|
1375
|
+
|
1376
|
+
memcpy(lpVtbl, @pITask, 4)
|
1377
|
+
memcpy(table, lpVtbl.unpack('L').first, 72)
|
1378
|
+
table = table.unpack('L*')
|
1379
|
+
|
1380
|
+
getExitCode = Win32::API::Function.new(table[17], 'PP', 'L')
|
1381
|
+
ptr = 0.chr * 4
|
1382
|
+
hr = getExitCode.call(@pITask, ptr)
|
1383
|
+
|
1384
|
+
if hr > 0x80000000
|
1385
|
+
raise Error, get_last_error
|
1386
|
+
end
|
1387
|
+
|
1388
|
+
exit_code = ptr.unpack('L').first
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
# Returns the comment associated with the task, if any.
|
1392
|
+
#
|
1393
|
+
def comment
|
1394
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1395
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1396
|
+
|
1397
|
+
lpVtbl = 0.chr * 4
|
1398
|
+
table = 0.chr * 80
|
1399
|
+
|
1400
|
+
memcpy(lpVtbl, @pITask, 4)
|
1401
|
+
memcpy(table, lpVtbl.unpack('L').first, 80)
|
1402
|
+
table = table.unpack('L*')
|
1403
|
+
|
1404
|
+
getComment = Win32::API::Function.new(table[19], 'PP', 'L')
|
1405
|
+
ptr = 0.chr * 4
|
1406
|
+
hr = getComment.call(@pITask, ptr)
|
1407
|
+
|
1408
|
+
if hr != S_OK
|
1409
|
+
raise Error,get_last_error
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
str = 0.chr * 256
|
1413
|
+
wcscpy(str, ptr.unpack('L').first)
|
1414
|
+
CoTaskMemFree(ptr.unpack('L').first)
|
1415
|
+
wide_to_multi(str)
|
1416
|
+
end
|
1417
|
+
|
1418
|
+
# Sets the comment for the task.
|
1419
|
+
#
|
1420
|
+
def comment=(comment)
|
1421
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1422
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1423
|
+
raise TypeError unless comment.is_a?(String)
|
1424
|
+
|
1425
|
+
lpVtbl = 0.chr * 4
|
1426
|
+
table = 0.chr * 76
|
1427
|
+
|
1428
|
+
memcpy(lpVtbl, @pITask, 4)
|
1429
|
+
memcpy(table, lpVtbl.unpack('L').first, 76)
|
1430
|
+
table = table.unpack('L*')
|
1431
|
+
|
1432
|
+
setComment = Win32::API::Function.new(table[18], 'PP', 'L')
|
1433
|
+
comment_w = multi_to_wide(comment)
|
1434
|
+
hr = setComment.call(@pITask, comment_w)
|
1435
|
+
|
1436
|
+
if hr != S_OK
|
1437
|
+
raise Error, get_last_error
|
1438
|
+
end
|
1439
|
+
|
1440
|
+
comment
|
1441
|
+
end
|
1442
|
+
|
1443
|
+
# Returns the name of the user who created the task.
|
1444
|
+
#
|
1445
|
+
def creator
|
1446
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1447
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1448
|
+
|
1449
|
+
lpVtbl = 0.chr * 4
|
1450
|
+
table = 0.chr * 88
|
1451
|
+
|
1452
|
+
memcpy(lpVtbl, @pITask, 4)
|
1453
|
+
memcpy(table, lpVtbl.unpack('L').first, 88)
|
1454
|
+
table = table.unpack('L*')
|
1455
|
+
|
1456
|
+
getCreator = Win32::API::Function.new(table[21], 'PP', 'L')
|
1457
|
+
ptr = 0.chr * 4
|
1458
|
+
hr = getCreator.call(@pITask, ptr)
|
1459
|
+
|
1460
|
+
if hr != S_OK
|
1461
|
+
raise Error, get_last_error
|
1462
|
+
end
|
1463
|
+
|
1464
|
+
str = 0.chr * 256
|
1465
|
+
wcscpy(str, ptr.unpack('L').first)
|
1466
|
+
CoTaskMemFree(ptr.unpack('L').first)
|
1467
|
+
wide_to_multi(str)
|
1468
|
+
end
|
1469
|
+
|
1470
|
+
# Sets the creator for the task.
|
1471
|
+
#
|
1472
|
+
def creator=(creator)
|
1473
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1474
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1475
|
+
raise TypeError unless creator.is_a?(String)
|
1476
|
+
|
1477
|
+
lpVtbl = 0.chr * 4
|
1478
|
+
table = 0.chr * 84
|
1479
|
+
|
1480
|
+
memcpy(lpVtbl, @pITask, 4)
|
1481
|
+
memcpy(table, lpVtbl.unpack('L').first, 84)
|
1482
|
+
table = table.unpack('L*')
|
1483
|
+
|
1484
|
+
setCreator = Win32::API::Function.new(table[20], 'PP', 'L')
|
1485
|
+
creator_w = multi_to_wide(creator)
|
1486
|
+
hr = setCreator.call(@pITask, creator_w)
|
1487
|
+
|
1488
|
+
if hr != S_OK
|
1489
|
+
raise Error, get_last_error
|
1490
|
+
end
|
1491
|
+
|
1492
|
+
creator
|
1493
|
+
end
|
1494
|
+
|
1495
|
+
# Returns a Time object that indicates the next time the task will run.
|
1496
|
+
#
|
1497
|
+
def next_run_time
|
1498
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1499
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1500
|
+
|
1501
|
+
lpVtbl = 0.chr * 4
|
1502
|
+
table = 0.chr * 40
|
1503
|
+
|
1504
|
+
memcpy(lpVtbl, @pITask, 4)
|
1505
|
+
memcpy(table, lpVtbl.unpack('L').first, 40)
|
1506
|
+
table = table.unpack('L*')
|
1507
|
+
|
1508
|
+
getNextRunTime = Win32::API::Function.new(table[9], 'PP', 'L')
|
1509
|
+
st = 0.chr * 16
|
1510
|
+
hr = getNextRunTime.call(@pITask, st)
|
1511
|
+
|
1512
|
+
if hr != S_OK
|
1513
|
+
raise Error, get_last_error
|
1514
|
+
end
|
1515
|
+
|
1516
|
+
a1,a2,_,a3,a4,a5,a6,a7 = st.unpack('S*')
|
1517
|
+
a7 *= 1000
|
1518
|
+
|
1519
|
+
Time.local(a1,a2,a3,a4,a5,a6,a7)
|
1520
|
+
end
|
1521
|
+
|
1522
|
+
# Returns a Time object indicating the most recent time the task ran or
|
1523
|
+
# nil if the task has never run.
|
1524
|
+
#
|
1525
|
+
def most_recent_run_time
|
1526
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1527
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1528
|
+
|
1529
|
+
lpVtbl = 0.chr * 4
|
1530
|
+
table = 0.chr * 64
|
1531
|
+
|
1532
|
+
memcpy(lpVtbl, @pITask, 4)
|
1533
|
+
memcpy(table, lpVtbl.unpack('L').first, 64)
|
1534
|
+
table = table.unpack('L*')
|
1535
|
+
|
1536
|
+
getMostRecentRunTime = Win32::API::Function.new(table[15], 'PP', 'L')
|
1537
|
+
st = 0.chr * 16
|
1538
|
+
hr = getMostRecentRunTime.call(@pITask, st)
|
1539
|
+
|
1540
|
+
if hr == 0x00041303 # SCHED_S_TASK_HAS_NOT_RUN
|
1541
|
+
time = nil
|
1542
|
+
elsif hr == S_OK
|
1543
|
+
a1, a2, _, a3, a4, a5, a6, a7 = st.unpack('S*')
|
1544
|
+
a7 *= 1000
|
1545
|
+
time = Time.local(a1, a2, a3, a4, a5, a6, a7)
|
1546
|
+
else
|
1547
|
+
raise Error, get_last_error
|
1548
|
+
end
|
1549
|
+
|
1550
|
+
time
|
1551
|
+
end
|
1552
|
+
|
1553
|
+
# Returns the maximum length of time, in milliseconds, that the task
|
1554
|
+
# will run before terminating.
|
1555
|
+
#
|
1556
|
+
def max_run_time
|
1557
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1558
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1559
|
+
|
1560
|
+
lpVtbl = 0.chr * 4
|
1561
|
+
table = 0.chr * 176
|
1562
|
+
|
1563
|
+
memcpy(lpVtbl, @pITask, 4)
|
1564
|
+
memcpy(table, lpVtbl.unpack('L').first, 176)
|
1565
|
+
table = table.unpack('L*')
|
1566
|
+
|
1567
|
+
getMaxRunTime = Win32::API::Function.new(table[43], 'PP', 'L')
|
1568
|
+
|
1569
|
+
ptr = 0.chr * 4
|
1570
|
+
hr = getMaxRunTime.call(@pITask, ptr)
|
1571
|
+
|
1572
|
+
if hr != S_OK
|
1573
|
+
raise Error, get_last_error
|
1574
|
+
end
|
1575
|
+
|
1576
|
+
max_run_time = ptr.unpack('L').first
|
1577
|
+
end
|
1578
|
+
|
1579
|
+
# Sets the maximum length of time, in milliseconds, that the task can run
|
1580
|
+
# before terminating. Returns the value you specified if successful.
|
1581
|
+
#
|
1582
|
+
def max_run_time=(max_run_time)
|
1583
|
+
raise Error, 'null pointer' if @pITask.nil?
|
1584
|
+
raise Error, 'No currently active task' if @pITask.nil?
|
1585
|
+
raise TypeError unless max_run_time.is_a?(Numeric)
|
1586
|
+
|
1587
|
+
lpVtbl = 0.chr * 4
|
1588
|
+
table = 0.chr * 172
|
1589
|
+
|
1590
|
+
memcpy(lpVtbl, @pITask, 4)
|
1591
|
+
memcpy(table, lpVtbl.unpack('L').first, 172)
|
1592
|
+
table = table.unpack('L*')
|
1593
|
+
|
1594
|
+
setMaxRunTime = Win32::API::Function.new(table[42], 'PL', 'L')
|
1595
|
+
hr = setMaxRunTime.call(@pITask, max_run_time)
|
1596
|
+
|
1597
|
+
if hr != S_OK
|
1598
|
+
raise Error,get_last_error
|
1599
|
+
end
|
1600
|
+
|
1601
|
+
max_run_time
|
1602
|
+
end
|
1603
|
+
|
1604
|
+
# Returns whether or not the scheduled task exists.
|
1605
|
+
def exists?(job_name)
|
1606
|
+
bool = false
|
1607
|
+
Dir.foreach('C:/Windows/Tasks'){ |file|
|
1608
|
+
if File.basename(file, '.job') == job_name
|
1609
|
+
bool = true
|
1610
|
+
break
|
1611
|
+
end
|
1612
|
+
}
|
1613
|
+
bool
|
1614
|
+
end
|
1615
|
+
|
1616
|
+
private
|
1617
|
+
|
1618
|
+
# Used for the new_work_item method
|
1619
|
+
ValidTriggerKeys = [
|
1620
|
+
'end_day',
|
1621
|
+
'end_month',
|
1622
|
+
'end_year',
|
1623
|
+
'flags',
|
1624
|
+
'minutes_duration',
|
1625
|
+
'minutes_interval',
|
1626
|
+
'random_minutes_interval',
|
1627
|
+
'start_day',
|
1628
|
+
'start_hour',
|
1629
|
+
'start_minute',
|
1630
|
+
'start_month',
|
1631
|
+
'start_year',
|
1632
|
+
'trigger_type',
|
1633
|
+
'type'
|
1634
|
+
]
|
1635
|
+
|
1636
|
+
ValidTypeKeys = [
|
1637
|
+
'days_interval',
|
1638
|
+
'weeks_interval',
|
1639
|
+
'days_of_week',
|
1640
|
+
'months',
|
1641
|
+
'days',
|
1642
|
+
'weeks'
|
1643
|
+
]
|
1644
|
+
|
1645
|
+
# Private method that validates keys, and converts all keys to lowercase
|
1646
|
+
# strings.
|
1647
|
+
#
|
1648
|
+
def transform_and_validate(hash)
|
1649
|
+
new_hash = {}
|
1650
|
+
|
1651
|
+
hash.each{ |key, value|
|
1652
|
+
key = key.to_s.downcase
|
1653
|
+
if key == 'type'
|
1654
|
+
new_type_hash = {}
|
1655
|
+
raise ArgumentError unless value.is_a?(Hash)
|
1656
|
+
value.each{ |subkey, subvalue|
|
1657
|
+
subkey = subkey.to_s.downcase
|
1658
|
+
if ValidTypeKeys.include?(subkey)
|
1659
|
+
new_type_hash[subkey] = subvalue
|
1660
|
+
else
|
1661
|
+
raise ArgumentError, "Invalid type key '#{subkey}'"
|
1662
|
+
end
|
1663
|
+
}
|
1664
|
+
new_hash[key] = new_type_hash
|
1665
|
+
else
|
1666
|
+
if ValidTriggerKeys.include?(key)
|
1667
|
+
new_hash[key] = value
|
1668
|
+
else
|
1669
|
+
raise ArgumentError, "Invalid key '#{key}'"
|
1670
|
+
end
|
1671
|
+
end
|
1672
|
+
}
|
1673
|
+
|
1674
|
+
new_hash
|
1675
|
+
end
|
1676
|
+
end
|
1677
|
+
end
|