隐藏的PowerShell计划任务

huangapple go评论108阅读模式
英文:

Hidden Powershell Sheduled Task

问题

I'm Running a Gaming PC and I wrote a Wallpaper Switching script in Powershell because Windows 11 doesn't allow me to set a Wallpaper Slideshow with the same Picture on Every Monitor. The Skript would be called on User login and every 30 Minutes after that indefinitely.

# Path of my slideshow
$wallpaperPath = [Environment]::GetFolderPath("MyPictures")+"\Wallpaper\Slide"
# Select random File from path
$wallpaperFile = Get-ChildItem -name -Path $wallpaperPath | Select-Object -index $(Get-Random $((Get-ChildItem -Path $wallpaperPath).Count))
Set-ItemProperty -path "HKCU:\Control Panel\Desktop\" -name wallpaper -value $wallpaperPath$wallpaperFile
rundll32.exe user32.dll, UpdatePerUserSystemParameters

I'm Trying to Run this Script with Task Scheduler. For Convenience Purposes the Task need to run as the Logged in User. (Without Admin Rights, no stored credentials are needed to give access to the Pictures Folder of the User which is needed in the Powershell Skript)
But every time the Task Scheduler Runs the Skript, there is a Window that Pops up, gains Fokes while doing so, and disappears again. Due to this, any Game will minimise every time the Skript runs.

I've tried to Run the Task with these commands and Parameters:

powershell.exe -WindowStyle Hidden -executionpolicy bypass -noninteractive -nologo -command "C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.ps1"

After that, I discovered a VB Script Wrapper everywhere on the net in different variations but the same in the core. It promises to create a Windowsless Powershell instance which would execute the Skript without ever creating a Windows.
The VB Skript looks like this:

command = "powershell.exe -WindowStyle Hidden -executionpolicy bypass -noninteractive -nologo -command ""C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.ps1"""
set shell = CreateObject("WScript.Shell")
shell.Run command, 0, False

And the Task would then Execute this VB Script which would then run the Skript Windows less:

C:\Windows\System32\wscript.exe "C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow_hidden.vbs"

At first, it seems like this helped. I tried executing the Scheduled Task manually a few times and I saw now windows popping up, like the other times. But after Rebooting and Running a Game during an automatically started Task Run I quickly learned that if the Task Runs automatically there is still a Windows Popping up.

So I tried another approach. I found a Git Repo: https://github.com/SeidChr/RunHiddenConsole
This Executable has the exact same Promises as the VB Skript, but it is programmed in C++ and has been compiled into a single .exe.
Renaming that exe to powershellw.exe and placing it next to the powershell.exe and then calling powershellw.exe instead of powershell.exe in Task Scheduler or other Apps should start a Windowless instance of powershellw.exe which forwards anything windowless to powershell.exe.
So everything would look like this:

powershellw.exe -WindowStyle Hidden -executionpolicy bypass -noninteractive -nologo -command "C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.ps1"

But it has exactly the same effect as the VB Skript. At manual execution, there is no Window. At automatic Execution, a Windows is throwing me out of the Game.

So I even tried to Combine different approaches by Using powershellw.exe the will be executed from a VB Script without a Windows.
That would look like this:

C:\Windows\System32\wscript.exe C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.vbs
command = "powershellw.exe -WindowStyle Hidden -executionpolicy bypass -noninteractive -nologo -command ""C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.ps1"""
set shell = CreateObject("WScript.Shell")
shell.Run command, 0, False

But I never got it Working. Still, a Windows that is throwing me out of the Game on automatic Tash Scheduler Execution.

The Last Approach I tried is to Use "Run whether a user is logged on or not" in the Task Scheduler instead of Running when a specific User Logged in. This got indeed rid of the Windows Popping up. Unfortunately, it also gets rid of any Functionality. I assume it Runs with a different User Context that way, so the Users Pictures Folder either can't be accessed or the Environment is Different and it's looking for Pictures in a completely different Users Profile.

So is there any really working way on Windows 11 to run a Scheduled Task that will run a Powershell Script completely unnoticeable for the User in the Users User Context and Environment?

Update 19.06.2023 11am:

as Discussed in the comments i now tried a few additional thing.

First there is a Inline Powershell Script that compiles a Windows application Powershell Host without any Window. link
I entered the path of PowerShellHost.exe in the scheduled task settings and the path to the PowerShell script as an argument.

This is an example .xml Task Sheduler Export:

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
  <RegistrationInfo>
    <Date>2023-06-16T15:28:33.3784615</Date>
    <Author>User</Author>
    <URI>\Eigene\Wallpaper Slideshow</URI>
  </RegistrationInfo>
  <Triggers>
    <LogonTrigger>
      <Repetition>
        <Interval>PT30M</Interval>
        <StopAtDurationEnd>false</StopAtDurationEnd>
      </Repetition>
      <Enabled>true</Enabled>
      <UserId>User</UserId>
    </LogonTrigger>
  </Triggers>
  <Principals>
    <Principal id="Author">
      <UserId>UserID</UserId>
      <LogonType>InteractiveToken</LogonType>
      <RunLevel>LeastPrivilege</RunLevel>
    </Principal>
  </Principals>
  <Settings>
    <MultipleInstancesPolicy>StopExisting</Multiple

<details>
<summary>英文:</summary>

I&#39;m Running a Gaming PC and I wrote a Wallpaper Switching script in Powershell because Windows 11 doesn&#39;t allow me to set a Wallpaper Slideshow with the same Picture on Every Monitor. The Skript would be called on User login and every 30 Minutes after that indefinitely.
The Skript looks like this (WIP - First the Problem described here needs to be fixe)

Path of my slideshow

$wallpaperPath = [Environment]::GetFolderPath("MyPictures")+"\Wallpaper\Slide"

Select random File from path

$wallpaperFile = Get-ChildItem -name -Path $wallpaperPath | Select-Object -index $(Get-Random $((Get-ChildItem -Path $wallpaperPath).Count))
Set-ItemProperty -path "HKCU:\Control Panel\Desktop&quot; -name wallpaper -value $wallpaperPath$wallpaperFile
rundll32.exe user32.dll, UpdatePerUserSystemParameters

I&#39;m Trying to Run this Script with Task Scheduler. For Convenience Purposes the Task need to run as the Logged in User. (Without Admin Rights, no stored credentials are needed to give access to the Pictures Folder of the User which is needed in the Powershell Skript)
But every time the Task Scheduler Runs the Skript, there is a Window that Pops up, gains Fokes while doing so, and disappears again. Due to this, any Game will minimise every time the Skript runs.
I&#39;ve tried to Run the Task with these commands and Parameters: 

powershell.exe -WindowStyle Hidden -executionpolicy bypass -noninteractive -nologo -command "C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.ps1"


After that, I discovered a VB Script Wrapper everywhere on the net in different variations but the same in the core. It promises to create a Windowsless Powershell instance which would execute the Skript without ever creating a Windows.
The VB Skript looks like this:

command = "powershell.exe -WindowStyle Hidden -executionpolicy bypass -noninteractive -nologo -command ""C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.ps1"""
set shell = CreateObject("WScript.Shell")
shell.Run command, 0, False

And the Task would then Execute this VB Script which would then run the Skript Windows less:

C:\Windows\System32\wscript.exe "C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow_hidden.vbs"

At first, it seems like this helped. I tried executing the Scheduled Task manually a few times and I saw now windows popping up, like the other times. But after Rebooting and Running a Game during an automatically started Task Run I quickly learned that if the Task Runs automatically there is still a Windows Popping up.

So I tried another approach. I found a Git Repo: https://github.com/SeidChr/RunHiddenConsole
This Executable has the exact same Promises as the VB Skript, but it is programmed in C++ and has been compiled into a single .exe. 
Renaming that exe to powershellw.exe and placing it next to the powershell.exe and then calling powershellw.exe instead of powershell.exe in Task Scheduler or other Apps should start a Windowless instance of powershellw.exe which forwards anything windowless to powershell.exe.
So everything would look like this:

powershellw.exe -WindowStyle Hidden -executionpolicy bypass -noninteractive -nologo -command "C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.ps1"

But it has exactly the same effect as the VB Skript. At manual execution, there is no Window. At automatic Execution, a Windows is throwing me out of the Game. 

So I even tried to Combine different approaches by Using powershellw.exe the will be executed from a VB Script without a Windows.
That would look like this: 

C:\Windows\System32\wscript.exe C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.vbs


command = "powershellw.exe -WindowStyle Hidden -executionpolicy bypass -noninteractive -nologo -command ""C:\Users\User\Git\Code\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.ps1"""
set shell = CreateObject("WScript.Shell")
shell.Run command, 0, False


But I never got it Working. Still, a Windows that is throwing me out of the Game on automatic Tash Scheduler Execution.
The Last Approach I tried is to Use &quot;Run whether a user is logged on or not&quot; in the Task Scheduler instead of Running when a specific User Logged in. This got indeed rid of the Windows Popping up. Unfortunately, it also gets rid of any Functionality. I assume it Runs with a different User Context that way, so the Users Pictures Folder either can&#39;t be accessed or the Environment is Different and it&#39;s looking for Pictures in a completely different Users Profile. 

##### So is there any really working way on Windows 11 to run a Scheduled Task that will run a Powershell  Script completely unnoticeable for the User in the Users User Context and Environment? #####


# Update 19.06.2023 11am: #


as Discussed in the comments i now tried a few additional thing.

First there is a Inline Powershell Script that compiles a Windows application Powershell Host without any Window. https://www-mariotti-de.translate.goog/simpler-powershell-host-zum-ausfuehren-von-powershell-skripten-ohne-stoerendes-konsolenfenster/?_x_tr_sl=auto&amp;_x_tr_tl=en&amp;_x_tr_hl=de&amp;_x_tr_pto=wapp
I entered the path of PowerShellHost.exe in the scheduled task settings and the path to the PowerShell script as an argument.

This is an example .xml Task Sheduler Export:

<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<Date>2023-06-16T15:28:33.3784615</Date>
<Author>User</Author>
<URI>\Eigene\Wallpaper Slideshow</URI>
</RegistrationInfo>
<Triggers>
<LogonTrigger>
<Repetition>
<Interval>PT30M</Interval>
<StopAtDurationEnd>false</StopAtDurationEnd>
</Repetition>
<Enabled>true</Enabled>
<UserId>User</UserId>
</LogonTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<UserId>UserID</UserId>
<LogonType>InteractiveToken</LogonType>
<RunLevel>LeastPrivilege</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>StopExisting</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT1H</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>C:\Git\Powershell\Wallpaper_Slideshow\PowerShellHost.exe</Command>
<Arguments>"C:\Git\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow_Task.ps1"</Arguments>
</Exec>
</Actions>
</Task>


Second i don&#39;t want to start the Script once by System startup and keep it in the background so there is no new process, because it takes up resources and it could be unreliable.

&gt;! You can reproduce mentioned behavior with my provided code examples and creating an sheduled task as described in my post. Then Reboot, Log-in, Run a Exclusiv fullscreen Game and let it sit there until the task gets executed automatically. You&#39;ll notice that the game will minimize on every automatic execution. Even using the Compiled PowerShellHost.exe or other Compiled Windowsless/Hidden Console Hosts or forwarders or the aforementioned VB Script. Alternativly you can just use this Website and Watch the Log while you have Fokused the Browser and the automitcly started Task sheduler task Runns. http://www2.stat.duke.edu/~cc248/jsphylosvg/js/yui/tests/event/tests/manual/window-focus-test.html

# Update 19.06.2023 8pm: #
I now identifyed exactly what windows is Popping Up and i Refined the Powershell Skript itself.
Here is the new Skript

Path of my slideshow

$wallpaper_path = [Environment]::GetFolderPath("MyPictures") + "\Wallpaper\Slide"

Select random File from path

$new_wallpaper_file_obj = Get-ChildItem -Path $wallpaper_path | Get-Random
$new_wallpaper_file_path = $new_wallpaper_file_obj.DirectoryName + "&quot; + $new_wallpaper_file_obj.Name

Get currently set Wallpaper File

$TIC = (Get-ItemProperty 'HKCU:\Control Panel\Desktop' TranscodedImageCache -ErrorAction Stop).TranscodedImageCache
$current_wallpaper_file_path = [System.Text.Encoding]::Unicode.GetString($TIC) -replace '(.+)([A-Z]:[0-9a-zA-Z\])+', '$2'

Check if New Wallpaper isn't the same as the current one, Repeat Random Until this Condition is met.

while ($current_wallpaper_file_path -eq $new_wallpaper_file_path) {
$new_wallpaper_file_obj = Get-ChildItem -Path $wallpaper_path | Get-Random
$new_wallpaper_file_path = $new_wallpaper_file_obj.DirectoryName + "&quot; + $new_wallpaper_file_obj.Name
}

Define New Wallpaper Type to set new Wallpaper with

$setwallpapersrc = @"
using System.Runtime.InteropServices;

public class Wallpaper
{
public const int SetDesktopWallpaper = 20;
public const int UpdateIniFile = 0x01;
public const int SendWinIniChange = 0x02;
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
public static void SetWallpaper(string path)
{
SystemParametersInfo(SetDesktopWallpaper, 0, path, UpdateIniFile | SendWinIniChange);
}
}
"@
Add-Type -TypeDefinition $setwallpapersrc

Set New Wallpaper

But still the same Windows Pops Up still even using all the hidden Console Hosts and stuff.
This is what i know so far about that Pop Up:

[![Watch Monitor provided by zett42][1]][1]


[![Task Sheduler Log][2]][2]


[![Fokus Logs of the afforementioned Fokus Test Website][3]][3]


  [1]: https://i.stack.imgur.com/SvqzC.png
  [2]: https://i.stack.imgur.com/Ksc5M.png
  [3]: https://i.stack.imgur.com/vziRM.png

The Powershell Watrch Monitor zett42 Provided:

Windows PowerShell (5.1) script

Watch top-level window creation and log window name, process ID and process name.

Add-Type -ReferencedAssemblies UIAutomationClient, UIAutomationTypes -TypeDef @'
using System;
using System.Windows.Automation;

public class WindowWatcher
{
public static void Watch()
{
Automation.AddAutomationEventHandler(
WindowPattern.WindowOpenedEvent,
AutomationElement.RootElement,
TreeScope.Children,
(sender, e) => {
var element = sender as AutomationElement;
var process = System.Diagnostics.Process.GetProcessById( element.Current.ProcessId );
Console.WriteLine(String.Format("Window Title: {0}, ProcessId: {1}, ProcessName: {2}", element.Current.Name, element.Current.ProcessId, process.ProcessName ));
});

	Console.ReadLine();
	Automation.RemoveAllEventHandlers();
}

}
'@


</details>


# 答案1
**得分**: 1

我找到了我的错误。在任务计划程序根文件夹中有一个第二个任务,其中包含我的第一个测试方法,直接调用``powershell.exe``并传递参数``-File *.ps1``。

我现在将描述如何使用Powershell和任务计划程序以最简化的方式成功切换所有监视器上的相同图片壁纸的方法。您可能需要更改路径,然后就可以使用了。此方法完全不需要管理员权限或提升的访问权限。此方法将适用于Windows 7及更新版本,包括Windows 11的最新非内部版本。

首先,您需要Powershell脚本,其中包含壁纸切换逻辑。

##### Wallpaper_Slideshow.ps1:
```powershell
# 我的幻灯片路径
$wallpaper_path = [Environment]::GetFolderPath("MyPictures") + "\Wallpaper\Slide"
# 从路径中选择随机文件
$new_wallpaper_file_obj = Get-ChildItem -Path $wallpaper_path | Get-Random
$new_wallpaper_file_path = $new_wallpaper_file_obj.DirectoryName + "\" + $new_wallpaper_file_obj.Name

# 获取当前设置的壁纸文件
$TIC = (Get-ItemProperty 'HKCU:\Control Panel\Desktop' TranscodedImageCache -ErrorAction Stop).TranscodedImageCache
$current_wallpaper_file_path = [System.Text.Encoding]::Unicode.GetString($TIC) -replace '(.+)([A-Z]:[0-9a-zA-Z\\])+', '$2'

# 检查新壁纸是否与当前壁纸不同,重复随机选择直到满足此条件
while ($current_wallpaper_file_path -eq $new_wallpaper_file_path) {
    $new_wallpaper_file_obj = Get-ChildItem -Path $wallpaper_path | Get-Random
    $new_wallpaper_file_path = $new_wallpaper_file_obj.DirectoryName + "\" + $new_wallpaper_file_obj.Name
}

# 定义新壁纸类型以设置新壁纸
$setwallpapersrc = @"
using System.Runtime.InteropServices;

public class Wallpaper
{
    public const int SetDesktopWallpaper = 20;
    public const int UpdateIniFile = 0x01;
    public const int SendWinIniChange = 0x02;
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
    public static void SetWallpaper(string path)
    {
        SystemParametersInfo(SetDesktopWallpaper, 0, path, UpdateIniFile | SendWinIniChange);
    }
}
"@
Add-Type -TypeDefinition $setwallpapersrc

# 设置新壁纸
[Wallpaper]::SetWallpaper($new_wallpaper_file_path)

或者,您可以使用此代码将下一个索引设置为壁纸(按字母顺序排序),而不是随机选择:

Wallpaper_Slideshow.ps1:
# 我的幻灯片路径
$wallpaper_path = [Environment]::GetFolderPath("MyPictures") + "\Wallpaper\Slide"

# 获取当前设置的壁纸文件
$TIC = (Get-ItemProperty 'HKCU:\Control Panel\Desktop' TranscodedImageCache -ErrorAction Stop).TranscodedImageCache
$current_wallpaper_file_path = [System.Text.Encoding]::Unicode.GetString($TIC) -replace '(.+)([A-Z]:[0-9a-zA-Z\\])+', '$2'
$current_wallpaper_file = Split-Path $current_wallpaper_file_path -leaf

# 获取文件夹中的文件列表
$List = Get-ChildItem -Path $wallpaper_path

# 获取当前设置壁纸的索引
$CurrentIndex = 0..($List.Count - 1) | Where-Object { $List.Name[$_] -eq $current_wallpaper_file }

if ($CurrentIndex + 1 -gt $List.Count - 1) {
    # 获取索引0的完整路径
    $NextWallpaper = $List[0].FullName
}
else {
    # 获取下一个索引的完整路径和名称
    $NextWallpaper = $List[$CurrentIndex + 1].FullName
}

# 定义新壁纸类型以设置新壁纸
$setwallpapersrc = @"
using System.Runtime.InteropServices;

public class Wallpaper
{
    public const int SetDesktopWallpaper = 20;
    public const int UpdateIniFile = 0x01;
    public const int SendWinIniChange = 0x02;
    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
    public static void SetWallpaper(string path)
    {
        SystemParametersInfo(SetDesktopWallpaper, 0, path, UpdateIniFile | SendWinIniChange);
    }
}
"@
Add-Type -TypeDefinition $setwallpapersrc

# 设置下一个壁纸
[Wallpaper]::SetWallpaper($NextWallpaper)

然后,您需要创建一个隐藏的自包含*.exe包装器,以使用此方法中描述的方法将脚本打包。可以将此文本保存为Wallpaper_Slideshow.SED并保存在计算机上。然后,可以将此文件导入到iexpress中,以提供某种默认值或(使用匹配的路径)再次创建*.exe,例如,如果您更改了*.ps1脚本但未更改其他内容,因此需要新的*.exe版本。

Wallpaper_Slideshow.SED:
[Version]
Class=IEXPRESS
SEDVersion=3
[Options]
PackagePurpose=InstallApp
ShowInstallProgramWindow=1
HideExtractAnimation=1
UseLongFileName=1
InsideCompressed=0
CAB_FixedSize=0
CAB_ResvCodeSigning=0
RebootMode=N
InstallPrompt=%InstallPrompt%
DisplayLicense=%DisplayLicense%
FinishMessage=%FinishMessage%
TargetName=%TargetName%
FriendlyName=%FriendlyName%
AppLaunched=%AppLaunched%
PostInstallCmd=%PostInstallCmd%
AdminQuietInstCmd=%AdminQuietInstCmd%
UserQuietInstCmd=%UserQuietInstCmd%
SourceFiles=SourceFiles
[Strings]
InstallPrompt=
DisplayLicense=
FinishMessage=
TargetName=C:\Git\Scripte\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.exe
FriendlyName=Wallpaper Slideshow
AppLaunched=powershell.exe -ExecutionPolicy Bypass -File "Wallpaper

<details>
<summary>英文:</summary>

I&#39;ve found my Error. There was a second Task in the Task Schedulers Root Folder with my very first test method, directly calling ``powershell.exe`` with the argument ``-File *.ps1``.

I&#39;ll now Describe my most Streamlined method to successfully switch the Wallpaper using the same Picture on all Monitors with Powershell and Task Scheduler. You may need to change the Paths and that should be it. This Method works entirely without Admin Rights or elevated access. This method will work on Win7 and newer including the latest non-insider Build of Windows 11.

First you&#39;ll need the Powershel Skript which contains the Wallpaper Switching Logic.

##### Wallpaper_Slideshow.ps1:
```powershell
# Path of my slideshow
$wallpaper_path = [Environment]::GetFolderPath(&quot;MyPictures&quot;) + &quot;\Wallpaper\Slide&quot;
# Select random File from path
$new_wallpaper_file_obj = Get-ChildItem -Path $wallpaper_path | Get-Random
$new_wallpaper_file_path = $new_wallpaper_file_obj.DirectoryName + &quot;\&quot; + $new_wallpaper_file_obj.Name

# Get currently set Wallpaper File
$TIC = (Get-ItemProperty &#39;HKCU:\Control Panel\Desktop&#39; TranscodedImageCache -ErrorAction Stop).TranscodedImageCache
$current_wallpaper_file_path = [System.Text.Encoding]::Unicode.GetString($TIC) -replace &#39;(.+)([A-Z]:[0-9a-zA-Z\\])+&#39;, &#39;$2&#39;

# Check if New Wallpaper isn&#39;t the same as the current one, Repeat Random Until this Condition is met.
while ($current_wallpaper_file_path -eq $new_wallpaper_file_path) {
    $new_wallpaper_file_obj = Get-ChildItem -Path $wallpaper_path | Get-Random
    $new_wallpaper_file_path = $new_wallpaper_file_obj.DirectoryName + &quot;\&quot; + $new_wallpaper_file_obj.Name
}

# Define New Wallpaper Type to set new Wallpaper with
$setwallpapersrc = @&quot;
using System.Runtime.InteropServices;

public class Wallpaper
{
    public const int SetDesktopWallpaper = 20;
    public const int UpdateIniFile = 0x01;
    public const int SendWinIniChange = 0x02;
    [DllImport(&quot;user32.dll&quot;, SetLastError = true, CharSet = CharSet.Auto)]
    private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
    public static void SetWallpaper(string path)
    {
        SystemParametersInfo(SetDesktopWallpaper, 0, path, UpdateIniFile | SendWinIniChange);
    }
}
&quot;@
Add-Type -TypeDefinition $setwallpapersrc

# Set New Wallpaper
[Wallpaper]::SetWallpaper($new_wallpaper_file_path)

Alternativly you can use this Code to Set the Next Index as Wallpaper (Terminal Sort Alpheticaly) insead of Random:

Wallpaper_Slideshow.ps1:
# Path of my slideshow
$wallpaper_path = [Environment]::GetFolderPath(&quot;MyPictures&quot;) + &quot;\Wallpaper\Slide&quot;

# Get currently set Wallpaper File
$TIC = (Get-ItemProperty &#39;HKCU:\Control Panel\Desktop&#39; TranscodedImageCache -ErrorAction Stop).TranscodedImageCache
$current_wallpaper_file_path = [System.Text.Encoding]::Unicode.GetString($TIC) -replace &#39;(.+)([A-Z]:[0-9a-zA-Z\\])+&#39;, &#39;$2&#39;
$current_wallpaper_file = Split-Path $current_wallpaper_file_path -leaf

# Get List of Files in Folder
$List = Get-ChildItem -Path $wallpaper_path

# Get Index of Currently Set Wallpaper
$CurrentIndex = 0..($List.Count - 1) | Where-Object { $List.Name[$_] -eq $current_wallpaper_file }

if ($CurrentIndex + 1 -gt $List.Count - 1) {
    # Gett Full Path of Index 0
    $NextWallpaper = $List[0].FullName
}
else {
    # Get Full Path and Name of the Next Index
    $NextWallpaper = $List[$CurrentIndex + 1].FullName
}

# Define New Wallpaper Type to set new Wallpaper with
$setwallpapersrc = @&quot;
using System.Runtime.InteropServices;

public class Wallpaper
{
    public const int SetDesktopWallpaper = 20;
    public const int UpdateIniFile = 0x01;
    public const int SendWinIniChange = 0x02;
    [DllImport(&quot;user32.dll&quot;, SetLastError = true, CharSet = CharSet.Auto)]
    private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
    public static void SetWallpaper(string path)
    {
        SystemParametersInfo(SetDesktopWallpaper, 0, path, UpdateIniFile | SendWinIniChange);
    }
}
&quot;@
Add-Type -TypeDefinition $setwallpapersrc

# Set Next Wallpaper
[Wallpaper]::SetWallpaper($NextWallpaper)

Then you need to create a hidden self-containing *.exe wrapper for the Skript using the method described here.
https://adamtheautomator.com/ps1-to-exe/#IExpress_20
You can save this Text to Wallpaper_Slideshow.SED and save it on your PC.
This File then can be used to Import in iexpress to provide some sort of Default Values or (with matching Paths) to create the *.exe directly again, say if you changed the *.ps1 Skript but nothing else and you need a new *.exe version because of that.

Wallpaper_Slideshow.SED:
[Version]
Class=IEXPRESS
SEDVersion=3
[Options]
PackagePurpose=InstallApp
ShowInstallProgramWindow=1
HideExtractAnimation=1
UseLongFileName=1
InsideCompressed=0
CAB_FixedSize=0
CAB_ResvCodeSigning=0
RebootMode=N
InstallPrompt=%InstallPrompt%
DisplayLicense=%DisplayLicense%
FinishMessage=%FinishMessage%
TargetName=%TargetName%
FriendlyName=%FriendlyName%
AppLaunched=%AppLaunched%
PostInstallCmd=%PostInstallCmd%
AdminQuietInstCmd=%AdminQuietInstCmd%
UserQuietInstCmd=%UserQuietInstCmd%
SourceFiles=SourceFiles
[Strings]
InstallPrompt=
DisplayLicense=
FinishMessage=
TargetName=C:\Git\Scripte\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.exe
FriendlyName=Wallpaper Slideshow
AppLaunched=powershell.exe -ExecutionPolicy Bypass -File &quot;Wallpaper_Slideshow.ps1&quot;
PostInstallCmd=&lt;None&gt;
AdminQuietInstCmd=
UserQuietInstCmd=
FILE0=&quot;Wallpaper_Slideshow.ps1&quot;
[SourceFiles]
SourceFiles0=C:\Git\Scripte\Powershell\Wallpaper_Slideshow\
[SourceFiles0]
%FILE0%=

Although this guide explicitly wants you to call iexpress with Admin right, I never used or tested it with Admin Rights and it's working perfectly fine without Admin Rights. Just call iexpress.
Change all Paths as needed in the *.SED File, import it and create your *.exe File is my preferred way.

Next, you'll need to create a Scheduled Task that'll run the *.exe File whenever you want. My example will execute on User's Logon and every 30 Minutes after that indefinitely.
So here is my Exported Task as a *.xml File

Wallpaper_Slideshow.xml:
&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-16&quot;?&gt;
&lt;Task version=&quot;1.2&quot; xmlns=&quot;http://schemas.microsoft.com/windows/2004/02/mit/task&quot;&gt;
  &lt;RegistrationInfo&gt;
    &lt;Date&gt;2023-06-16T15:28:33.3784615&lt;/Date&gt;
    &lt;Author&gt;AzureAD\vanThielMarvin(Group&lt;/Author&gt;
    &lt;URI&gt;\Eigene\Wallpaper_Slideshow&lt;/URI&gt;
  &lt;/RegistrationInfo&gt;
  &lt;Triggers&gt;
    &lt;LogonTrigger&gt;
      &lt;Repetition&gt;
        &lt;Interval&gt;PT30M&lt;/Interval&gt;
        &lt;StopAtDurationEnd&gt;false&lt;/StopAtDurationEnd&gt;
      &lt;/Repetition&gt;
      &lt;Enabled&gt;true&lt;/Enabled&gt;
      &lt;UserId&gt;AzureAD\vanThielMarvin(Group&lt;/UserId&gt;
    &lt;/LogonTrigger&gt;
  &lt;/Triggers&gt;
  &lt;Principals&gt;
    &lt;Principal id=&quot;Author&quot;&gt;
      &lt;UserId&gt;S-1-12-1-2062191229-1189786572-1918774432-1595217145&lt;/UserId&gt;
      &lt;LogonType&gt;InteractiveToken&lt;/LogonType&gt;
      &lt;RunLevel&gt;LeastPrivilege&lt;/RunLevel&gt;
    &lt;/Principal&gt;
  &lt;/Principals&gt;
  &lt;Settings&gt;
    &lt;MultipleInstancesPolicy&gt;StopExisting&lt;/MultipleInstancesPolicy&gt;
    &lt;DisallowStartIfOnBatteries&gt;false&lt;/DisallowStartIfOnBatteries&gt;
    &lt;StopIfGoingOnBatteries&gt;true&lt;/StopIfGoingOnBatteries&gt;
    &lt;AllowHardTerminate&gt;true&lt;/AllowHardTerminate&gt;
    &lt;StartWhenAvailable&gt;true&lt;/StartWhenAvailable&gt;
    &lt;RunOnlyIfNetworkAvailable&gt;false&lt;/RunOnlyIfNetworkAvailable&gt;
    &lt;IdleSettings&gt;
      &lt;StopOnIdleEnd&gt;true&lt;/StopOnIdleEnd&gt;
      &lt;RestartOnIdle&gt;false&lt;/RestartOnIdle&gt;
    &lt;/IdleSettings&gt;
    &lt;AllowStartOnDemand&gt;true&lt;/AllowStartOnDemand&gt;
    &lt;Enabled&gt;true&lt;/Enabled&gt;
    &lt;Hidden&gt;false&lt;/Hidden&gt;
    &lt;RunOnlyIfIdle&gt;false&lt;/RunOnlyIfIdle&gt;
    &lt;WakeToRun&gt;false&lt;/WakeToRun&gt;
    &lt;ExecutionTimeLimit&gt;PT1H&lt;/ExecutionTimeLimit&gt;
    &lt;Priority&gt;7&lt;/Priority&gt;
    &lt;RestartOnFailure&gt;
      &lt;Interval&gt;PT1M&lt;/Interval&gt;
      &lt;Count&gt;3&lt;/Count&gt;
    &lt;/RestartOnFailure&gt;
  &lt;/Settings&gt;
  &lt;Actions Context=&quot;Author&quot;&gt;
    &lt;Exec&gt;
      &lt;Command&gt;C:\Git\Scripte\Powershell\Wallpaper_Slideshow\Wallpaper_Slideshow.exe&lt;/Command&gt;
    &lt;/Exec&gt;
  &lt;/Actions&gt;
&lt;/Task&gt;

Then again change all the Paths including the URI.
The URI is the Path in Task Scheduler where you store the Task.
So open Task Scheduler and Create your Folders / Subfolders where you want to Organize your Tasks matching with the URI in the *.xml. In Task Scheduler then just click Import Task and select the *.xml File. You can now Visually check if all paths are okay. Hit okay to complete the Import process. You can now Reboot your System and everything should work as intended.

huangapple
  • 本文由 发表于 2023年6月19日 01:20:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/76501747.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定