fix: normalize ExecutionTimeLimit PT0S and load TaskQuery locally (2.0.33-SAC)

Treat Get-ScheduledTask limit strings as unlimited, prefer ProgramData for TaskQuery import, and copy UNC modules to temp before dot-source.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-11 15:27:38 +10:00
parent 5e9939724c
commit 6ee5fdcbf2
5 changed files with 105 additions and 33 deletions
+77 -24
View File
@@ -973,12 +973,35 @@ function Stop-RdpLoginMonitorMainProcesses {
}
}
function Convert-RdpMonitorDeployTaskExecutionTimeLimitValue {
param($Limit)
if ($null -eq $Limit) { return $null }
if ($Limit -is [TimeSpan]) { return $Limit }
$text = [string]$Limit
if ([string]::IsNullOrWhiteSpace($text)) { return $null }
$t = $text.Trim()
if ($t -eq 'PT0S') { return [TimeSpan]::Zero }
try {
return [System.Xml.XmlConvert]::ToTimeSpan($t)
} catch { }
try {
return [TimeSpan]::Parse($t)
} catch { }
return $null
}
function Test-RdpMonitorDeployTaskExecutionLimitUnlimitedValue {
param($Limit)
if ($null -eq $Limit -or $Limit -isnot [TimeSpan]) { return $false }
if ($Limit.Ticks -le 0) { return $true }
if ($Limit.TotalDays -ge 999) { return $true }
$normalized = Convert-RdpMonitorDeployTaskExecutionTimeLimitValue -Limit $Limit
if ($null -eq $normalized) { return $false }
if ($normalized.Ticks -le 0) { return $true }
if ($normalized.TotalDays -ge 999) { return $true }
return $false
}
@@ -987,9 +1010,9 @@ function Get-RdpMonitorDeployTaskExecutionTimeLimitLabelFromResolved {
if ($null -eq $Resolved) { return '(null)' }
if ($Resolved.Source -eq 'missing') { return '(task missing)' }
$limit = $Resolved.Limit
$limit = Convert-RdpMonitorDeployTaskExecutionTimeLimitValue -Limit $Resolved.Limit
if ($null -eq $limit) { return '(null)' }
if ($limit -is [TimeSpan] -and $limit.Ticks -le 0) { return 'PT0S' }
if ($limit.Ticks -le 0) { return 'PT0S' }
return $limit.ToString()
}
@@ -1019,33 +1042,63 @@ function Publish-RdpMonitorDeployTaskQueryFunctionsToScriptScope {
}
}
function Import-RdpMonitorDeployTaskQueryModule {
param([string]$ShareRoot = '')
function Import-RdpMonitorDeployTaskQueryModuleAtPath {
param(
[Parameter(Mandatory = $true)][string]$Candidate
)
$candidates = [System.Collections.Generic.List[string]]::new()
if (-not [string]::IsNullOrWhiteSpace($ShareRoot)) {
$candidates.Add((Join-Path $ShareRoot 'RdpMonitor-TaskQuery.ps1')) | Out-Null
}
$deployScriptPath = $PSCommandPath
if ([string]::IsNullOrWhiteSpace($deployScriptPath)) { $deployScriptPath = $MyInvocation.MyCommand.Path }
if (-not [string]::IsNullOrWhiteSpace($deployScriptPath)) {
$candidates.Add((Join-Path (Split-Path -Parent $deployScriptPath) 'RdpMonitor-TaskQuery.ps1')) | Out-Null
}
$candidates.Add((Join-Path $InstallRoot 'RdpMonitor-TaskQuery.ps1')) | Out-Null
if (-not (Test-Path -LiteralPath $Candidate)) { return $false }
foreach ($candidate in @($candidates)) {
if (-not (Test-Path -LiteralPath $candidate)) { continue }
$loadPath = $Candidate
$tempCopy = $null
try {
. $candidate
} catch {
Write-DeployLog "Предупреждение: не удалось загрузить $candidate$($_.Exception.Message)"
continue
if ($Candidate.StartsWith('\\')) {
$tempCopy = Join-Path $env:TEMP ("rdp-taskquery-{0}.ps1" -f [guid]::NewGuid().ToString('N'))
Copy-Item -LiteralPath $Candidate -Destination $tempCopy -Force
$loadPath = $tempCopy
}
. $loadPath
if (Test-RdpMonitorDeployTaskQueryReady) {
Publish-RdpMonitorDeployTaskQueryFunctionsToScriptScope
return $true
}
Write-DeployLog "Предупреждение: $candidate загружен, но функции TaskQuery недоступны — пробуем следующий источник."
Write-DeployLog "Предупреждение: $Candidate загружен, но функции TaskQuery недоступны — пробуем следующий источник."
} catch {
Write-DeployLog "Предупреждение: не удалось загрузить $Candidate$($_.Exception.Message)"
} finally {
if ($null -ne $tempCopy -and (Test-Path -LiteralPath $tempCopy)) {
Remove-Item -LiteralPath $tempCopy -Force -ErrorAction SilentlyContinue
}
}
return $false
}
function Import-RdpMonitorDeployTaskQueryModule {
param([string]$ShareRoot = '')
$candidates = [System.Collections.Generic.List[string]]::new()
$installCandidate = Join-Path $InstallRoot 'RdpMonitor-TaskQuery.ps1'
$candidates.Add($installCandidate) | Out-Null
$deployScriptPath = $PSCommandPath
if ([string]::IsNullOrWhiteSpace($deployScriptPath)) { $deployScriptPath = $MyInvocation.MyCommand.Path }
if (-not [string]::IsNullOrWhiteSpace($deployScriptPath)) {
$deployCandidate = Join-Path (Split-Path -Parent $deployScriptPath) 'RdpMonitor-TaskQuery.ps1'
if ($deployCandidate -ne $installCandidate) {
$candidates.Add($deployCandidate) | Out-Null
}
}
if (-not [string]::IsNullOrWhiteSpace($ShareRoot)) {
$shareCandidate = Join-Path $ShareRoot 'RdpMonitor-TaskQuery.ps1'
if ($shareCandidate -ne $installCandidate -and -not ($candidates -contains $shareCandidate)) {
$candidates.Add($shareCandidate) | Out-Null
}
}
foreach ($candidate in @($candidates)) {
if (Import-RdpMonitorDeployTaskQueryModuleAtPath -Candidate $candidate) {
return $true
}
}
return $false
}
+1 -1
View File
@@ -90,7 +90,7 @@ $script:SkipLogDetailLimit = 15
# строки ниже, если правки «мелкие» и вы не хотите менять отображаемую версию в логах).
# Рекомендация: при значимых релизах меняйте и $ScriptVersion, и version.txt одинаково; при только
# исправлениях на шаре — достаточно поднять patch в version.txt (например 1.3.0.1).
$ScriptVersion = "2.0.32-SAC"
$ScriptVersion = "2.0.33-SAC"
# Логи (все под InstallRoot)
$LogFile = Join-Path $script:InstallRoot "Logs\login_monitor.log"
+14 -6
View File
@@ -87,13 +87,21 @@ function Get-RdpMonitorScheduledTaskActionFromDocument {
}
}
function Convert-RdpMonitorScheduledTaskExecutionTimeLimitValue {
param($Limit)
if ($null -eq $Limit) { return $null }
if ($Limit -is [TimeSpan]) { return $Limit }
return Convert-RdpMonitorScheduledTaskExecutionTimeLimitText -LimitText ([string]$Limit)
}
function Test-RdpMonitorScheduledTaskExecutionTimeLimitUnlimitedValue {
param($Limit)
if ($null -eq $Limit) { return $false }
if ($Limit -isnot [TimeSpan]) { return $false }
if ($Limit.Ticks -le 0) { return $true }
if ($Limit.TotalDays -ge 999) { return $true }
$normalized = Convert-RdpMonitorScheduledTaskExecutionTimeLimitValue -Limit $Limit
if ($null -eq $normalized) { return $false }
if ($normalized.Ticks -le 0) { return $true }
if ($normalized.TotalDays -ge 999) { return $true }
return $false
}
@@ -153,9 +161,9 @@ function Get-RdpMonitorScheduledTaskExecutionTimeLimitLabel {
$resolved = Get-RdpMonitorScheduledTaskExecutionTimeLimitResolved -TaskName $TaskName
if ($resolved.Source -eq 'missing') { return '(task missing)' }
$limit = $resolved.Limit
$limit = Convert-RdpMonitorScheduledTaskExecutionTimeLimitValue -Limit $resolved.Limit
if ($null -eq $limit) { return '(null)' }
if ($limit -is [TimeSpan] -and $limit.Ticks -le 0) { return 'PT0S' }
if ($limit.Ticks -le 0) { return 'PT0S' }
return $limit.ToString()
}
+11
View File
@@ -7,6 +7,17 @@ Invoke-RdpMonitorTestCase -Name 'Deploy functions load (RDP_DEPLOY_FUNCTIONS_ONL
Assert-CommandExists -Name 'Test-RdpMonitorDeployMainTaskNeedsUnlimitedExecutionTime'
Assert-CommandExists -Name 'Write-RdpMonitorDeployScheduledTaskVerification'
Assert-CommandExists -Name 'Get-RdpMonitorDeployTaskExecutionTimeLimitLabelFromResolved'
Assert-CommandExists -Name 'Convert-RdpMonitorDeployTaskExecutionTimeLimitValue'
}
Invoke-RdpMonitorTestCase -Name 'Deploy ExecutionTimeLimit accepts PT0S string (Get-ScheduledTask shape)' -Script {
Assert-True -Condition (Test-RdpMonitorDeployTaskExecutionLimitUnlimitedValue -Limit 'PT0S') `
-Message 'PT0S string must be treated as unlimited'
$resolved = [pscustomobject]@{ Limit = 'PT0S'; Source = 'Get-ScheduledTask' }
$label = Get-RdpMonitorDeployTaskExecutionTimeLimitLabelFromResolved -Resolved $resolved
Assert-True -Condition ($label -eq 'PT0S') -Message "Expected PT0S label, got $label"
Assert-True -Condition (Test-RdpMonitorDeployTaskExecutionLimitUnlimitedValue -Limit $resolved.Limit) `
-Message 'Resolved PT0S string must pass unlimited check'
}
Invoke-RdpMonitorTestCase -Name 'Deploy pre-check task limit (no throw on early path)' -Script {
+1 -1
View File
@@ -1 +1 @@
2.0.32-SAC
2.0.33-SAC