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