PSLockScreenSlideShow.psm1
Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Runtime.WindowsRuntime $Script:ModuleName = (Get-Item -Path $PSCommandPath).BaseName function Get-PSLockScreenMetrics { $object = [System.Windows.Forms.Screen]::AllScreens | Where-Object Primary | Select-Object DeviceName, Bounds, WorkingArea, BitsPerPixel $object | Add-Member -MemberType ScriptProperty -Name Width -Value { $this.Bounds.Width } $object | Add-Member -MemberType ScriptProperty -Name Height -Value { $this.Bounds.Height } $object | Add-Member -MemberType ScriptProperty -Name Size -Value { "$($this.Width )x$( $this.Height)" } $object | Add-Member -MemberType ScriptProperty -Name Ratio -Value { [Math]::Round($this.Width / $this.Height, 2) } $object | Add-Member -MemberType ScriptProperty -Name Portrait -value { $this.Width -lt $this.Height } $object | Add-Member -MemberType ScriptProperty -Name Landscape -value { $this.Width -gt $this.Height } $object | Add-Member -MemberType ScriptProperty -Name SortProperty -value { if($this.Landscape) { "Width" } else { "Height" } } $object } $Script:SlideShowRootPath = "$([System.Environment]::GetFolderPath([System.Environment+SpecialFolder]::CommonApplicationData))\$($Script:ModuleName)" function Set-PSLockScreenSlideShowPathRoot($Path) { $Script:SlideShowRootPath = $Path } function Get-PSLockScreenSlideShowPathRoot { $Script:SlideShowRootPath } function Get-PSLockScreenSlideShowPath { if($path = Get-PSLockScreenSlideShowPathExactMatch) { return $path } if($path = Get-PSLockScreenSlideShowPathBestMatch ) { return $path } if($path = Get-PSLockScreenSlideShowPathDefault ) { return $path } Get-PSLockScreenSlideShowPathRoot } function Get-PSLockScreenSlideShowPathDefault { if($path = Get-ChildItem -Path (Get-PSLockScreenSlideShowPathRoot) -Filter Default -Directory) { $path.FullName } } function Get-PSLockScreenSlideShowPathExactMatch { if($path = Get-ChildItem -Path (Get-PSLockScreenSlideShowPathRoot) -Filter "$((Get-PSLockScreenMetrics).Size)" -Directory) { $path.FullName } } function Get-PSLockScreenSlideShowPathBestMatch { $LockScreen = Get-PSLockScreenMetrics $groupedByRatioDeviation = Get-PSLockScreenSlideShowPathAllRes | Group-Object -Property { [Math]::Abs($_.Ratio - $LockScreen.Ratio) } if($path = $groupedByRatioDeviation | Sort-Object -Property Name | Select-Object -First 1 | Select-Object -ExpandProperty Group | Sort-Object $LockScreen.SortProperty | Select-Object -Last 1) { $path.FullName } } function Get-PSLockScreenSlideShowPathAllRes { Get-ChildItem -Path (Get-PSLockScreenSlideShowPathRoot) -Directory | ForEach-Object { if($_.BaseName -match "^(?<Width>\d{1,})x(?<Height>\d{1,})$") { [PSCustomObject]@{ FullName = $_.FullName Width = [Convert]::ToInt32($Matches.Width ) Height = [Convert]::ToInt32($Matches.Height) Ratio = [Math]::Round($Matches.Width / $Matches.Height, 2) } } } } <# .SYNOPSIS Get PSLockScreenSlideShow images from path .DESCRIPTION Get PSLockScreenSlideShow images from path and return a list of objects with Path, Time, Unit properties. The result can directly be piped to Start-PSLockScreenSlideShow (e.g. see PSLockScreenSlideShow.ps1) .PARAMETER Path Specifiy the path where to load the LockScreen images from Defaults to C:\ProgramData\PSLockScreenSlideShow Files are NOT loaded recursively, but sorted by name to force a consistent order .PARAMETER Time The time each slideshow image will be shown if no time can be extracted from filename by parameter TimeRegEx Defaults to "5s" .PARAMETER TimeRegEx Defaults to "^.*\W(?<Time>\d{1,})(?<Unit>ms|s)$" which means: ^ start of line (line=file.Basename) .* 0 to n any characters \W one any non digit or letter (means special character) (?<Time>\d{1,}) one or more digits (this pattern must be named 'Time' to be used internally to fill the result objects property "Time") (?<Unit>ms|s) 'ms' or 's', unit of the afore mentioned Time (this pattern must be named 'Unit' to be used internally to fill the result objects property "Unit") $ end of line (line= file.Basename) .INPUTS None. Right now you cannot pipe objects to Get-PSLockScreenSlideShow .OUTPUTS a list of PSCustomObjects [PSCustomObject]@{ Path = ... Time = ... Unit = ... } .EXAMPLE Get-PSLockScreenSlideShow Path Time Unit ---- ---- ---- C:\ProgramData\PSLockScreenSlideShow\00-Show.jpg 5 s C:\ProgramData\PSLockScreenSlideShow\01-Show-5s.jpg 5 s C:\ProgramData\PSLockScreenSlideShow\02-Show-10s.jpg 10 s C:\ProgramData\PSLockScreenSlideShow\03-Show-5000ms.jpg 5000 ms assumed C:\ProgramData\PSLockScreenSlideShow contains the following files 00-Show.jpg 01-Show-5s.jpg 02-Show-10s.jpg 03-Show-5000ms.jpg #> function Get-PSLockScreenSlideShow { [CmdletBinding()] Param ( [Parameter()][string]$Path = (Get-PSLockScreenSlideShowPath), [Parameter()][string]$Time = "5s", [Parameter()][string]$TimeRegEx = "^.*\W(?<Time>\d{1,})(?<Unit>ms|s)$" ) Begin { $Default = Get-PSLockScreenSlideShowTime $Time } Process { Get-ChildItem -Path $Path | Sort-Object -Property FullName | ForEach-Object { $Result = [PSCustomObject]@{ Path = $_.FullName Time = $Default.Time Unit = $Default.Unit } if($TimeRegEx) { if($_.BaseName -match $TimeRegEx) { if($Matches.Time) { $Result.Time = [Convert]::ToInt32($Matches.Time) } if($Matches.Unit) { $Result.Unit = $Matches.Unit } } } $Result } } } function Get-PSLockScreenSlideShowTime($Time) { if($Time -match "^(\d{1,})(ms|s)$") { [PSCustomObject]@{ Time = [Convert]::ToInt32($Matches[1]) Unit = $Matches[2] } } else { [PSCustomObject]@{ Time = 5 Unit = "s" } } } function Wait-PSLockScreenSlideShow { [CmdletBinding()] Param ( [Parameter(ValueFromPipelineByPropertyName)]$Time, [Parameter(ValueFromPipelineByPropertyName)]$Unit ) Process { $Sleep = @{ @{ "s" = "Seconds" "ms" = "Milliseconds" "Seconds" = "Seconds" "Milliseconds" = "Milliseconds" }[$Unit] = $Time } Start-Sleep @Sleep } } <# .SYNOPSIS Set LockScreen image .DESCRIPTION Set LockScreen image .PARAMETER Path Specifiy the image filename to set as LockScreen image .INPUTS Image filename that should be set as LockScreen image .OUTPUTS None. .EXAMPLE "C:\ProgramData\PSLockScreenSlideShow\00-Show.jpg" | Set-PSLockScreenSlideShowImage Set "C:\ProgramData\PSLockScreenSlideShow\00-Show.jpg" via pipeline as the actual LockScreen image .EXAMPLE Set-PSLockScreenSlideShowImage "C:\ProgramData\PSLockScreenSlideShow\00-Show.jpg" Set "C:\ProgramData\PSLockScreenSlideShow\00-Show.jpg" via parameter as the actual LockScreen image #> function Set-PSLockScreenSlideShowImage { [CmdletBinding()] Param ( [Parameter(ValueFromPipelineByPropertyName,ValueFromPipeline)][string]$Path ) Begin { [void]([Windows.Storage.StorageFile,Windows.Storage,ContentType=WindowsRuntime]) [void]([Windows.System.UserProfile.LockScreen,Windows.System.UserProfile,ContentType=WindowsRuntime]) Add-Type -AssemblyName System.Runtime.WindowsRuntime $asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0] function Await($WinRtTask, $ResultType) { $asTask = $asTaskGeneric.MakeGenericMethod($ResultType) $netTask = $asTask.Invoke($null, @($WinRtTask)) $netTask.Wait(-1) | Out-Null $netTask.Result } function AwaitAction($WinRtAction) { $asTask = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and !$_.IsGenericMethod })[0] $netTask = $asTask.Invoke($null, @($WinRtAction)) $netTask.Wait(-1) | Out-Null } } Process { $image = Await ([Windows.Storage.StorageFile]::GetFileFromPathAsync($Path)) ([Windows.Storage.StorageFile]) AwaitAction ([Windows.System.UserProfile.LockScreen]::SetImageFileAsync($image)) } } function Start-PSLockScreenSlideShow { [CmdletBinding()] Param ( [Parameter(ValueFromPipelineByPropertyName,ValueFromPipeline)][string[]]$Path, [Parameter(ValueFromPipelineByPropertyName)] [int] $Time, [Parameter(ValueFromPipelineByPropertyName)] [string] $Unit, [Parameter()] [switch] $Forever ) Begin { $Slides = [System.Collections.Generic.List[object]]::new() } Process { foreach($p in $Path) { $Slides.Add([PSCustomObject]@{ Path = $p Time = $Time Unit = $Unit }) Set-PSLockScreenSlideShowImage -Path $p Wait-PSLockScreenSlideShow -Time $Time -Unit $Unit } } End { if($Forever) { $Slides | ForEach-Object { Set-PSLockScreenSlideShowImage -Path $_.Path Wait-PSLockScreenSlideShow -Time $_.Time -Unit $_.Unit } } } } <# .SYNOPSIS Register PSLockScreen ScheduledTasks .DESCRIPTION Register PSLockScreen ScheduledTasks \PSLockScreenSlideShow\Lock (triggered on lock) \PSLockScreenSlideShow\Unlock (triggered on unlock) .PARAMETER Path see Get-PSLockScreenSlideShow .PARAMETER Time see Get-PSLockScreenSlideShow .PARAMETER TimeRegEx see Get-PSLockScreenSlideShow .INPUTS None. .OUTPUTS None. #> function Register-PSLockScreenSlideShowScheduledTask { [CmdletBinding()] Param ( [Parameter()][string]$Path, [Parameter()][string]$Time, [Parameter()][string]$TimeRegEx ) Unregister-PSLockScreenSlideShowScheduledTask @( @{ TaskPath = "\$($Script:ModuleName)\" TaskName = "Lock" Principal = New-ScheduledTaskPrincipal -GroupId "S-1-5-32-545" # VORDEFINIERT\Benutzer = Builtin\Users Trigger = . { $class = Get-CimClass MSFT_TaskSessionStateChangeTrigger Root/Microsoft/Windows/TaskScheduler $instance = $class | New-CimInstance -ClientOnly $instance.Enabled = $true $instance.StateChange = 7 # Lock $instance } Action = . { $ActionExe = (([System.Environment]::GetCommandLineArgs() | Select-Object -First 1) -replace "_ISE", "") $ActionArgs = . { "-ExecutionPolicy Bypass -WindowStyle Hidden -File ""$([System.IO.Path]::ChangeExtension($PSCommandPath, ".ps1"))""" foreach($key in $PSBoundParameters.Keys) { "-$($key) $($PSBoundParameters[$key])" } } New-ScheduledTaskAction -Execute $ActionExe -Argument ($ActionArgs -join ' ') } Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -MultipleInstances IgnoreNew -ExecutionTimeLimit "00:00:00" } @{ TaskPath = "\$($Script:ModuleName)\" TaskName = "Unlock" Principal = New-ScheduledTaskPrincipal -GroupId "S-1-5-32-545" # VORDEFINIERT\Benutzer = Builtin\Users Trigger = . { $class = Get-CimClass MSFT_TaskSessionStateChangeTrigger Root/Microsoft/Windows/TaskScheduler $instance = $class | New-CimInstance -ClientOnly $instance.Enabled = $true $instance.StateChange = 8 # Unlock $instance } Action = . { $ActionExe = (([System.Environment]::GetCommandLineArgs() | Select-Object -First 1) -replace "_ISE", "") $ActionArgs = . { "-ExecutionPolicy Bypass -WindowStyle Hidden -Command ""Stop-ScheduledTask -TaskPath '\$($Script:ModuleName)\' -TaskName 'Lock' -ErrorAction SilentlyContinue""" } New-ScheduledTaskAction -Execute $ActionExe -Argument ($ActionArgs -join ' ') } Settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -MultipleInstances IgnoreNew -ExecutionTimeLimit "00:00:00" } ) | ForEach-Object { Register-ScheduledTask @_ -ErrorAction SilentlyContinue } } <# .SYNOPSIS Unregister PSLockScreen ScheduledTasks .DESCRIPTION Unregister PSLockScreen ScheduledTasks \PSLockScreenSlideShow\Lock (triggered on lock) \PSLockScreenSlideShow\Unlock (triggered on unlock) .INPUTS None. .OUTPUTS None. #> function Unregister-PSLockScreenSlideShowScheduledTask { Unregister-ScheduledTask -TaskPath "\$($Script:ModuleName)\" -TaskName "Lock" -Confirm:$false -ErrorAction SilentlyContinue Unregister-ScheduledTask -TaskPath "\$($Script:ModuleName)\" -TaskName "Unlock" -Confirm:$false -ErrorAction SilentlyContinue try { $taskScheduler = New-Object -ComObject Schedule.Service $taskScheduler.Connect() $taskScheduler.GetFolder("\").DeleteFolder($Script:ModuleName, $null) } catch { } } |