http://qs1969.pair.com?node_id=1224397

kambe has asked for the wisdom of the Perl Monks concerning the following question:

I am trying to use Win32::OLE to create a task on a Windows 10 system, something similar to href:https://onedrive.live.com/?authkey=%21ABowWrIzVQzcElo&cid=07CD1B37769E1B7D&id=7CD1B37769E1B7D%21102691&parId=7CD1B37769E1B7D%21102690&o=OneUp. I tried to manually validate the query by creating the same task by hand in the task scheduler GUI, and that seemed to work.

Here's my test code:

use File::Basename; use Win32; use Win32::OLE; $Win32::OLE::Warn = 3; use Data::Dumper; my ($me, $dirpath, $suffix) = fileparse($0, qr/\.[^.]*/); my ($system, $login, $domain, $sidbin, $sidtype, $sidtxt) = ""; $login = Win32::LoginName(); Win32::LookupAccountName($system, $login, $domain, $sidbin, $sidtype); my($Revision, $SubAuthorityCount,@IdentifierAuthorities) = unpack("CCn +nn", $sidbin); unless (($IdentifierAuthorities[0] || $IdentifierAuthorities[1])) { my($temp, $temp2, @SubAuthorities) = unpack("VVV$SubAuthorityCount +",$sidbin); $sidtxt = "S-$Revision-$IdentifierAuthorities[2]-".join("-",@SubAu +thorities); } die Win32::OLE->LastError() unless (my $service = Win32::OLE->CreateOb +ject('Schedule.Service')); $service->Connect; my $RootFolder = $service->GetFolder('\\'); die Win32::OLE->LastError() unless (my $TaskDefinition = $service->New +Task(0)); die Win32::OLE->LastError() unless (my $regInfo = $TaskDefinition->Reg +istrationInfo); $regInfo->{Description} = "Register a perl task as an event $me"; $regInfo->{Author} = "$domain\\$login"; $regInfo->{URI} = "$sidtxt\\$me"; die Win32::OLE->LastError() unless (my $settings = $TaskDefinition->Se +ttings); $settings->{Enabled} = 1; $settings->{AllowDemandStart} = 1; $settings->{DisallowStartIfOnBatteries} = 0; $settings->{StopIfGoingOnBatteries} = 0; $settings->{Hidden} = 0; my @Triggers; my $TriggerSet; die Win32::OLE->LastError() unless ($TriggerSet = $TaskDefinition->Tri +ggers); for (10000..10001) { die Win32::OLE->LastError() unless (push @Triggers, $TriggerSet->C +reate(0)); $Triggers[$#Triggers]->{Id} = $_; $Triggers[$#Triggers]->{Subscription} = "<QueryList> <Query Id=\"event$_\" Path=\"Microsoft-Windows-NetworkProfile/Operat +ional\"> <Select Path=\"Microsoft-Windows-NetworkProfile/Operational\">*[Sy +stem[(EventID=\"$_\")]]</Select> </Query> </QueryList>"; die Win32::OLE->LastError() unless (my $values = $Triggers[$#Triggers]->ValueQueries->Create +("eventId", "Event/System/EventID")); $Triggers[$#Triggers]->{Enabled} = 1; } die Win32::OLE->LastError() unless (my $Action = $TaskDefinition->Acti +ons()->Create(0)); $Action->{Path} = 'C:\Perl64\Bin\Perl.exe'; $Action->{Arguments} = "$0 -f event\${eventID}"; $RootFolder->RegisterTaskDefinition("OLE-Test",$TaskDefinition,6,undef +,undef,3); print Dumper $TaskDefinition->{XmlText};

If I run the code with RegisterTaskDefinition with TASK_VALIDATE_ONLY flag set (third parameter = 1), I get a nice XML dump. So far so good. When I run the code with RegisterTaskDefinition with TASK_CREATE_OR_UPDATE (third parameter = 6), I get this error:

OLE exception from "<Unknown Source>": (11,263):Subscription:<QueryList><Query Id="event10000" Path="Microsoft-Windows-NetworkProfile/Operational"><Select Path="Microsoft-Windows-NetworkProfile/Operational">*[System[(EventID= +"10000")]]</Select></Query></QueryList> Win32::OLE(0.1712) error 0x80073a99: "The specified query is invalid" in METHOD/PROPERTYGET "RegisterTaskDefinition" at OLE-test.pl line + 63.

Anyone familiar enough with Win32::OLE, and the Windows task scheduler XML to explain what I'm doing wrong

Replies are listed 'Best First'.
Re: Win32::OLE and Task Scheduling - Invalid Query
by Corion (Patriarch) on Oct 21, 2018 at 09:50 UTC

    The Query Id attribute must be numeric. I got the following to work:

    for (10000..10001) { die Win32::OLE->LastError() unless (push @Triggers, $TriggerSet->C +reate(0)); $Triggers[$#Triggers]->{Id} = $_; $Triggers[$#Triggers]->{Subscription} = qq{<QueryList> <Query Id="$_" Path="Microsoft-Windows-NetworkProfile/Operat +ional"> <Select Path="Microsoft-Windows-NetworkProfile/Operational +">*[System[(EventID="$_")]]</Select> </Query> </QueryList>}; die Win32::OLE->LastError() unless (my $values = $Triggers[$#Triggers]->ValueQueries->Create("ev +entId", "Event/System/EventID")); $Triggers[$#Triggers]->{Enabled} = 1; }

    The relevant, magic change is matching

    $Triggers[$#Triggers]->{Id} = $_;
    and
    <Query Id="$_" Path="Microsoft-Windows-NetworkProfile/Operational" +>
    Maybe you can change both to be non-numeric, but with that change, I created tasks that I was able to view them afterwards.

    Some stylistic notes:

    Don't use double-quoted strings with (many) escaped backslashes in them. Instead, use one of the alternate quoting mechanisms, like qq{...} to prevent excessive backslashitis.

    For easier testing, do both calls at the end of your program:

    warn "Testing query"; $RootFolder->RegisterTaskDefinition("OLE-Test",$TaskDefinition,1,undef +,undef,3); warn "Query OK, installing task"; $RootFolder->RegisterTaskDefinition("OLE-Test",$TaskDefinition,6,undef +,undef,3); print Dumper $TaskDefinition->{XmlText};
Re: Win32::OLE and Task Scheduling - Invalid Query
by Corion (Patriarch) on Oct 20, 2018 at 22:38 UTC

    Crossposted at Stackoverflow.

    This is not bad per se, but it's good courtesy to mention other locations where you post this, so that there is no double effort wasted.

      I'll try and remember that in future. In my defence, I was getting an uninformative "Permission Denied" trying to submit this question, and cross posted to SO in the meantime. The permission denied turned out to be the link to the powershell script that inspired this.

      There are good reasons why the code to be deployed is in perl