win32-taskscheduler 0.2.0 → 2.0.4

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 00e707355e5575715bf3e9dbc5c393b961f25582297aeabf0c1c0d926bdc5b57
4
+ data.tar.gz: '04017484cc39076681c335c3be0698b14242bb7ef72041a1b67795d31dea44a0'
5
+ SHA512:
6
+ metadata.gz: d0b8dd84740d507701fa5968bf495d576656428f03b27230f62aedcee15fde5977243d838c343f95eed78a23203b0b5390d0ba2636e79b43aacf114b9c946429
7
+ data.tar.gz: 265ae1a999a3edded0a35e4b43062373be85ab5f0f239fc9b8300ffdb5f2266d46f2b3bae485b50adacf670cedee6cd086fadb4d22f8a9d7d95ff68b3f8ecaab
@@ -0,0 +1 @@
1
+ require_relative "win32/taskscheduler"
@@ -1,1677 +1,1434 @@
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
1
+ require_relative "taskscheduler/sid"
2
+ require_relative "taskscheduler/helper"
3
+ require_relative "taskscheduler/time_calc_helper"
4
+ require_relative "taskscheduler/constants"
5
+ require_relative "taskscheduler/version"
6
+ require "win32ole"
7
+ require "socket"
8
+ require "time"
9
+ require "structured_warnings"
10
+
11
+ # The Win32 module serves as a namespace only
12
+ module Win32
13
+ # The TaskScheduler class encapsulates a Windows scheduled task
14
+ class TaskScheduler
15
+ include Win32::TaskScheduler::Helper
16
+ include Win32::TaskScheduler::TaskSchedulerConstants
17
+ include Win32::TaskScheduler::TimeCalcHelper
18
+ include Win32::TaskScheduler::SID
19
+
20
+ # The Error class is typically raised if any TaskScheduler methods fail.
21
+ class Error < StandardError; end
22
+
23
+ # Shorthand constants
24
+
25
+ IDLE = IDLE_PRIORITY_CLASS
26
+ NORMAL = NORMAL_PRIORITY_CLASS
27
+ HIGH = HIGH_PRIORITY_CLASS
28
+ REALTIME = REALTIME_PRIORITY_CLASS
29
+ BELOW_NORMAL = BELOW_NORMAL_PRIORITY_CLASS
30
+ ABOVE_NORMAL = ABOVE_NORMAL_PRIORITY_CLASS
31
+
32
+ ONCE = TASK_TIME_TRIGGER_ONCE
33
+ DAILY = TASK_TIME_TRIGGER_DAILY
34
+ WEEKLY = TASK_TIME_TRIGGER_WEEKLY
35
+ MONTHLYDATE = TASK_TIME_TRIGGER_MONTHLYDATE
36
+ MONTHLYDOW = TASK_TIME_TRIGGER_MONTHLYDOW
37
+
38
+ ON_IDLE = TASK_EVENT_TRIGGER_ON_IDLE
39
+ AT_SYSTEMSTART = TASK_EVENT_TRIGGER_AT_SYSTEMSTART
40
+ AT_LOGON = TASK_EVENT_TRIGGER_AT_LOGON
41
+ FIRST_WEEK = TASK_FIRST_WEEK
42
+ SECOND_WEEK = TASK_SECOND_WEEK
43
+ THIRD_WEEK = TASK_THIRD_WEEK
44
+ FOURTH_WEEK = TASK_FOURTH_WEEK
45
+ LAST_WEEK = TASK_LAST_WEEK
46
+ SUNDAY = TASK_SUNDAY
47
+ MONDAY = TASK_MONDAY
48
+ TUESDAY = TASK_TUESDAY
49
+ WEDNESDAY = TASK_WEDNESDAY
50
+ THURSDAY = TASK_THURSDAY
51
+ FRIDAY = TASK_FRIDAY
52
+ SATURDAY = TASK_SATURDAY
53
+ JANUARY = TASK_JANUARY
54
+ FEBRUARY = TASK_FEBRUARY
55
+ MARCH = TASK_MARCH
56
+ APRIL = TASK_APRIL
57
+ MAY = TASK_MAY
58
+ JUNE = TASK_JUNE
59
+ JULY = TASK_JULY
60
+ AUGUST = TASK_AUGUST
61
+ SEPTEMBER = TASK_SEPTEMBER
62
+ OCTOBER = TASK_OCTOBER
63
+ NOVEMBER = TASK_NOVEMBER
64
+ DECEMBER = TASK_DECEMBER
65
+
66
+ INTERACTIVE = TASK_FLAG_INTERACTIVE
67
+ DELETE_WHEN_DONE = TASK_FLAG_DELETE_WHEN_DONE
68
+ DISABLED = TASK_FLAG_DISABLED
69
+ START_ONLY_IF_IDLE = TASK_FLAG_START_ONLY_IF_IDLE
70
+ KILL_ON_IDLE_END = TASK_FLAG_KILL_ON_IDLE_END
71
+ DONT_START_IF_ON_BATTERIES = TASK_FLAG_DONT_START_IF_ON_BATTERIES
72
+ KILL_IF_GOING_ON_BATTERIES = TASK_FLAG_KILL_IF_GOING_ON_BATTERIES
73
+ RUN_ONLY_IF_DOCKED = TASK_FLAG_RUN_ONLY_IF_DOCKED
74
+ HIDDEN = TASK_FLAG_HIDDEN
75
+ RUN_IF_CONNECTED_TO_INTERNET = TASK_FLAG_RUN_IF_CONNECTED_TO_INTERNET
76
+ RESTART_ON_IDLE_RESUME = TASK_FLAG_RESTART_ON_IDLE_RESUME
77
+ SYSTEM_REQUIRED = TASK_FLAG_SYSTEM_REQUIRED
78
+ RUN_ONLY_IF_LOGGED_ON = TASK_FLAG_RUN_ONLY_IF_LOGGED_ON
79
+
80
+ FLAG_HAS_END_DATE = TASK_TRIGGER_FLAG_HAS_END_DATE
81
+ FLAG_KILL_AT_DURATION_END = TASK_TRIGGER_FLAG_KILL_AT_DURATION_END
82
+ FLAG_DISABLED = TASK_TRIGGER_FLAG_DISABLED
83
+
84
+ MAX_RUN_TIMES = TASK_MAX_RUN_TIMES
85
+
86
+ FIRST = TASK_FIRST
87
+ SECOND = TASK_SECOND
88
+ THIRD = TASK_THIRD
89
+ FOURTH = TASK_FOURTH
90
+ FIFTH = TASK_FIFTH
91
+ SIXTH = TASK_SIXTH
92
+ SEVENTH = TASK_SEVENTH
93
+ EIGHTH = TASK_EIGHTH
94
+ NINETH = TASK_NINETH
95
+ TENTH = TASK_TENTH
96
+ ELEVENTH = TASK_ELEVENTH
97
+ TWELFTH = TASK_TWELFTH
98
+ THIRTEENTH = TASK_THIRTEENTH
99
+ FOURTEENTH = TASK_FOURTEENTH
100
+ FIFTEENTH = TASK_FIFTEENTH
101
+ SIXTEENTH = TASK_SIXTEENTH
102
+ SEVENTEENTH = TASK_SEVENTEENTH
103
+ EIGHTEENTH = TASK_EIGHTEENTH
104
+ NINETEENTH = TASK_NINETEENTH
105
+ TWENTIETH = TASK_TWENTIETH
106
+ TWENTY_FIRST = TASK_TWENTY_FIRST
107
+ TWENTY_SECOND = TASK_TWENTY_SECOND
108
+ TWENTY_THIRD = TASK_TWENTY_THIRD
109
+ TWENTY_FOURTH = TASK_TWENTY_FOURTH
110
+ TWENTY_FIFTH = TASK_TWENTY_FIFTH
111
+ TWENTY_SIXTH = TASK_TWENTY_SIXTH
112
+ TWENTY_SEVENTH = TASK_TWENTY_SEVENTH
113
+ TWENTY_EIGHTH = TASK_TWENTY_EIGHTH
114
+ TWENTY_NINTH = TASK_TWENTY_NINTH
115
+ THIRTYETH = TASK_THIRTYETH
116
+ THIRTY_FIRST = TASK_THIRTY_FIRST
117
+ LAST = TASK_LAST
118
+
119
+ # :startdoc:
120
+
121
+ attr_accessor :password
122
+ attr_reader :host
123
+
124
+ def root_path(path = '\\')
125
+ path
126
+ end
127
+
128
+ # Returns a new TaskScheduler object, attached to +folder+. If that
129
+ # folder does not exist, but the +force+ option is set to true, then
130
+ # it will be created. Otherwise an error will be raised. The default
131
+ # is to use the root folder.
132
+ #
133
+ # If +task+ and +trigger+ are present, then a new task is generated
134
+ # as well. This is effectively the same as .new + #new_work_item.
135
+ #
136
+ def initialize(task = nil, trigger = nil, folder = root_path, force = false)
137
+ @folder = folder
138
+ @force = force
139
+
140
+ @host = Socket.gethostname
141
+ @task = nil
142
+ @password = nil
143
+
144
+ raise ArgumentError, "invalid folder" unless folder.include?('\\')
145
+
146
+ unless [TrueClass, FalseClass].include?(force.class)
147
+ raise TypeError, "invalid force value"
148
+ end
149
+
150
+ begin
151
+ @service = WIN32OLE.new("Schedule.Service")
152
+ rescue WIN32OLERuntimeError => err
153
+ raise Error, err.inspect
154
+ end
155
+
156
+ @service.Connect
157
+
158
+ if folder != root_path
159
+ begin
160
+ @root = @service.GetFolder(folder)
161
+ rescue WIN32OLERuntimeError => err
162
+ if force
163
+ @root = @service.GetFolder(root_path)
164
+ @root = @root.CreateFolder(folder)
165
+ else
166
+ raise ArgumentError, "folder '#{folder}' not found"
167
+ end
168
+ end
169
+ else
170
+ @root = @service.GetFolder(folder)
171
+ end
172
+
173
+ new_work_item(task, trigger) if task && trigger
174
+ end
175
+
176
+ # Returns an array of scheduled task names.
177
+ #
178
+ def enum
179
+ # Get the task folder that contains the tasks.
180
+ taskCollection = @root.GetTasks(0)
181
+
182
+ array = []
183
+
184
+ taskCollection.each do |registeredTask|
185
+ array << registeredTask.Name
186
+ end
187
+
188
+ array
189
+ end
190
+
191
+ alias tasks enum
192
+
193
+ # Returns whether or not the specified task exists.
194
+ #
195
+ def exists?(full_task_path)
196
+ path = nil
197
+ task_name = nil
198
+
199
+ if full_task_path.include?('\\')
200
+ *path, task_name = full_task_path.split('\\')
201
+ else
202
+ task_name = full_task_path
203
+ end
204
+
205
+ folder = path.nil? ? root_path : path.join('\\')
206
+
207
+ begin
208
+ root = @service.GetFolder(folder)
209
+ rescue WIN32OLERuntimeError => err
210
+ return false
211
+ end
212
+
213
+ if root.nil?
214
+ return false
215
+ else
216
+ begin
217
+ task = root.GetTask(task_name)
218
+ return task && task.Name == task_name
219
+ rescue WIN32OLERuntimeError => err
220
+ return false
221
+ end
222
+ end
223
+ end
224
+
225
+ # Return the sepcified task if exist
226
+ #
227
+ def get_task(task)
228
+ raise TypeError unless task.is_a?(String)
229
+
230
+ begin
231
+ registeredTask = @root.GetTask(task)
232
+ @task = registeredTask
233
+ rescue WIN32OLERuntimeError => err
234
+ raise Error, ole_error("activate", err)
235
+ end
236
+ end
237
+
238
+ # Activate the specified task.
239
+ #
240
+ def activate(task)
241
+ raise TypeError unless task.is_a?(String)
242
+
243
+ begin
244
+ registeredTask = @root.GetTask(task)
245
+ registeredTask.Enabled = 1
246
+ @task = registeredTask
247
+ rescue WIN32OLERuntimeError => err
248
+ raise Error, ole_error("activate", err)
249
+ end
250
+ end
251
+
252
+ # Delete the specified task name.
253
+ #
254
+ def delete(task)
255
+ raise TypeError unless task.is_a?(String)
256
+
257
+ begin
258
+ @root.DeleteTask(task, 0)
259
+ rescue WIN32OLERuntimeError => err
260
+ raise Error, ole_error("DeleteTask", err)
261
+ end
262
+ end
263
+
264
+ # Execute the current task.
265
+ #
266
+ def run
267
+ check_for_active_task
268
+ @task.run(nil)
269
+ end
270
+
271
+ # This method no longer has any effect. It is a no-op that remains for
272
+ # backwards compatibility. It will be removed in 0.4.0.
273
+ #
274
+ def save(_file = nil)
275
+ warn DeprecatedMethodWarning, "this method is no longer necessary"
276
+ check_for_active_task
277
+ # Do nothing, deprecated.
278
+ end
279
+
280
+ # Terminate (stop) the current task.
281
+ #
282
+ def terminate
283
+ check_for_active_task
284
+ @task.stop(nil)
285
+ end
286
+
287
+ alias stop terminate
288
+
289
+ # Set the host on which the various TaskScheduler methods will execute.
290
+ # This method may require administrative privileges.
291
+ #
292
+ def machine=(host)
293
+ raise TypeError unless host.is_a?(String)
294
+
295
+ begin
296
+ @service.Connect(host)
297
+ rescue WIN32OLERuntimeError => err
298
+ raise Error, ole_error("Connect", err)
299
+ end
300
+
301
+ @host = host
302
+ host
303
+ end
304
+
305
+ # Similar to the TaskScheduler#machine= method, this method also allows
306
+ # you to pass a user, domain and password as needed. This method may
307
+ # require administrative privileges.
308
+ #
309
+ def set_machine(host, user = nil, domain = nil, password = nil)
310
+ raise TypeError unless host.is_a?(String)
311
+
312
+ begin
313
+ @service.Connect(host, user, domain, password)
314
+ rescue WIN32OLERuntimeError => err
315
+ raise Error, ole_error("Connect", err)
316
+ end
317
+
318
+ @host = host
319
+ host
320
+ end
321
+
322
+ alias host= machine=
323
+ alias machine host
324
+ alias set_host set_machine
325
+
326
+ # Sets the +user+ and +password+ for the given task. If the user and
327
+ # password are set properly then true is returned.
328
+ # throws TypeError if password is not provided for other than system users
329
+ def set_account_information(user_id, password, interactive)
330
+ @interactive ||= interactive
331
+ check_credential_requirements(user_id, password)
332
+ check_for_active_task
333
+ @password = password
334
+ register_task_definition(@task.Definition, @task.Path, user_id, password)
335
+ true
336
+ end
337
+
338
+ # Returns the user or group associated with the task or nil if no one has been associated with the task yet
339
+ #
340
+ # @return [String] user or group associated with the task
341
+ #
342
+ def account_information
343
+ if @task.nil?
344
+ nil
345
+ elsif !@task.Definition.Principal.UserId.empty?
346
+ @task.Definition.Principal.UserId
347
+ else
348
+ @task.Definition.Principal.GroupId
349
+ end
350
+ end
351
+
352
+ # Returns the name of the application associated with the task. If
353
+ # no application is associated with the task then nil is returned.
354
+ #
355
+ def application_name
356
+ check_for_active_task
357
+
358
+ app = nil
359
+
360
+ @task.Definition.Actions.each do |action|
361
+ if action.Type == 0 # TASK_ACTION_EXEC
362
+ app = action.Path
363
+ break
364
+ end
365
+ end
366
+
367
+ app
368
+ end
369
+
370
+ # Sets the name of the application associated with the task.
371
+ #
372
+ def application_name=(app)
373
+ raise TypeError unless app.is_a?(String)
374
+ check_for_active_task
375
+
376
+ definition = @task.Definition
377
+
378
+ definition.Actions.each do |action|
379
+ action.Path = app if action.Type == 0
380
+ end
381
+
382
+ register_task_definition(definition)
383
+
384
+ app
385
+ end
386
+
387
+ # Returns the command line parameters for the task.
388
+ #
389
+ def parameters
390
+ check_for_active_task
391
+
392
+ param = nil
393
+
394
+ @task.Definition.Actions.each do |action|
395
+ param = action.Arguments if action.Type == 0
396
+ end
397
+
398
+ param
399
+ end
400
+
401
+ # Sets the parameters for the task. These parameters are passed as command
402
+ # line arguments to the application the task will run. To clear the command
403
+ # line parameters set it to an empty string.
404
+ #--
405
+ # NOTE: Again, it seems the task must be reactivated to be picked up.
406
+ #
407
+ def parameters=(param)
408
+ raise TypeError unless param.is_a?(String)
409
+ check_for_active_task
410
+
411
+ definition = @task.Definition
412
+
413
+ definition.Actions.each do |action|
414
+ action.Arguments = param if action.Type == 0
415
+ end
416
+
417
+ register_task_definition(definition)
418
+
419
+ param
420
+ end
421
+
422
+ # Returns the working directory for the task.
423
+ #
424
+ def working_directory
425
+ check_for_active_task
426
+
427
+ dir = nil
428
+
429
+ @task.Definition.Actions.each do |action|
430
+ dir = action.WorkingDirectory if action.Type == 0
431
+ end
432
+
433
+ dir
434
+ end
435
+
436
+ # Sets the working directory for the task.
437
+ #--
438
+ # TODO: Why do I have to reactivate the task to see the change?
439
+ #
440
+ def working_directory=(dir)
441
+ raise TypeError unless dir.is_a?(String)
442
+ check_for_active_task
443
+
444
+ definition = @task.Definition
445
+
446
+ definition.Actions.each do |action|
447
+ action.WorkingDirectory = dir if action.Type == 0
448
+ end
449
+
450
+ register_task_definition(definition)
451
+
452
+ dir
453
+ end
454
+
455
+ # Returns the task's priority level. Possible values are 'idle', 'lowest'.
456
+ # 'below_normal_8', 'below_normal_7', 'normal_6', 'normal_5', 'normal_4',
457
+ # 'above_normal_3', 'above_normal_2', 'highest', 'critical' and 'unknown'.
458
+ #
459
+ def priority
460
+ check_for_active_task
461
+
462
+ priority = case @task.Definition.Settings.Priority
463
+ when 0
464
+ "critical"
465
+ when 1
466
+ "highest"
467
+ when 2
468
+ "above_normal_2"
469
+ when 3
470
+ "above_normal_3"
471
+ when 4
472
+ "normal_4"
473
+ when 5
474
+ "normal_5"
475
+ when 6
476
+ "normal_6"
477
+ when 7
478
+ "below_normal_7"
479
+ when 8
480
+ "below_normal_8"
481
+ when 9
482
+ "lowest"
483
+ when 10
484
+ "idle"
485
+ else
486
+ "unknown"
487
+ end
488
+
489
+ priority
490
+ end
491
+
492
+ # Sets the priority of the task. The +priority+ should be a numeric
493
+ # priority constant value.
494
+ #
495
+ def priority=(priority)
496
+ raise TypeError unless priority.is_a?(Numeric)
497
+ check_for_active_task
498
+
499
+ definition = @task.Definition
500
+ definition.Settings.Priority = priority
501
+
502
+ register_task_definition(definition)
503
+
504
+ priority
505
+ end
506
+
507
+ # Creates a new work item (scheduled job) with the given +trigger+. The
508
+ # trigger variable is a hash of options that define when the scheduled
509
+ # job should run.
510
+ #
511
+ def new_work_item(task, trigger, userinfo = { user: nil, password: nil, interactive: false })
512
+ raise TypeError unless userinfo.is_a?(Hash) && task.is_a?(String) && trigger.is_a?(Hash)
513
+
514
+ # If user ID is not given, consider it as a 'SYSTEM' user
515
+ userinfo[:user] = SERVICE_ACCOUNT_USERS.first if userinfo[:user].to_s.empty?
516
+ @password = userinfo[:password]
517
+ @interactive = userinfo[:interactive]
518
+
519
+ check_credential_requirements(userinfo[:user], userinfo[:password])
520
+
521
+ taskDefinition = @service.NewTask(0)
522
+ taskDefinition.RegistrationInfo.Description = ""
523
+ taskDefinition.RegistrationInfo.Author = ""
524
+ taskDefinition.Settings.StartWhenAvailable = false
525
+ taskDefinition.Settings.Enabled = true
526
+ taskDefinition.Settings.Hidden = false
527
+
528
+ unless trigger.empty?
529
+ raise ArgumentError, "Unknown trigger type" unless valid_trigger_option(trigger[:trigger_type])
530
+ validate_trigger(trigger)
531
+
532
+ startTime = format("%04d-%02d-%02dT%02d:%02d:00", trigger[:start_year], trigger[:start_month], trigger[:start_day], trigger[:start_hour], trigger[:start_minute])
533
+
534
+ # Set defaults
535
+ trigger[:end_year] ||= 0
536
+ trigger[:end_month] ||= 0
537
+ trigger[:end_day] ||= 0
538
+
539
+ endTime = format("%04d-%02d-%02dT00:00:00", trigger[:end_year], trigger[:end_month], trigger[:end_day])
540
+
541
+ trig = taskDefinition.Triggers.Create(trigger[:trigger_type].to_i)
542
+ trig.Id = "RegistrationTriggerId#{taskDefinition.Triggers.Count}"
543
+ trig.StartBoundary = startTime if startTime != "0000-00-00T00:00:00"
544
+ trig.EndBoundary = endTime if endTime != "0000-00-00T00:00:00"
545
+ trig.Enabled = true
546
+
547
+ repetitionPattern = trig.Repetition
548
+
549
+ if trigger[:minutes_duration].to_i > 0
550
+ repetitionPattern.Duration = "PT#{trigger[:minutes_duration] || 0}M"
551
+ end
552
+
553
+ if trigger[:minutes_interval].to_i > 0
554
+ repetitionPattern.Interval = "PT#{trigger[:minutes_interval] || 0}M"
555
+ end
556
+
557
+ tmp = trigger[:type]
558
+ tmp = nil unless tmp.is_a?(Hash)
559
+
560
+ case trigger[:trigger_type]
561
+ when TASK_TIME_TRIGGER_DAILY
562
+ trig.DaysInterval = tmp[:days_interval] if tmp && tmp[:days_interval]
563
+ if trigger[:random_minutes_interval].to_i > 0
564
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval]}M"
565
+ end
566
+ when TASK_TIME_TRIGGER_WEEKLY
567
+ trig.DaysOfWeek = tmp[:days_of_week] if tmp && tmp[:days_of_week]
568
+ trig.WeeksInterval = tmp[:weeks_interval] if tmp && tmp[:weeks_interval]
569
+ if trigger[:random_minutes_interval].to_i > 0
570
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
571
+ end
572
+ when TASK_TIME_TRIGGER_MONTHLYDATE
573
+ trig.MonthsOfYear = tmp[:months] if tmp && tmp[:months]
574
+ trig.DaysOfMonth = tmp[:days] if tmp && tmp[:days]
575
+ if trigger[:random_minutes_interval].to_i > 0
576
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
577
+ end
578
+ trig.RunOnLastDayOfMonth = trigger[:run_on_last_day_of_month] if trigger[:run_on_last_day_of_month]
579
+ when TASK_TIME_TRIGGER_MONTHLYDOW
580
+ trig.MonthsOfYear = tmp[:months] if tmp && tmp[:months]
581
+ trig.DaysOfWeek = tmp[:days_of_week] if tmp && tmp[:days_of_week]
582
+ trig.WeeksOfMonth = tmp[:weeks_of_month] if tmp && tmp[:weeks_of_month]
583
+ if trigger[:random_minutes_interval].to_i > 0
584
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
585
+ end
586
+ trig.RunOnLastWeekOfMonth = trigger[:run_on_last_week_of_month] if trigger[:run_on_last_week_of_month]
587
+ when TASK_TIME_TRIGGER_ONCE
588
+ if trigger[:random_minutes_interval].to_i > 0
589
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
590
+ end
591
+ when TASK_EVENT_TRIGGER_AT_SYSTEMSTART
592
+ trig.Delay = "PT#{trigger[:delay_duration] || 0}M"
593
+ when TASK_EVENT_TRIGGER_AT_LOGON
594
+ trig.UserId = trigger[:user_id] if trigger[:user_id]
595
+ trig.Delay = "PT#{trigger[:delay_duration] || 0}M"
596
+ end
597
+ end
598
+
599
+ act = taskDefinition.Actions.Create(0)
600
+ act.Path = "cmd"
601
+
602
+ register_task_definition(taskDefinition, task, userinfo[:user], userinfo[:password])
603
+
604
+ @task = @root.GetTask(task)
605
+ end
606
+
607
+ alias new_task new_work_item
608
+
609
+ # Returns the number of triggers associated with the active task.
610
+ #
611
+ def trigger_count
612
+ raise Error, "No currently active task" if @task.nil?
613
+
614
+ @task.Definition.Triggers.Count
615
+ end
616
+
617
+ # Returns a string that describes the current trigger at the specified
618
+ # index for the active task.
619
+ #
620
+ # Example: "At 7:14 AM every day, starting 4/11/2015"
621
+ #
622
+ def trigger_string(index)
623
+ raise TypeError unless index.is_a?(Numeric)
624
+ check_for_active_task
625
+ index += 1 # first item index is 1
626
+
627
+ begin
628
+ trigger = @task.Definition.Triggers.Item(index)
629
+ rescue WIN32OLERuntimeError
630
+ raise Error, "No trigger found at index '#{index}'"
631
+ end
632
+
633
+ "Starting #{trigger.StartBoundary}"
634
+ end
635
+
636
+ # Deletes the trigger at the specified index.
637
+ #--
638
+ # TODO: Fix.
639
+ #
640
+ def delete_trigger(index)
641
+ raise TypeError unless index.is_a?(Numeric)
642
+ check_for_active_task
643
+ index += 1 # first item index is 1
644
+
645
+ definition = @task.Definition
646
+ definition.Triggers.Remove(index)
647
+ register_task_definition(definition)
648
+
649
+ index
650
+ end
651
+
652
+ # Returns a hash that describes the trigger at the given index for the
653
+ # current task.
654
+ #
655
+ def trigger(index)
656
+ raise TypeError unless index.is_a?(Numeric)
657
+ check_for_active_task
658
+ index += 1 # first item index is 1
659
+
660
+ begin
661
+ trig = @task.Definition.Triggers.Item(index)
662
+ rescue WIN32OLERuntimeError => err
663
+ raise Error, ole_error("Item", err)
664
+ end
665
+
666
+ trigger = {}
667
+
668
+ case trig.Type
669
+ when TASK_TIME_TRIGGER_DAILY
670
+ tmp = {}
671
+ tmp[:days_interval] = trig.DaysInterval
672
+ trigger[:type] = tmp
673
+ trigger[:random_minutes_interval] = time_in_minutes(trig.RandomDelay)
674
+ when TASK_TIME_TRIGGER_WEEKLY
675
+ tmp = {}
676
+ tmp[:weeks_interval] = trig.WeeksInterval
677
+ tmp[:days_of_week] = trig.DaysOfWeek
678
+ trigger[:type] = tmp
679
+ trigger[:random_minutes_interval] = time_in_minutes(trig.RandomDelay)
680
+ when TASK_TIME_TRIGGER_MONTHLYDATE
681
+ tmp = {}
682
+ tmp[:months] = trig.MonthsOfYear
683
+ tmp[:days] = trig.DaysOfMonth
684
+ trigger[:type] = tmp
685
+ trigger[:random_minutes_interval] = time_in_minutes(trig.RandomDelay)
686
+ trigger[:run_on_last_day_of_month] = trig.RunOnLastDayOfMonth
687
+ when TASK_TIME_TRIGGER_MONTHLYDOW
688
+ tmp = {}
689
+ tmp[:months] = trig.MonthsOfYear
690
+ tmp[:days_of_week] = trig.DaysOfWeek
691
+ tmp[:weeks_of_month] = trig.WeeksOfMonth
692
+ trigger[:type] = tmp
693
+ trigger[:random_minutes_interval] = time_in_minutes(trig.RandomDelay)
694
+ trigger[:run_on_last_week_of_month] = trig.RunOnLastWeekOfMonth
695
+ when TASK_TIME_TRIGGER_ONCE
696
+ tmp = {}
697
+ tmp[:once] = nil
698
+ trigger[:type] = tmp
699
+ trigger[:random_minutes_interval] = time_in_minutes(trig.RandomDelay)
700
+ when TASK_EVENT_TRIGGER_AT_SYSTEMSTART
701
+ trigger[:delay_duration] = time_in_minutes(trig.Delay)
702
+ when TASK_EVENT_TRIGGER_AT_LOGON
703
+ trigger[:user_id] = trig.UserId if trig.UserId.to_s != ""
704
+ trigger[:delay_duration] = time_in_minutes(trig.Delay)
705
+ when TASK_EVENT_TRIGGER_ON_IDLE
706
+ trigger[:execution_time_limit] = time_in_minutes(trig.ExecutionTimeLimit)
707
+ else
708
+ raise Error, "Unknown trigger type"
709
+ end
710
+
711
+ trigger[:start_year], trigger[:start_month], trigger[:start_day],
712
+ trigger[:start_hour], trigger[:start_minute] = trig.StartBoundary.scan(/(\d+)-(\d+)-(\d+)T(\d+):(\d+)/).first
713
+
714
+ trigger[:end_year], trigger[:end_month],
715
+ trigger[:end_day] = trig.EndBoundary.scan(/(\d+)-(\d+)-(\d+)T/).first
716
+
717
+ trigger[:minutes_duration] = time_in_minutes(trig.Repetition.Duration)
718
+ trigger[:minutes_interval] = time_in_minutes(trig.Repetition.Interval)
719
+ trigger[:trigger_type] = trig.Type
720
+
721
+ trigger
722
+ end
723
+
724
+ # Sets the trigger for the currently active task. The +trigger+ is a hash
725
+ # with the following possible options:
726
+ #
727
+ # * days
728
+ # * days_interval
729
+ # * days_of_week
730
+ # * end_day
731
+ # * end_month
732
+ # * end_year
733
+ # * flags
734
+ # * minutes_duration
735
+ # * minutes_interval
736
+ # * months
737
+ # * random_minutes_interval
738
+ # * start_day
739
+ # * start_hour
740
+ # * start_minute
741
+ # * start_month
742
+ # * start_year
743
+ # * trigger_type
744
+ # * type
745
+ # * weeks
746
+ # * weeks_interval
747
+ #
748
+ def trigger=(trigger)
749
+ raise TypeError unless trigger.is_a?(Hash)
750
+ raise ArgumentError, "Unknown trigger type" unless valid_trigger_option(trigger[:trigger_type])
751
+
752
+ check_for_active_task
753
+
754
+ validate_trigger(trigger)
755
+
756
+ definition = @task.Definition
757
+ definition.Triggers.Clear()
758
+
759
+ startTime = format("%04d-%02d-%02dT%02d:%02d:00", trigger[:start_year], trigger[:start_month], trigger[:start_day], trigger[:start_hour], trigger[:start_minute])
760
+
761
+ endTime = format("%04d-%02d-%02dT00:00:00", trigger[:end_year], trigger[:end_month], trigger[:end_day])
762
+
763
+ trig = definition.Triggers.Create(trigger[:trigger_type].to_i)
764
+ trig.Id = "RegistrationTriggerId#{definition.Triggers.Count}"
765
+ trig.StartBoundary = startTime if startTime != "0000-00-00T00:00:00"
766
+ trig.EndBoundary = endTime if endTime != "0000-00-00T00:00:00"
767
+ trig.Enabled = true
768
+
769
+ repetitionPattern = trig.Repetition
770
+
771
+ if trigger[:minutes_duration].to_i > 0
772
+ repetitionPattern.Duration = "PT#{trigger[:minutes_duration] || 0}M"
773
+ end
774
+
775
+ if trigger[:minutes_interval].to_i > 0
776
+ repetitionPattern.Interval = "PT#{trigger[:minutes_interval] || 0}M"
777
+ end
778
+
779
+ tmp = trigger[:type]
780
+ tmp = nil unless tmp.is_a?(Hash)
781
+
782
+ case trigger[:trigger_type]
783
+ when TASK_TIME_TRIGGER_DAILY
784
+ trig.DaysInterval = tmp[:days_interval] if tmp && tmp[:days_interval]
785
+ if trigger[:random_minutes_interval].to_i > 0
786
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval]}M"
787
+ end
788
+ when TASK_TIME_TRIGGER_WEEKLY
789
+ trig.DaysOfWeek = tmp[:days_of_week] if tmp && tmp[:days_of_week]
790
+ trig.WeeksInterval = tmp[:weeks_interval] if tmp && tmp[:weeks_interval]
791
+ if trigger[:random_minutes_interval].to_i > 0
792
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
793
+ end
794
+ when TASK_TIME_TRIGGER_MONTHLYDATE
795
+ trig.MonthsOfYear = tmp[:months] if tmp && tmp[:months]
796
+ trig.DaysOfMonth = tmp[:days] if tmp && tmp[:days]
797
+ if trigger[:random_minutes_interval].to_i > 0
798
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
799
+ end
800
+ trig.RunOnLastDayOfMonth = trigger[:run_on_last_day_of_month] if trigger[:run_on_last_day_of_month]
801
+ when TASK_TIME_TRIGGER_MONTHLYDOW
802
+ trig.MonthsOfYear = tmp[:months] if tmp && tmp[:months]
803
+ trig.DaysOfWeek = tmp[:days_of_week] if tmp && tmp[:days_of_week]
804
+ trig.WeeksOfMonth = tmp[:weeks_of_month] if tmp && tmp[:weeks_of_month]
805
+ if trigger[:random_minutes_interval].to_i > 0
806
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
807
+ end
808
+ trig.RunOnLastWeekOfMonth = trigger[:run_on_last_week_of_month] if trigger[:run_on_last_week_of_month]
809
+ when TASK_TIME_TRIGGER_ONCE
810
+ if trigger[:random_minutes_interval].to_i > 0
811
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
812
+ end
813
+ when TASK_EVENT_TRIGGER_AT_SYSTEMSTART
814
+ trig.Delay = "PT#{trigger[:delay_duration] || 0}M"
815
+ when TASK_EVENT_TRIGGER_AT_LOGON
816
+ trig.UserId = trigger[:user_id] if trigger[:user_id]
817
+ trig.Delay = "PT#{trigger[:delay_duration] || 0}M"
818
+ when TASK_EVENT_TRIGGER_ON_IDLE
819
+ # for setting execution time limit Ref : https://msdn.microsoft.com/en-us/library/windows/desktop/aa380724(v=vs.85).aspx
820
+ if trigger[:execution_time_limit].to_i > 0
821
+ trig.ExecutionTimeLimit = "PT#{trigger[:execution_time_limit] || 0}M"
822
+ end
823
+ end
824
+
825
+ register_task_definition(definition)
826
+
827
+ trigger
828
+ end
829
+
830
+ # Adds a trigger at the specified index.
831
+ #
832
+ def add_trigger(index, trigger)
833
+ raise TypeError unless index.is_a?(Numeric)
834
+ raise TypeError unless trigger.is_a?(Hash)
835
+ raise ArgumentError, "Unknown trigger type" unless valid_trigger_option(trigger[:trigger_type])
836
+
837
+ check_for_active_task
838
+
839
+ definition = @task.Definition
840
+
841
+ startTime = format("%04d-%02d-%02dT%02d:%02d:00", trigger[:start_year], trigger[:start_month], trigger[:start_day], trigger[:start_hour], trigger[:start_minute])
842
+
843
+ # Set defaults
844
+ trigger[:end_year] ||= 0
845
+ trigger[:end_month] ||= 0
846
+ trigger[:end_day] ||= 0
847
+
848
+ endTime = format("%04d-%02d-%02dT00:00:00", trigger[:end_year], trigger[:end_month], trigger[:end_day])
849
+
850
+ trig = definition.Triggers.Create(trigger[:trigger_type].to_i)
851
+ trig.Id = "RegistrationTriggerId#{definition.Triggers.Count}"
852
+ trig.StartBoundary = startTime if startTime != "0000-00-00T00:00:00"
853
+ trig.EndBoundary = endTime if endTime != "0000-00-00T00:00:00"
854
+ trig.Enabled = true
855
+
856
+ repetitionPattern = trig.Repetition
857
+
858
+ if trigger[:minutes_duration].to_i > 0
859
+ repetitionPattern.Duration = "PT#{trigger[:minutes_duration] || 0}M"
860
+ end
861
+
862
+ if trigger[:minutes_interval].to_i > 0
863
+ repetitionPattern.Interval = "PT#{trigger[:minutes_interval] || 0}M"
864
+ end
865
+
866
+ tmp = trigger[:type]
867
+ tmp = nil unless tmp.is_a?(Hash)
868
+
869
+ case trigger[:trigger_type]
870
+ when TASK_TIME_TRIGGER_DAILY
871
+ trig.DaysInterval = tmp[:days_interval] if tmp && tmp[:days_interval]
872
+ if trigger[:random_minutes_interval].to_i > 0
873
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval]}M"
874
+ end
875
+ when TASK_TIME_TRIGGER_WEEKLY
876
+ trig.DaysOfWeek = tmp[:days_of_week] if tmp && tmp[:days_of_week]
877
+ trig.WeeksInterval = tmp[:weeks_interval] if tmp && tmp[:weeks_interval]
878
+ if trigger[:random_minutes_interval].to_i > 0
879
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
880
+ end
881
+ when TASK_TIME_TRIGGER_MONTHLYDATE
882
+ trig.MonthsOfYear = tmp[:months] if tmp && tmp[:months]
883
+ trig.DaysOfMonth = tmp[:days] if tmp && tmp[:days]
884
+ if trigger[:random_minutes_interval].to_i > 0
885
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
886
+ end
887
+ trig.RunOnLastDayOfMonth = trigger[:run_on_last_day_of_month] if trigger[:run_on_last_day_of_month]
888
+ when TASK_TIME_TRIGGER_MONTHLYDOW
889
+ trig.MonthsOfYear = tmp[:months] if tmp && tmp[:months]
890
+ trig.DaysOfWeek = tmp[:days_of_week] if tmp && tmp[:days_of_week]
891
+ trig.WeeksOfMonth = tmp[:weeks_of_month] if tmp && tmp[:weeks_of_month]
892
+ if trigger[:random_minutes_interval].to_i > 0
893
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
894
+ end
895
+ trig.RunOnLastWeekOfMonth = trigger[:run_on_last_week_of_month] if trigger[:run_on_last_week_of_month]
896
+ when TASK_TIME_TRIGGER_ONCE
897
+ if trigger[:random_minutes_interval].to_i > 0
898
+ trig.RandomDelay = "PT#{trigger[:random_minutes_interval] || 0}M"
899
+ end
900
+ when TASK_EVENT_TRIGGER_AT_SYSTEMSTART
901
+ trig.Delay = "PT#{trigger[:delay_duration] || 0}M"
902
+ when TASK_EVENT_TRIGGER_AT_LOGON
903
+ trig.UserId = trigger[:user_id] if trigger[:user_id]
904
+ trig.Delay = "PT#{trigger[:delay_duration] || 0}M"
905
+ end
906
+
907
+ register_task_definition(definition)
908
+
909
+ true
910
+ end
911
+
912
+ # Returns the status of the currently active task. Possible values are
913
+ # 'ready', 'running', 'not scheduled' or 'unknown'.
914
+ #
915
+ def status
916
+ check_for_active_task
917
+
918
+ status = case @task.State
919
+ when 3
920
+ "ready"
921
+ when 4
922
+ "running"
923
+ when 2
924
+ "queued"
925
+ when 1
926
+ "not scheduled"
927
+ else
928
+ "unknown"
929
+ end
930
+
931
+ status
932
+ end
933
+
934
+ # Returns true if current task is enabled
935
+ def enabled?
936
+ check_for_active_task
937
+ @task.enabled
938
+ end
939
+
940
+ # Returns the exit code from the last scheduled run.
941
+ #
942
+ def exit_code
943
+ check_for_active_task
944
+ @task.LastTaskResult
945
+ end
946
+
947
+ # Returns the comment associated with the task, if any.
948
+ #
949
+ def comment
950
+ check_for_active_task
951
+ @task.Definition.RegistrationInfo.Description
952
+ end
953
+
954
+ alias description comment
955
+
956
+ # Sets the comment for the task.
957
+ #
958
+ def comment=(comment)
959
+ raise TypeError unless comment.is_a?(String)
960
+ check_for_active_task
961
+
962
+ definition = @task.Definition
963
+ definition.RegistrationInfo.Description = comment
964
+ register_task_definition(definition)
965
+
966
+ comment
967
+ end
968
+
969
+ alias description= comment=
970
+
971
+ # Returns the name of the user who created the task.
972
+ #
973
+ def creator
974
+ check_for_active_task
975
+ @task.Definition.RegistrationInfo.Author
976
+ end
977
+
978
+ alias author creator
979
+
980
+ # Sets the creator for the task.
981
+ #
982
+ def creator=(creator)
983
+ raise TypeError unless creator.is_a?(String)
984
+ check_for_active_task
985
+
986
+ definition = @task.Definition
987
+ definition.RegistrationInfo.Author = creator
988
+ register_task_definition(definition)
989
+
990
+ creator
991
+ end
992
+
993
+ alias author= creator=
994
+
995
+ # Returns a Time object that indicates the next time the task will run.
996
+ #
997
+ def next_run_time
998
+ check_for_active_task
999
+ @task.NextRunTime
1000
+ end
1001
+
1002
+ # Returns a Time object indicating the most recent time the task ran or
1003
+ # nil if the task has never run.
1004
+ #
1005
+ def most_recent_run_time
1006
+ check_for_active_task
1007
+
1008
+ time = nil
1009
+
1010
+ begin
1011
+ time = Time.parse(@task.LastRunTime)
1012
+ rescue StandardError
1013
+ # Ignore
1014
+ end
1015
+
1016
+ time
1017
+ end
1018
+
1019
+ # Returns the execution time limit for current active task
1020
+ #
1021
+ def execution_time_limit
1022
+ check_for_active_task
1023
+ @task.Definition.Settings.ExecutionTimeLimit
1024
+ end
1025
+
1026
+ # Returns the maximum length of time, in milliseconds, that the task
1027
+ # will run before terminating.
1028
+ #
1029
+ def max_run_time
1030
+ check_for_active_task
1031
+
1032
+ t = @task.Definition.Settings.ExecutionTimeLimit
1033
+ year = t.scan(/(\d+?)Y/).flatten.first
1034
+ month = t.scan(/(\d+?)M/).flatten.first
1035
+ day = t.scan(/(\d+?)D/).flatten.first
1036
+ hour = t.scan(/(\d+?)H/).flatten.first
1037
+ min = t.scan(/T.*(\d+?)M/).flatten.first
1038
+ sec = t.scan(/(\d+?)S/).flatten.first
1039
+
1040
+ time = 0
1041
+ time += year.to_i * 365 if year
1042
+ time += month.to_i * 30 if month
1043
+ time += day.to_i if day
1044
+ time *= 24
1045
+ time += hour.to_i if hour
1046
+ time *= 60
1047
+ time += min.to_i if min
1048
+ time *= 60
1049
+ time += sec.to_i if sec
1050
+ time *= 1000
1051
+
1052
+ time
1053
+ end
1054
+
1055
+ # Sets the maximum length of time, in milliseconds, that the task can run
1056
+ # before terminating. Returns the value you specified if successful.
1057
+ #
1058
+ def max_run_time=(max_run_time)
1059
+ raise TypeError unless max_run_time.is_a?(Numeric)
1060
+ check_for_active_task
1061
+
1062
+ t = max_run_time
1063
+ t /= 1000
1064
+ limit = "PT#{t}S"
1065
+
1066
+ definition = @task.Definition
1067
+ definition.Settings.ExecutionTimeLimit = limit
1068
+ register_task_definition(definition)
1069
+
1070
+ max_run_time
1071
+ end
1072
+
1073
+ # The Idle settings of a task
1074
+ #
1075
+ # @see https://docs.microsoft.com/en-us/windows/desktop/TaskSchd/idlesettings#properties
1076
+ #
1077
+ IdleSettings = %i{idle_duration restart_on_idle stop_on_idle_end wait_timeout}.freeze
1078
+
1079
+ # Configures tasks settings
1080
+ #
1081
+ # @param [Hash] settings_hash The settings to configure a task
1082
+ # @option settings_hash [Boolean] :allow_demand_start The subject
1083
+ # @option settings_hash [Boolean] :allow_hard_terminate
1084
+ # @option settings_hash [Boolean] :disallow_start_if_on_batteries
1085
+ # @option settings_hash [Boolean] :disallow_start_on_remote_app_session
1086
+ # @option settings_hash [Boolean] :enabled
1087
+ # @option settings_hash [Boolean] :hidden
1088
+ # @option settings_hash [Boolean] :run_only_if_idle
1089
+ # @option settings_hash [Boolean] :run_only_if_network_available
1090
+ # @option settings_hash [Boolean] :start_when_available
1091
+ # @option settings_hash [Boolean] :stop_if_going_on_batteries
1092
+ # @option settings_hash [Boolean] :use_unified_scheduling_engine
1093
+ # @option settings_hash [Boolean] :volatile
1094
+ # @option settings_hash [Boolean] :wake_to_run
1095
+ # @option settings_hash [Boolean] :restart_on_idle The Idle Setting
1096
+ # @option settings_hash [Boolean] :stop_on_idle_end The Idle Setting
1097
+ # @option settings_hash [Integer] :compatibility
1098
+ # @option settings_hash [Integer] :multiple_instances
1099
+ # @option settings_hash [Integer] :priority
1100
+ # @option settings_hash [Integer] :restart_count
1101
+ # @option settings_hash [String] :delete_expired_task_after
1102
+ # @option settings_hash [String] :execution_time_limit
1103
+ # @option settings_hash [String] :restart_interval
1104
+ # @option settings_hash [String] :idle_duration The Idle Setting
1105
+ # @option settings_hash [String] :wait_timeout The Idle Setting
1106
+ #
1107
+ # @return [Hash] User input
1108
+ #
1109
+ # @see https://msdn.microsoft.com/en-us/library/windows/desktop/aa383480(v=vs.85).aspx#properties
1110
+ #
1111
+ def configure_settings(settings_hash)
1112
+ raise TypeError, "User input settings are required in hash" unless settings_hash.is_a?(Hash)
1113
+
1114
+ check_for_active_task
1115
+ definition = @task.Definition
1116
+
1117
+ # Check for invalid setting
1118
+ invalid_settings = settings_hash.keys - valid_settings_options
1119
+ raise TypeError, "Invalid setting passed: #{invalid_settings.join(', ')}" unless invalid_settings.empty?
1120
+
1121
+ # Some modification is required in user input
1122
+ hash = settings_hash.dup
1123
+
1124
+ # Conversion of few settings
1125
+ hash[:execution_time_limit] = hash[:max_run_time] unless hash[:max_run_time].nil?
1126
+ %i{execution_time_limit idle_duration restart_interval wait_timeout}.each do |setting|
1127
+ hash[setting] = "PT#{hash[setting]}M" unless hash[setting].nil?
1128
+ end
1129
+
1130
+ task_settings = definition.Settings
1131
+
1132
+ # Some Idle setting needs to be configured
1133
+ if IdleSettings.any? { |setting| hash.key?(setting) }
1134
+ idle_settings = task_settings.IdleSettings
1135
+ IdleSettings.each do |setting|
1136
+ next if hash[setting].nil?
1137
+ idle_settings.setproperty(camelize(setting.to_s), hash[setting])
1138
+ # This setting is not required to be configured now
1139
+ hash.delete(setting)
1140
+ end
1141
+ end
1142
+
1143
+ # XML settings are not to be configured
1144
+ %i{xml_text xml}.map { |x| hash.delete(x) }
1145
+
1146
+ hash.each do |setting, value|
1147
+ setting = camelize(setting.to_s)
1148
+ definition.Settings.setproperty(setting, value)
1149
+ end
1150
+
1151
+ register_task_definition(definition)
1152
+
1153
+ settings_hash
1154
+ end
1155
+
1156
+ # Set registration information options. The possible options are:
1157
+ #
1158
+ # * author
1159
+ # * date
1160
+ # * description (or comment)
1161
+ # * documentation
1162
+ # * security_descriptor (should be a Win32::Security::SID)
1163
+ # * source
1164
+ # * uri
1165
+ # * version
1166
+ # * xml_text (or xml)
1167
+ #
1168
+ # Note that most of these options have standalone methods as well,
1169
+ # e.g. calling ts.configure_registration_info(:author => 'Dan') is
1170
+ # the same as calling ts.author = 'Dan'.
1171
+ #
1172
+ def configure_registration_info(hash)
1173
+ raise TypeError unless hash.is_a?(Hash)
1174
+ check_for_active_task
1175
+
1176
+ definition = @task.Definition
1177
+
1178
+ author = hash[:author]
1179
+ date = hash[:date]
1180
+ description = hash[:description] || hash[:comment]
1181
+ documentation = hash[:documentation]
1182
+ security_descriptor = hash[:security_descriptor]
1183
+ source = hash[:source]
1184
+ uri = hash[:uri]
1185
+ version = hash[:version]
1186
+ xml_text = hash[:xml_text] || hash[:xml]
1187
+
1188
+ definition.RegistrationInfo.Author = author if author
1189
+ definition.RegistrationInfo.Date = date if date
1190
+ definition.RegistrationInfo.Description = description if description
1191
+ definition.RegistrationInfo.Documentation = documentation if documentation
1192
+ definition.RegistrationInfo.SecurityDescriptor = security_descriptor if security_descriptor
1193
+ definition.RegistrationInfo.Source = source if source
1194
+ definition.RegistrationInfo.URI = uri if uri
1195
+ definition.RegistrationInfo.Version = version if version
1196
+ definition.RegistrationInfo.XmlText = xml_text if xml_text
1197
+
1198
+ register_task_definition(definition)
1199
+
1200
+ hash
1201
+ end
1202
+
1203
+ # Sets the principals for current active task. The principal is hash with following possible options.
1204
+ # Expected principal hash: { id: STRING, display_name: STRING, user_id: STRING,
1205
+ # logon_type: INTEGER, group_id: STRING, run_level: INTEGER }
1206
+ #
1207
+ def configure_principals(principals)
1208
+ raise TypeError unless principals.is_a?(Hash)
1209
+ check_for_active_task
1210
+ definition = @task.Definition
1211
+ definition.Principal.Id = principals[:id] if principals[:id].to_s != ""
1212
+ definition.Principal.DisplayName = principals[:display_name] if principals[:display_name].to_s != ""
1213
+ definition.Principal.UserId = principals[:user_id] if principals[:user_id].to_s != ""
1214
+ definition.Principal.LogonType = principals[:logon_type] if principals[:logon_type].to_s != ""
1215
+ definition.Principal.GroupId = principals[:group_id] if principals[:group_id].to_s != ""
1216
+ definition.Principal.RunLevel = principals[:run_level] if principals[:run_level].to_s != ""
1217
+ @interactive = true if principals[:logon_type] == TASK_LOGON_INTERACTIVE_TOKEN
1218
+ register_task_definition(definition)
1219
+ principals
1220
+ end
1221
+
1222
+ # Returns a hash containing all the principal information of the current task
1223
+ def principals
1224
+ check_for_active_task
1225
+ principals_hash = {}
1226
+ @task.Definition.Principal.ole_get_methods.each do |principal|
1227
+ principals_hash[principal.name] = @task.Definition.Principal._getproperty(principal.dispid, [], [])
1228
+ end
1229
+ symbolize_keys(principals_hash)
1230
+ end
1231
+
1232
+ # Returns a hash containing all settings of the current task
1233
+ def settings
1234
+ check_for_active_task
1235
+ settings_hash = {}
1236
+ @task.Definition.Settings.ole_get_methods.each do |setting|
1237
+ next if setting.name == "XmlText" # not needed
1238
+ settings_hash[setting.name] = @task.Definition.Settings._getproperty(setting.dispid, [], [])
1239
+ end
1240
+
1241
+ settings_hash["IdleSettings"] = idle_settings
1242
+ settings_hash["NetworkSettings"] = network_settings
1243
+ symbolize_keys(settings_hash)
1244
+ end
1245
+
1246
+ # Returns a hash of idle settings of the current task
1247
+ def idle_settings
1248
+ check_for_active_task
1249
+ settings_hash = {}
1250
+ @task.Definition.Settings.IdleSettings.ole_get_methods.each do |setting|
1251
+ settings_hash[setting.name] = @task.Definition.Settings.IdleSettings._getproperty(setting.dispid, [], [])
1252
+ end
1253
+ symbolize_keys(settings_hash)
1254
+ end
1255
+
1256
+ # Returns a hash of network settings of the current task
1257
+ def network_settings
1258
+ check_for_active_task
1259
+ settings_hash = {}
1260
+ @task.Definition.Settings.NetworkSettings.ole_get_methods.each do |setting|
1261
+ settings_hash[setting.name] = @task.Definition.Settings.NetworkSettings._getproperty(setting.dispid, [], [])
1262
+ end
1263
+ symbolize_keys(settings_hash)
1264
+ end
1265
+
1266
+ # Returns the user or group associated with the task. If no one is associated, it returns the default user i.e., 'SYSTEM'
1267
+ #
1268
+ # @param [WIN32OLE] definition
1269
+ #
1270
+ # @return [String] user_id
1271
+ #
1272
+ def task_user_id(definition)
1273
+ user_id = definition.Principal.UserId.to_s
1274
+ user_id = definition.Principal.GroupId.to_s if user_id.empty?
1275
+ user_id = SERVICE_ACCOUNT_USERS.first if user_id.empty?
1276
+ user_id
1277
+ end
1278
+
1279
+ private
1280
+
1281
+ # Returns a camle-case string to its underscore format
1282
+ def underscore(string)
1283
+ string.gsub(/([a-z\d])([A-Z])/, '\1_\2'.freeze).downcase
1284
+ end
1285
+
1286
+ # Converts a snake-case string to camel-case format
1287
+ #
1288
+ # @param [String] str
1289
+ #
1290
+ # @return [String] In camel case format
1291
+ #
1292
+ def camelize(str)
1293
+ str.split("_").map(&:capitalize).join
1294
+ end
1295
+
1296
+ # Converts all the keys of a hash to underscored-symbol format
1297
+ def symbolize_keys(hash)
1298
+ hash.each_with_object({}) do |(k, v), h|
1299
+ h[underscore(k.to_s).to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v
1300
+ end
1301
+ end
1302
+
1303
+ def valid_trigger_option(trigger_type)
1304
+ [TASK_TIME_TRIGGER_ONCE, TASK_TIME_TRIGGER_DAILY, TASK_TIME_TRIGGER_WEEKLY,
1305
+ TASK_TIME_TRIGGER_MONTHLYDATE, TASK_TIME_TRIGGER_MONTHLYDOW, TASK_EVENT_TRIGGER_ON_IDLE,
1306
+ TASK_EVENT_TRIGGER_AT_SYSTEMSTART, TASK_EVENT_TRIGGER_AT_LOGON].include?(trigger_type.to_i)
1307
+ end
1308
+
1309
+ def validate_trigger(hash)
1310
+ %i{start_year start_month start_day}.each do |key|
1311
+ raise ArgumentError, "#{key} must be set" unless hash[key]
1312
+ end
1313
+ end
1314
+
1315
+ # Configurable settings options
1316
+ #
1317
+ # @note Logically, this is summation of
1318
+ # * Settings
1319
+ # * IdleSettings - [:idle_settings]
1320
+ # * :max_run_time, :xml
1321
+ #
1322
+ # @return [Array]
1323
+ #
1324
+ def valid_settings_options
1325
+ %i{allow_demand_start allow_hard_terminate compatibility delete_expired_task_after
1326
+ disallow_start_if_on_batteries disallow_start_on_remote_app_session enabled
1327
+ execution_time_limit hidden idle_duration maintenance_settings max_run_time
1328
+ multiple_instances network_settings priority restart_count restart_interval
1329
+ restart_on_idle run_only_if_idle run_only_if_network_available
1330
+ start_when_available stop_if_going_on_batteries stop_on_idle_end
1331
+ use_unified_scheduling_engine volatile wait_timeout wake_to_run xml xml_text}
1332
+ end
1333
+
1334
+ def check_for_active_task
1335
+ raise Error, "No currently active task" if @task.nil?
1336
+ end
1337
+
1338
+ # Checks if the user belongs to service accounts category
1339
+ #
1340
+ # @return [Boolean] True or False
1341
+ #
1342
+ def service_account_user?(user)
1343
+ SERVICE_ACCOUNT_USERS.include?(user.to_s.upcase)
1344
+ end
1345
+
1346
+ # Checks if the user belongs to group accounts category
1347
+ #
1348
+ # @return [Boolean] True or False
1349
+ #
1350
+ def group_user?(user)
1351
+ BUILT_IN_GROUPS.include?(user.to_s.upcase)
1352
+ end
1353
+
1354
+ # Checks if the user belongs to system users category
1355
+ #
1356
+ # @return [Boolean] True or False
1357
+ #
1358
+ def system_user?(user)
1359
+ SYSTEM_USERS.include?(user.to_s.upcase)
1360
+ end
1361
+
1362
+ # System users will not require a password
1363
+ # Other users will require a password if the task is non-interactive.
1364
+ #
1365
+ # @param [String] user_id
1366
+ # @param [String] password
1367
+ #
1368
+ def check_credential_requirements(user_id, password)
1369
+ user_id = user_id.to_s
1370
+ password = password.to_s
1371
+
1372
+ if password.empty?
1373
+ unless system_user?(user_id) || @interactive
1374
+ raise ArgumentError, "Password is required for non-system users"
1375
+ end
1376
+ else
1377
+ if system_user?(user_id)
1378
+ raise ArgumentError, "Password is not required for system users"
1379
+ end
1380
+ end
1381
+ end
1382
+
1383
+ # Returns the applicable flag as per the given users and groups which is used while
1384
+ # RegisterTaskDefinition
1385
+ #
1386
+ # @param [String] user_id
1387
+ # @param [String] password
1388
+ #
1389
+ # @return [Integer] Logon Types
1390
+ #
1391
+ def logon_type(user_id, password)
1392
+ if service_account_user?(user_id)
1393
+ TASK_LOGON_SERVICE_ACCOUNT
1394
+ elsif group_user?(user_id)
1395
+ TASK_LOGON_GROUP
1396
+ elsif !user_id.to_s.empty? && !password.to_s.empty?
1397
+ if @interactive
1398
+ TASK_LOGON_INTERACTIVE_TOKEN
1399
+ else
1400
+ TASK_LOGON_PASSWORD
1401
+ end
1402
+ else
1403
+ TASK_LOGON_INTERACTIVE_TOKEN
1404
+ end
1405
+ end
1406
+
1407
+ # Uses RegisterTaskDefinition and creates or updates the task
1408
+ #
1409
+ # @param [WIN32OLE] definition
1410
+ # @param [String] path
1411
+ # @param [String] user_id
1412
+ # @param [String] password
1413
+ #
1414
+ # @return [Integer] Logon Types
1415
+ #
1416
+ def register_task_definition(definition, path = nil, user_id = nil, password = nil)
1417
+ user_id ||= task_user_id(definition)
1418
+ password ||= @password
1419
+ path ||= @task.Path
1420
+ @task = @root.RegisterTaskDefinition(
1421
+ path, # Path (name) of the task
1422
+ definition, # definition of the task
1423
+ TASK_CREATE_OR_UPDATE, # Equivalent to TASK_CREATE | TASK_UPDATE
1424
+ user_id,
1425
+ password,
1426
+ logon_type(user_id, password)
1427
+ )
1428
+ rescue WIN32OLERuntimeError => err
1429
+ method_name = caller_locations(1, 1)[0].label
1430
+ raise Error, ole_error(method_name, err)
1431
+ end
1432
+ end
1433
+ # :stopdoc:
1434
+ end