Prevent Shutdown - C#, Powershell or CMD/Bat - c#

I would like to ask how to prevent shutdown, when running a script or at least give a popup that will ask whenever or not you want to shutdown (like when you open a notepad and write a char, but doesn't save it and the click shutdown).
I have been creating scripts that runs installers silent, but some of them still seems to activate windows shutdown (this can happen if they are missing prerequisites).
Here is the code I use for the installation:
# --- Install ---
$fileExtension = (Get-ChildItem -path $installationFilePath).Extension
if(".msi" -eq $fileExtension)
{
[string[]]$Private:args = New-Object string[] 4
$args[0] = "/qn"
$args[1] = "TRANSFORM=1033.mst"
$args[2] = "REBOOT=Suppress"
$args[3] = "/l*v $errorLogPath"
$process = Start-Process $installationFilePath -ArgumentList $args -PassThru
}
if(".exe" -eq $fileExtension)
{
[string[]]$Private:args = New-Object string[] 2
$args[0] = '/v"' + "/qn TRANSFORM=1033.mst REBOOT=Suppress /l*v $errorLogPath" + '"'
$args[1] = "/s"
$process = Start-Process $installationFilePath -ArgumentList $args -PassThru
}
$processActive = $process
while ($processActive -ne $null)
{
Start-Sleep -Seconds 1
Write-Host '.' -NoNewline
$processActive = Get-Process -Id $processActive.Id -ErrorAction SilentlyContinue
}
I know this should be possible, but I have yet to find out how.

Here is an example of aborting shutdown after the install has finished:
Start-Process yourprogram.exe -Wait
shutdown /a
You could even loop the abort a few times to make sure you hit it.
for($i=0;$i -lt 5;$i++)
{
shutdown /a
sleep 1
}

Related

Powershell read the output of the process is already running

I have a powershell script which runs an exe and get its output which is working fine, but the issue that i am facing is that if the process is already running then it is not able to capture the output.
Below is my code
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "E:\Program Files (x86)\Consul_Ent\Bin\consul.exe"
$pinfo.Arguments = "agent -config-dir E:\Consul_Ent\Configuration\Client"
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $false
$pinfo.RedirectStandardInput = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardError = $true
$pinfo.EnvironmentVariables["GOMAXPROCS"] = (Get-WmiObject Win32_Processor | measure -p NumberOfLogicalProcessors -sum).Sum
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
[Void]$process.Start()
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
try
{
while (!$process.WaitForExit(1))
{
}
Write-Host "WaitForExit(1)"
}
finally
{
# give the thread gracefully shutdown
Start-Sleep -s 3
}
so as the process is not terminated yet and hence it is not able to read the output as it tries to create a new instance, i want it to create a new instance only if process is not running but if it is running i just want to capture the output.

ExitCode always has null does not show the actual return value from exe

I am calling my exe from powershell script like shown below.
$file = $PSScriptRoot + "\executor.exe"
$code = (Start-Process -WindowStyle Hidden $file -Verb runAs -ArgumentList $Logfile).StandardOutput.ToString;
$nid = (Get-Process "executor.exe").id
Wait-Process -Id $nid
if ($code -eq 1) {
LogWrite "Execution succeeded"
} else
{
LogWrite "Execution Failed"
}
I have a int main function in my exe program which would return 1 on success, and 0 on failure.
When i try to get the ExitCode(using $LASTEXITCODE) from powershell script it always shows null(neither 1 nor 0), But my exe is returning 1 as expected.
How do i capture the return value of exe in powershell script?
You can use this:
$psi = New-Object System.Diagnostics.ProcessStartInfo
$psi.FileName = # path to your exe file
# additional options:
$psi.UseShellExecute = $false
$psi.CreateNoWindow = $false
$psi.WindowStyle = "Maximized"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $psi
$p.Start() | Out-Null # returns $true if the process started, $false otherwise
$p.WaitForExit()
# here's the exitcode
$exitCode = $p.ExitCode
Create the process start info, to specify the executable path, and additional options. It's important to use .WaitForExit() to wait until the process finishes.
What you have tried doesn't get the app exitcode, but what the application write to standard console, which, in your case, I assume is nothing. If you could modify the exe to write to the console, what you did would work.

How to Self-Sign a .NET exe generated with SharpDeveloper or built-in csc.exe?

Let's say I compile my c# application using csc.exe distributed with Windows 10 without installing Visual Studio, How can I self sign this application? The best I can come up with is this crazy powershell script posted below... Seems to me that it should be easier to sign your application than that.
# SCRIPT: signit.ps1
#
# Purpose: Sign a .NET Exe compiled by SharpDeveloper with a SelfSignedCertificate
#
# Usage:
# Run signit.ps1 Script from an Administrator Powershell
#
# PS> Process-start -verb runas powershell
# PS(ADMIN)> Set-ExecutionPolicy -scope Process Unrestricted
# Yes
# PS(ADMIN)> ./signit.ps1
# Sign EXE with PFX Certificate using SHA1
function SignIt {
# Path to your Exe to sign
$exe = "$home\Desktop\tntrocketcar\bin\Debug\tntrocketcar.exe"
# Name of your company
$friendly_name = "ACME Software"
$subject_cn = "Wile E. Coyote Ventures" #Common Name
$subject_o = "Roadrunner Foundation" #Organization
$subject_e = "wile.e.coyote#mailinator.com" #Email
$subject_c = "US" #Country
$subject_st = "Arizona" #State
# Path to signtool installed from "Windows SDK" download
$signtool = "C:\Program Files (x86)\Windows Kits\10\bin\10.0.16299.0\x64\signtool.exe"
$pfx = "MySigniture.pfx"
$location = "Cert:\LocalMachine\My"
$tstamp = "http://timestamp.verisign.com/scripts/timstamp.dll"
try {
Write-Host "SignIt: $pfx"
if (![IO.File]::Exists($signtool)) {
write-host "`nERROR: signtool tool not found. Install WIndows SDK and update signtool.exe path in script.`n"
exit 1
}
$pwd = get-location
$pass1_sec = $null
$pass1_bstr = $null
$pass1_text = $null
# Creates a SelfSigned PFX Certificate and save it to current directory
if (![IO.File]::Exists("$pwd/MySigniture.pfx")) {
Write-Host "`n!!! Creating New SelfSignedCertficate !!!`n"
$pass1_sec = read-host "Password: " -AsSecureString
$pass1_bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass1_sec)
$pass1_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto($pass1_bstr)
$pass2_sec = read-host "Re-Enter Password: " -AsSecureString
$pass2_bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass2_sec)
$pass2_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto($pass2_bstr)
if ($pass1_text -ceq $pass2_text) {
Write-Host "Passwords matched"
}
else {
Write-Host "Passwords differ. Aborting script."
exit 1
}
$subject="CN=${subject_cn},O=${subject_o},E=${subject_e},C=${subject_c},ST=${subject_st}"
$cert = New-SelfSignedCertificate `
-Type Custom `
-Subject $subject `
-KeyUsage DigitalSignature `
-CertStoreLocation $location `
-FriendlyName $friendly_name
$ThumbPrint = $cert.ThumbPrint
$provider = "${location}\${ThumbPrint}"
$tmp = Export-PfxCertificate `
-cert $provider `
-FilePath $pfx `
-Password $pass1_sec
del $provider
}
if ($pass1_sec -eq $null) {
$pass1_sec = read-host "Password: " -AsSecureString
$pass1_bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($pass1_sec)
$pass1_text = [Runtime.InteropServices.Marshal]::PtrToStringAuto($pass1_bstr)
}
& $signtool sign `
/a `
/t http://timestamp.verisign.com/scripts/timstamp.dll `
/f $pfx `
/p $pass1_text `
/v `
$exe
}
catch {
write-host "ERROR: Error Signing Exe."
throw
}
}
SignIt

Run PowerShell Unzip commands through C# Process.Start()

I want to extract a zip file in Downloads folder to Desktop using PowerShell and C#.
I need it to work with Windows 7, 8, & 10.
I'm trying to take these PowerShell commands
extract https://stackoverflow.com/a/36472063/6806643
overwrite https://stackoverflow.com/a/5711383/6806643
chain https://superuser.com/a/612413/740888
$shell = New-Object -ComObject shell.application
$zip = $shell.NameSpace("zip file path")
foreach ($item in $zip.items()) {
$shell.Namespace("unzip destination path").CopyHere($item)
}
And run it through a C# Process.Start()
Process.Start("powershell.exe",
"timeout 3; "
+ "$shell = New-Object -ComObject shell.application; "
+ "$zip = $shell.NameSpace(\"C:\\Users\\Matt\\Downloads\\MyFile.zip\"); "
+ "foreach ($item in $zip.items()) {$shell.Namespace(\"C:\\Users\\Matt\\Desktop\\\").CopyHere($item, 0x14)}"
);
Problem
PowerShell launches but fails to extract, and closes out before I can read the error.
However, if I copy paste those chained commands into PowerShell without C#, it works.
$shell = New-Object -ComObject shell.application; $zip = $shell.NameSpace('C:\Users\Matt\Downloads\MyFile.zip'); foreach ($item in $zip.items()) {$shell.Namespace('C:\Users\Matt\Desktop\').CopyHere($item, 0x14)}
This works but it's for PowerShell 5 only.
Process.Start("powershell.exe",
"timeout 3; Expand-Archive 'C:\\Users\\Matt\\Downloads\\MyFile.zip' -Force -DestinationPath 'C:\\Users\\Matt\\Desktop\\'"
);
I may have solved it while writing out the question and refactoring other chained commands.
I found this article and combined it with the other code.
https://www.howtogeek.com/tips/how-to-extract-zip-files-using-powershell/
Process.Start("powershell.exe",
"-nologo -noprofile -command "
+ "timeout 3; "
+ "$shell = new-object -com shell.application; "
+ "$zip = $shell.NameSpace('C:\\Users\\Matt\\Downloads\\MyFile.zip'); "
+ "foreach ($item in $zip.items()) {$shell.Namespace('C:\\Users\\Matt\\Desktop\\').CopyHere($item, 0x14)}"
);
The differences are:
adding -nologo -noprofile -command
-com instead of -ComObject
single quotes ' instead of double " around paths.
And chaining messages with Write-Host \"hello\" -NoNewLine instead of echo.

Why does this function break in Powershell?

I am trying to replicate this Self-Elevating script in Powershell, but as a function instead of as an entire script so to break up the code for better flow. The original code can be found here.
http://blogs.msdn.com/b/virtual_pc_guy/archive/2010/09/23/a-self-elevating-powershell-script.aspx
However, when I rewrite the same code as a function, it errors out dramatically. Any idea what is causing the fault? This is the new, erroneous code.
function SelfElevation
{
# Get the ID and security principal of the current user account
$myWindowsID=[System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal=new-object System.Security.Principal.WindowsPrincipal ($myWindowsID)
# Get the security principal for the Administrator role
$adminRole=[System.Security.Principal.WindowsBuiltInRole]::Administrator
# Check to see if we are currently running "as Administrator"
if ($myWindowsPrincipal.IsInRole($adminRole))
{
# We are running "as Administrator" - so change the title and background color to indicate this
$Host.UI.RawUI.WindowTitle = $myInvocation.MyCommand.Definition + "(Elevated)"
$Host.UI.RawUI.BackgroundColor = "DarkBlue"
$Host.UI.RawUI.ForegroundColor = "White"
clear-host
}
else
{
# We are not running "as Administrator" - so relaunch as administrator
# Create a new process object that starts PowerShell
$newProcess = new-object System.Diagnostics.ProcessStartInfo "PowerShell";
# Specify the current script path and name as a parameter
$newProcess.Arguments = $myInvocation.MyCommand.Definition;
# Indicate that the process should be elevated
$newProcess.Verb = "runas";
# Start the new process
[System.Diagnostics.Process]::Start($newProcess)
# Exit from the current, unelevated, process
Stop-Process -Id $PID
}
}
# We call the self elevation here
SelfElevation
# Run your code that needs to be elevated here
Write-Host -NoNewLine "Press any key to continue..."
$null = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
This code works fine as I truncate it, right up until the part where it hits Start $newProcess, and it simply does not seem to like the new process being executed in a function. However, why?
Thank you in advance for your time!
The arguments your script takes is likely to be slightly different than what Powershell.exe takes. Try this approach:
[string[]]$argList = #('-NoProfile', '-NoExit', '-File', $MyInvocation.MyCommand.Path)
$argList += $MyInvocation.BoundParameters.GetEnumerator() | Foreach {"-$($_.Key)", "$($_.Value)"}
$argList += $MyInvocation.UnboundArguments
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $pwd -ArgumentList $argList
Fix for "white spaces in path" problem and also additional code to set new powershell process path to actual script location
And another fix of error when script is located at mapped network drive
#region Self-Elevating
Set-Location ( Get-Item $script:MyInvocation.MyCommand.Path ).Directory
$myWindowsID = [System.Security.Principal.WindowsIdentity]::GetCurrent()
$myWindowsPrincipal = new-object System.Security.Principal.WindowsPrincipal( $myWindowsID )
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator
if ( !$myWindowsPrincipal.IsInRole( $adminRole )) {
# This fixes error wen script is located at mapped network drive
$private:scriptFullPath = $script:MyInvocation.MyCommand.Path
if ( $scriptFullPath.Contains([io.path]::VolumeSeparatorChar )) { # check for a drive letter
$private:psDrive = Get-PSDrive -Name $scriptFullPath.Substring(0,1) -PSProvider 'FileSystem'
if ( $psDrive.DisplayRoot ) { # check if it's a mapped network drive
$scriptFullPath = $scriptFullPath.Replace( $psdrive.Name + [io.path]::VolumeSeparatorChar, $psDrive.DisplayRoot )
}
}
[string[]]$argList = #( '-NoLogo', '-NoProfile', '-NoExit', '-File', "`"$scriptFullPath`"" )
$argList += $MyInvocation.BoundParameters.GetEnumerator() | % { "-$( $_.Key )", "$( $_.Value )" }
$argList += $MyInvocation.UnboundArguments
Start-Process PowerShell.exe -Verb Runas -WorkingDirectory $PWD -ArgumentList $argList -PassThru
Stop-Process $PID
}
#endregion
Thanks Keith Hill and Randy in Marin

Categories

Resources