Powershell: Aufgabe im Hintergrund MsgBox in Vordergrund
Hallo,
Lösung Siehe : Aufgabenplanung: Powershell Script im Hintergrund mit Userbenachrichtigung im Vordergrund
ich habe ein Script in dem ich Dateien mit Aufgabenplaner von A nach B Kopiere. Die Pfade stehen in einer CSV.
Das Script lasse ich im Hintergrund laufen, jedoch möchte ich wenn ein Pfad nicht Erreichbar ist eine Info ausgeben, was leider noch nicht funktioniert. Habt Ihr eine Idee zur umsetzung?
CSV:
Powershell:
Danke.
Lösung Siehe : Aufgabenplanung: Powershell Script im Hintergrund mit Userbenachrichtigung im Vordergrund
ich habe ein Script in dem ich Dateien mit Aufgabenplaner von A nach B Kopiere. Die Pfade stehen in einer CSV.
Das Script lasse ich im Hintergrund laufen, jedoch möchte ich wenn ein Pfad nicht Erreichbar ist eine Info ausgeben, was leider noch nicht funktioniert. Habt Ihr eine Idee zur umsetzung?
CSV:
Quelle Ziel
C:\Temp\source C:\Temp\dest
Powershell:
#Aktuellen Standardpfad setzen
Set-Location "C:\Temp"
If (Test-Connection Server01 -Count 1 -quiet)
{
$csvpath = '.\Sync.CSV'
$Aufgaben = Import-Csv $csvpath -Delimiter ([regex]::match((gc $csvpath -Raw),'^("[^"]*"|[^,;]+)\s*([,;])').Groups[2].Value)
# Zeile der CSV durchgehen
Foreach ($Aufgabe in $Aufgaben)
{
# gültige Pfad in CSV eingetragen?
If (!(Test-Path -Path $Aufgabe.Quelle))
{
[System.Windows.Forms.MessageBox]::Show("Verzeichnis: "+$Aufgabe.Quelle+" nicht gefunden.")
}
elsif (!(Test-Path -Path $Aufgabe.Ziel))
{
[System.Windows.Forms.MessageBox]::Show("Verzeichnis: "+$Aufgabe.Ziel+" nicht gefunden.")
}
else
{
Write-Output ("Pfad "+$Aufgabe.Ziel+" gefunden")
$file = (Get-ChildItem $Aufgabe.Quelle -File -Recurse| Where {$_.Name -like "*.zap*"}| sort lastwritetime | select -last 1)
$ZielDatei= $Aufgabe.Ziel+"\"+"RW_"+$file.Name
#Zieldatei schon vorhanden?
If (-NOT(Test-Path -Path $ZielDatei -PathType Leaf))
{
Write-Output ("Kopiere Datei "+$file.Name+" nach "+$ZielDatei)
Copy-Item $file.FullName -Destination $ZielDatei
}
}
}
}
Danke.
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 4743938752
Url: https://administrator.de/forum/powershell-aufgabe-im-hintergrund-msgbox-in-vordergrund-4743938752.html
Ausgedruckt am: 26.12.2024 um 10:12 Uhr
25 Kommentare
Neuester Kommentar
Aufgabe im User-Kontext ausführen lassen und im Skript Konsole einfach ausblenden lassen
Evt, Message-Boxes erscheinen dann trotzdem noch.
Alternativ wenn Aufgabe mit anderem Account ausgeführt wird per msg.exe die Nachricht dem User schicken.
Oder das hier wäre auch noch eine Möglichkeit, gerade noch gefunden:
Popup erzeugen in Windows, dessen Inhalt nicht mitgeloggt wird
Uk.
Add-Type –MemberDefinition '[DllImport("user32.dll")]public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);' -name Window -namespace tools
[void][Tools.Window]::ShowWindow((Get-Process -id $pid).MainWindowHandle, 0)
Alternativ wenn Aufgabe mit anderem Account ausgeführt wird per msg.exe die Nachricht dem User schicken.
Oder das hier wäre auch noch eine Möglichkeit, gerade noch gefunden:
Popup erzeugen in Windows, dessen Inhalt nicht mitgeloggt wird
Uk.
Moin,
zu starten
In beiden fällen ploppt dann aber natürlich noch kurz das Konsolenfenster auf bis der Code oder das Argument verabeitet wurde. Das wiederum lässt sich nur verhindern, indem man sich eine Binärdatei baut, die den Powershell-Code direkt im Hintergrund ausführt.
Gruß Thomas
Zitat von @4400667902:
Aufgabe im User-Kontext ausführen lassen und im Skript Konsole einfach ausblenden lassen
Dafür reicht es auch, Powershell einfach mit dem ArgumentAufgabe im User-Kontext ausführen lassen und im Skript Konsole einfach ausblenden lassen
Add-Type –MemberDefinition '[DllImport("user32.dll")]public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);' -name Window -namespace tools
[void][Tools.Window]::ShowWindow((Get-Process -id $pid).MainWindowHandle, 0)
-WindowStyle Hidden
In beiden fällen ploppt dann aber natürlich noch kurz das Konsolenfenster auf bis der Code oder das Argument verabeitet wurde. Das wiederum lässt sich nur verhindern, indem man sich eine Binärdatei baut, die den Powershell-Code direkt im Hintergrund ausführt.
Gruß Thomas
Jepp klar war nur eine andere Variante.
Zitat von @TK1987:
In beiden fällen ploppt dann aber natürlich noch kurz das Konsolenfenster auf bis der Code oder das Argument verabeitet wurde. Das wiederum lässt sich nur verhindern, indem man sich eine Binärdatei baut, die den Powershell-Code direkt im Hintergrund ausführt.
In beiden fällen ploppt dann aber natürlich noch kurz das Konsolenfenster auf bis der Code oder das Argument verabeitet wurde. Das wiederum lässt sich nur verhindern, indem man sich eine Binärdatei baut, die den Powershell-Code direkt im Hintergrund ausführt.
Oder mit VBS und Hidden. Was besseres gibt es nicht oder? Ich bin immer noch bei VBS hängengebieben....
Ich such mir da auch immer einen Wolf.
Dim objShell,objFSO,objFile
Set objShell=CreateObject("WScript.Shell")
Set objFSO=CreateObject("Scripting.FileSystemObject")
'enter the path for your PowerShell Script
strPath="c:\ps\archiv_pdf.ps1"
'verify file exists
If objFSO.FileExists(strPath) Then
'return short path name
set objFile=objFSO.GetFile(strPath)
strCMD="powershell -ExecutionPolicy Bypass -nologo -command " & Chr(34) & "&{" &_
objFile.ShortPath & " " & WScript.Arguments(0) &" "& WScript.Arguments(1) &" "& WScript.Arguments(2) & "}" & Chr(34)
'Uncomment next line for debugging
'WScript.Echo strCMD
'use 0 to hide window
objShell.Run strCMD,0
Else
'Display error message
WScript.Echo "Failed to find " & strPath
WScript.Quit
End If
Wenn du per VBS hidden startest werden aber auch sämtliche evt. Messageboxen mit versteckt weil der ganze Prozess hidden startet und genau das will er ja nicht ...
Man kann sich aber auch in echten Hintergrund-Prozessen das Konsolen-Token krallen und dort Anwendungen im Kontext des sichtbaren Users ausführen lassen, das geht per win32 impersonation wird aber etwas länger.
Oder man nimmt eben einen Scheduled Task und lässt den mit einem Gruppen-Account ausführen das wird dann auch wieder sichtbar für den User (siehe Link oben).
Man kann sich aber auch in echten Hintergrund-Prozessen das Konsolen-Token krallen und dort Anwendungen im Kontext des sichtbaren Users ausführen lassen, das geht per win32 impersonation wird aber etwas länger.
Oder man nimmt eben einen Scheduled Task und lässt den mit einem Gruppen-Account ausführen das wird dann auch wieder sichtbar für den User (siehe Link oben).
Wie gesagt, einfach schnell eine Binary bauen, die den Code im Hintergrund ausführt.
Zum Beispiel so:
Am besten sollte die Binärdatei im Anschluss natürlich noch mit Set-AuthenticodeSignature signiert werden.
Dann einfach das Skript wie folgt starten:
Gruß Thomas
Zum Beispiel so:
# Ausgabedatei
$Bin = "$HOME\Desktop\PsHiddenExec.exe"
# Modul PS2EXE installieren
Install-Module -Scope CurrentUser PS2EXE -Confirm:$false
# Temporäre Skript-Datei erstellen
$TMP = "$ENV:Temp\PsHiddenExec.ps1"
'iex ($args -join "`r`n")' | Set-Content -Encoding UTF8 -Path $TMP
# Binärdatei erstellen
ps2exe -InputFile $TMP -Outputfile $Bin -NoConsole -NoOutput -NoError -NoVisualStyles
Dann einfach das Skript wie folgt starten:
PsHiddenExec.exe "&'C:\Pfad\zur\SkriptDatei.ps1'"
Gruß Thomas
Mittels Impersonation kann man auch aus einem geplanten Task der bspw. mit SYSTEM läuft heraus eine Meldung an den gerade angemeldeten User schicken ( so in etwa macht das auch die msg.exe), das geht bspw. so:
Zum Testen das ganze als geplanten Task und SYSTEM Rechten starten lassen, schon bekommt der gerade angemeldete User eine Msgbox zu Gesicht obwohl als geplanter Task mit SYSTEM gestartet.
Zum Testen das ganze als geplanten Task und SYSTEM Rechten starten lassen, schon bekommt der gerade angemeldete User eine Msgbox zu Gesicht obwohl als geplanter Task mit SYSTEM gestartet.
Add-Type -ReferencedAssemblies 'System', 'System.Runtime.InteropServices' -TypeDefinition @'
using System;
using System.Runtime.InteropServices;
namespace Runasuser
{
public static class ProcessExtensions
{
#region Win32 Constants
private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
private const int CREATE_NO_WINDOW = 0x08000000;
private const int CREATE_NEW_CONSOLE = 0x00000010;
private const uint INVALID_SESSION_ID = 0xFFFFFFFF;
private static readonly IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;
#endregion
#region DllImports
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
private static extern bool CreateProcessAsUser(
IntPtr hToken,
String lpApplicationName,
String lpCommandLine,
IntPtr lpProcessAttributes,
IntPtr lpThreadAttributes,
bool bInheritHandle,
uint dwCreationFlags,
IntPtr lpEnvironment,
String lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
private static extern bool DuplicateTokenEx(
IntPtr ExistingTokenHandle,
uint dwDesiredAccess,
IntPtr lpThreadAttributes,
int TokenType,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle);
[DllImport("userenv.dll", SetLastError = true)]
private static extern bool CreateEnvironmentBlock(ref IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hSnapshot);
[DllImport("kernel32.dll")]
private static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
private static extern uint WTSQueryUserToken(uint SessionId, ref IntPtr phToken);
[DllImport("wtsapi32.dll", SetLastError = true)]
private static extern int WTSEnumerateSessions(
IntPtr hServer,
int Reserved,
int Version,
ref IntPtr ppSessionInfo,
ref int pCount);
#endregion
#region Win32 Structs
private enum SW
{
SW_HIDE = 0,
SW_SHOWNORMAL = 1,
SW_NORMAL = 1,
SW_SHOWMINIMIZED = 2,
SW_SHOWMAXIMIZED = 3,
SW_MAXIMIZE = 3,
SW_SHOWNOACTIVATE = 4,
SW_SHOW = 5,
SW_MINIMIZE = 6,
SW_SHOWMINNOACTIVE = 7,
SW_SHOWNA = 8,
SW_RESTORE = 9,
SW_SHOWDEFAULT = 10,
SW_MAX = 10
}
private enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[StructLayout(LayoutKind.Sequential)]
private struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
private enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3,
}
[StructLayout(LayoutKind.Sequential)]
private struct STARTUPINFO
{
public int cb;
public String lpReserved;
public String lpDesktop;
public String lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public short wShowWindow;
public short cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
private enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation = 2
}
[StructLayout(LayoutKind.Sequential)]
private struct WTS_SESSION_INFO
{
public readonly UInt32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public readonly String pWinStationName;
public readonly WTS_CONNECTSTATE_CLASS State;
}
#endregion
// Gets the user token from the currently active session
private static bool GetSessionUserToken(ref IntPtr phUserToken)
{
var bResult = false;
var hImpersonationToken = IntPtr.Zero;
var activeSessionId = INVALID_SESSION_ID;
var pSessionInfo = IntPtr.Zero;
var sessionCount = 0;
// Get a handle to the user access token for the current active session.
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
{
var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
var current = pSessionInfo;
for (var i = 0; i < sessionCount; i++)
{
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
current += arrayElementSize;
if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
{
activeSessionId = si.SessionID;
}
}
}
// If enumerating did not work, fall back to the old method
if (activeSessionId == INVALID_SESSION_ID)
{
activeSessionId = WTSGetActiveConsoleSessionId();
}
if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)
{
// Convert the impersonation token to a primary token
bResult = DuplicateTokenEx(hImpersonationToken, 0, IntPtr.Zero,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, (int)TOKEN_TYPE.TokenPrimary,
ref phUserToken);
CloseHandle(hImpersonationToken);
}
return bResult;
}
public static bool StartProcessAsCurrentUser(string appPath, string cmdLine = null, string workDir = null, bool visible = true)
{
var hUserToken = IntPtr.Zero;
var startInfo = new STARTUPINFO();
var procInfo = new PROCESS_INFORMATION();
var pEnv = IntPtr.Zero;
int iResultOfCreateProcessAsUser;
startInfo.cb = Marshal.SizeOf(typeof(STARTUPINFO));
try
{
if (!GetSessionUserToken(ref hUserToken))
{
throw new Exception("StartProcessAsCurrentUser: GetSessionUserToken failed.");
}
uint dwCreationFlags = CREATE_UNICODE_ENVIRONMENT | (uint)(visible ? CREATE_NEW_CONSOLE : CREATE_NO_WINDOW);
startInfo.wShowWindow = (short)(visible ? SW.SW_SHOW : SW.SW_HIDE);
startInfo.lpDesktop = "winsta0\\default";
if (!CreateEnvironmentBlock(ref pEnv, hUserToken, false))
{
throw new Exception("StartProcessAsCurrentUser: CreateEnvironmentBlock failed.");
}
if (!CreateProcessAsUser(hUserToken,
appPath, // Application Name
cmdLine, // Command Line
IntPtr.Zero,
IntPtr.Zero,
false,
dwCreationFlags,
pEnv,
workDir, // Working directory
ref startInfo,
out procInfo))
{
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
throw new Exception("StartProcessAsCurrentUser: CreateProcessAsUser failed. Error Code -" + iResultOfCreateProcessAsUser);
}
iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error();
}
finally
{
CloseHandle(hUserToken);
if (pEnv != IntPtr.Zero)
{
DestroyEnvironmentBlock(pEnv);
}
CloseHandle(procInfo.hThread);
CloseHandle(procInfo.hProcess);
}
return true;
}
}
}
'@
[Runasuser.ProcessExtensions]::StartProcessAsCurrentUser("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"," -EP Bypass -C `"Add-Type -A System.Windows.Forms;[System.Windows.Forms.MessageBox]::Show('Das ist eine Nachricht!','Benachrichtigung',0,64)`"","C:\windows",$false)
Ich habe jetzt mal den Stumpf in die PS ISE reinkopiert, da kam allerdings:
Das ist dein Fehler, das läuft dort nicht mit gleichem Account, das muss zwingend in der Ausführung in einem anderen Account-Kontext laufen,dann klappt's...Also bspw. als geplanter Task mit SYSTEM als ausführender Account.
Variable falsch verwendet, die darf nicht escaped werden und muss in Hochkommas/Anführungszeichen.
$text_string = "Hallo_Welt"
[Runasuser.ProcessExtensions]::StartProcessAsCurrentUser("C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"," -EP Bypass -C `"Add-Type -A System.Windows.Forms;[System.Windows.Forms.MessageBox]::Show('$text_string','Benachrichtigung',0,64)`"","C:\windows",$false)
Das ist Quatsch mit Soße, klar kann es das! Man muss nur den Machine Account auf dem Share berechtigen, bzw. eine Gruppe in dem der Computeraccount Mitglied ist z.B. die Gruppe "Domänen-Computer"!
Zitat von @SPSman:
Ok ich habe mich ungenau ausgedrückt. in der Aktuellen Einstellung geht das nicht. Test-Path gib einen Fehler aus das das Netzlaufwerk nicht vorhanden ist.
Normal wenn man keine UNC Pfade nutzt. Vorhandene Netzlaufwerke werden ausschließlich Account-Abhängig gemappt!!Ok ich habe mich ungenau ausgedrückt. in der Aktuellen Einstellung geht das nicht. Test-Path gib einen Fehler aus das das Netzlaufwerk nicht vorhanden ist.
Gibt es eine Option innerhalb des Scripts wo auf Ordner mit den Berechtigungen des aktuell angemeldeten Users zugegriffwird?
Zugriffe auf gemappte Netzlaufwerke des Users geht nur mit einem Registry-Eintraghttps://www.fmsinc.com/microsoftaccess/developer/mapped_drives_not_avail ...
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
"EnableLinkedConnections"=dword:00000001
Ein Zugriff von Computer-Konto auf das Share würde ich jetzt erstmal nicht freigeben wollen, der Security wegen...
Normale User können damit eh nichts anfangen.Zitat von @SPSman:
Jetzt ist nur die Frage ob eine Freigabe für Domänen Computer nicht ein Sicherheitsrisikow darstell, dass wenn ein Lokales Konto auf den UNC Pfad zugreift, er durch dieses Tor ungewollt Zugriff erhält...
Nein, der Prozess muss im Kontext von SYSTEM laufen, das kann ein normaler User nicht das können nur lokale Admins.Jetzt ist nur die Frage ob eine Freigabe für Domänen Computer nicht ein Sicherheitsrisikow darstell, dass wenn ein Lokales Konto auf den UNC Pfad zugreift, er durch dieses Tor ungewollt Zugriff erhält...
Kann ich der Funktion (letzte Zeile) nicht einfach eine "*.PS1" Datei mitgeben die dann im Userkontext des angemeldeten User läuft?
Sicher war ja nur ein Beispiel mit command ...Ein ToDo habe ich noch die Massage soll nach 60s wieder verschwinden
Nimmst du statt der Messagebox einfach ein Popup, das kann das out of the box(New-Object -Com Wscript.Shell).Popup("Das ist eine Nachricht",60,"Mein Titel",64)
s geht jetzt nicht um den Process "SENDE TEXT" sondern um die Share-Zugiffsberechtigung im allgemeinen.
Um zuzugreifen müsste sich der User als SYSTEM ausgeben und das kann er nunmal nicht das können nur lokale Admins ...sendMassage("Achtung","CSV-123")
Weil du die Funktion falsch aufrufst ... In PowerShell werden keine Klammern und auch keine Kommas zur Trennung der Parameter in einem Funktionsaufruf verwendet ! Also bitte erst mal die Grundlagen "einmassieren".sendMassage "Achtung" "CSV-123"
Und massieren tut die Funktion auch nicht 😂😂😂, wäre aber schön wenn sie es bei mir täte ...🤪
Serie: Powershell
Powershell für User in KMU6GPO Passwortrichtlinie finden18Powershell: Office Programme inkl.- Bit-Version Auslesen2W11: Aufgabenplanung führt PS Copy-item nicht aus3Powershell remote Session: UnauthorizedAccessException3Powershell eigenes Objekt in Funktion verändern2Powershell individueller Rückgabewert von AddClick7Aufgabenplanung: Powershell Script im Hintergrund mit Userbenachrichtigung im VordergrundPowershell: Aufgabe im Hintergrund MsgBox in Vordergrund25