win32-taskscheduler 0.2.0 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/win32-taskscheduler.rb +1 -0
- data/lib/win32/taskscheduler.rb +1434 -1677
- data/lib/win32/taskscheduler/constants.rb +266 -0
- data/lib/win32/taskscheduler/helper.rb +51 -0
- data/lib/win32/taskscheduler/sid.rb +132 -0
- data/lib/win32/taskscheduler/time_calc_helper.rb +163 -0
- data/lib/win32/taskscheduler/version.rb +6 -0
- metadata +91 -48
- data/CHANGES +0 -51
- data/MANIFEST +0 -11
- data/README +0 -68
- data/Rakefile +0 -54
- data/doc/taskscheduler.txt +0 -419
- data/examples/taskscheduler_example.rb +0 -56
- data/test/test_taskscheduler.rb +0 -524
- data/win32-taskscheduler.gemspec +0 -36
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"
|
data/lib/win32/taskscheduler.rb
CHANGED
@@ -1,1677 +1,1434 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
module Win32
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
-
|
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
|
-
end
|
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
|
-
|
509
|
-
|
510
|
-
|
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
|
-
|
1072
|
-
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1077
|
-
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
|
1084
|
-
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1089
|
-
|
1090
|
-
|
1091
|
-
|
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
|
-
|
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
|
-
|
1370
|
-
|
1371
|
-
|
1372
|
-
|
1373
|
-
|
1374
|
-
|
1375
|
-
|
1376
|
-
|
1377
|
-
|
1378
|
-
|
1379
|
-
|
1380
|
-
|
1381
|
-
|
1382
|
-
|
1383
|
-
|
1384
|
-
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
|
1394
|
-
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1400
|
-
|
1401
|
-
|
1402
|
-
|
1403
|
-
|
1404
|
-
|
1405
|
-
|
1406
|
-
|
1407
|
-
|
1408
|
-
|
1409
|
-
|
1410
|
-
|
1411
|
-
|
1412
|
-
|
1413
|
-
|
1414
|
-
|
1415
|
-
|
1416
|
-
|
1417
|
-
|
1418
|
-
|
1419
|
-
|
1420
|
-
|
1421
|
-
|
1422
|
-
|
1423
|
-
|
1424
|
-
|
1425
|
-
|
1426
|
-
|
1427
|
-
|
1428
|
-
|
1429
|
-
|
1430
|
-
|
1431
|
-
|
1432
|
-
|
1433
|
-
|
1434
|
-
|
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
|