win32-taskscheduler 0.2.0 → 2.0.4

Sign up to get free protection for your applications and to get access to all the features.
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