In Windows I have console programs that run in the background with the console hidden. Is there anyway to direct input to the programs console? I want to be able to do something like:
echo Y| *the_running_process_here*
to send Y to the process' stdin.
As far as I know this is not possible by basic cmd commands. PowerShell is more powerful though and can use the .NET framework from windows.
I found this PowerShell script which claims to pass text to an already opened cmd:
$psi = New-Object System.Diagnostics.ProcessStartInfo;
$psi.FileName = "cmd.exe"; #process file
$psi.UseShellExecute = $false; #start the process from it's own executable file
$psi.RedirectStandardInput = $true; #enable the process to read from standard input
$p = [System.Diagnostics.Process]::Start($psi);
Start-Sleep -s 2 #wait 2 seconds so that the process can be up and running
$p.StandardInput.WriteLine("dir"); #StandardInput property of the Process is a .NET StreamWriter object
Source: https://stackoverflow.com/a/16100200/10588376
You would have to safe this script with an .ps1 ending and run it.
A workaround to use this as a cmd command would be to make it accept arguments, so that you can run it with yourScript.ps1 procced_pid arguments for example.
The standard windows cmd is just too limited to fullfill such a task by its own.
Related
This scripts works fine when executed from Powershell console...
but does not work when executed with Powershell.exe from CMD.exe...
(powershell.exe -file script.ps1, using Powershell 5.1.17763.771)
# display Windows Shell Folder propertes
$App = New-Object -ComObject Shell.Application;
$AppNS = $App.NameSpace( "c:\windows" );
$AppNS.Self.InvokeVerb( "Properties" );
I tested other GUI objects (Winforms & WPF)
and they work fine...
?any ideas...
The problem is that the in-process COM object you're creating goes out of scope when the calling process exits, which in your case, when called from cmd.exe via PowerShell's CLI, means that the window typically never even gets a chance to display or is automatically closed after a very brief appearance.
In an interactive PowerShell session, the process lives on after exiting the script - that's why your code works there.
When you invoke a script via via PowerShell's CLI (powershell.exe for Windows PowerShell, pwsh for PowerShell Core, without the -NoExit switch to keep the process alive indefinitely), the PowerShell process exits when the script terminates.
Use of -NoExit would be a stopgap at best, because it would keep the PowerShell process around indefinitely, even though you presumably want it to live only for as long as the Properties dialog window is open - whenever the user chooses to close it.
Therefore, you need to synchronously wait for (a) the Properties dialog window to open and then (b) wait for it close before exiting the script.
You can do this with the help of the .NET UI Automation library as follows; note that the code uses PowerShell v5+ syntax:
using namespace System.Windows.Automation
# Load the UI Automation client assemblies.
# Requires Windows PowerShell or PowerShell Core v7+ (on Windows only).
Add-Type -AssemblyName UIAutomationClient; Add-Type -AssemblyName UIAutomationTypes
# Initiate display of the Windows folder's Properties dialog.
$App = New-Object -ComObject Shell.Application
$AppNS = $App.NameSpace('c:\windows')
$AppNS.Self.InvokeVerb('Properties')
# Comment out this line to suppress the verbose messages.
$VerbosePreference = 'Continue'
Write-Verbose 'Wating for the window''s creation...'
do {
# Search among the current process' top-level windows for a winow
# with class name '#32770', which is what the Properties dialog windows
# use (don't know why, but it has been stable over time).
$w = [AutomationElement]::RootElement.FindFirst([TreeScope]::Children,
[AndCondition]::new(
[PropertyCondition]::new([AutomationElement]::ClassNameProperty, '#32770'),
[PropertyCondition]::new([AutomationElement]::ProcessIdProperty, $PID)
)
)
Start-Sleep -Milliseconds 100
} while (-not $w)
Write-Verbose 'Window has appeared, waiting for it to close...'
while ($w.Current.ProcessId) {
Start-Sleep -Milliseconds 100
}
Write-Verbose 'Window is now closed, moving on.'
# At this point, if the script was invoked via PowerShell's CLI (powershell.exe -file ...)
# the PowerShell process terminates.
Now, invoking your PowerShell script as follows from your batch file will pop up the Properties dialog and wait for it to close before continuing:
#echo off
:: # ... your batch file
:: # Pop up the Properties dialog and *wait for it to close*.
powershell.exe -file script.ps1
:: # ...
If, by contrast, you simply want to launch the Properties dialog while continuing to run your batch file (be sure to disable the verbose messages first):
:: # Only *initiate* display of the Properties dialog and *continue execution*.
start /B powershell.exe -file script.ps1
Seems like it has to wait for the graphics to finish. "get-childitem | out-gridview" does a similar thing. Or add "sleep 120" to the end of the script, or find some other way to wait. Killing the script kills the window.
powershell -noexit .\explorer.ps1
Running tcl 8.4.13, I have a tcl script to execute my NSIS installation application (duly signed by microsoft) that used to work but now fails (windows 7 and 10), maybe because of windows security update or something? The same tcl script works fine when the target is a local/renamed copy of notepad.exe. The NSIS application works fine when run from from the command line.
The tcl script looks like the following, run via tclkit-win32 tclmnu.tcl, where tclmnu.tcl looks like this:
#! /bin/sh -x
# \
exec wish "$0" "$#"
#
package require Tk
#set runcmd notepad_local_copy.exe
set runcmd my_nsis_app.exe # this doesn't work
# this works with the notepad_local_copy.exe (above) but not my_nsis_app.exe
set catchcode [ catch { exec ${runcmd} } result ]
# also tried this, doesn't work either
#set catchcode [ catch { exec "runas /usr:administrator: ${runcmd}" } result ]
tk_messageBox -type ok -icon error -message "DEBUG: catchcode=${catchcode}"
# catchcode is 0 when runas=notepad_local_copy.exe, 1 when it's my_nsis_app.exe
Well, I solved it by adding cmd /c, as in
set catchcode [ catch { cmd /c exec ${runcmd} } result ]
I don't know why it needs that, nor why it seemed to work before and not now.
I don't know anything about TCL but I'm assuming that exec calls CreateProcess. When UAC was added to Vista it seems Microsoft deemed CreateProcess too low-level to be able to perform elevation.
Dealing with Administrator and standard user’s context:
CreateProcess and CreateProcessWithLogonW do not have new flags to launch the child process as elevated. Internally, CreateProcess() checks whether the target application requires elevation by looking for a manifest, determining if it is an installer, or if it has an app compat shim. If CreateProcess() determines the target application requires elevation, it simply fails with ERROR_ELEVATION_REQUIRED(740). It will not contact the AIS to perform the elevation prompt or run the app. If CreateProcess() determines the application doesn’t require elevation, it will spawn it as a new process.
ShellExecute[Ex] however is able to contact the UAC service and display the UAC elevation dialog:
To programmatically launch a child process as an elevated process, two things must occur: first, the executable of the child process needs to be identified as needing elevation, and second, the parent process needs to use ShellExecute() or ShellExecuteEx().
It seems there are multiple ways to get TCL to call ShellExecute:
eval exec [auto_execok start] {""} [list [file nativename $filename]] ;# http://wiki.tcl.tk/2809
or
twapi::shell_execute -path filename.exe ;# http://www.magicsplat.com/blog/how-do-i-exec-thee/
You can also request elevation of executables even if they are not marked as requiring elevation:
twapi::shell_execute -path filename.exe -verb runas
(The runas verb is not the same thing as runas.exe)
NSIS scripts use the RequestExecutionLevel attribute to mark the executable with the desired UAC elevation level.
Using cmd /c is a bit of a hack and will probably display a console window for a brief period of time. cmd /c first tries CreateProcess and then falls back to ShellExecuteEx if elevation was required. I'm not sure if this is documented behavior.
I have windows 2012 server with LSI Megaraid controller. I am able to get raid status using below command in powershell and redirect output to a file.
C:\> MegaCli64.exe -LDInfo -Lall -aALL | Out-File raid.txt
However I tried to run same command using task scheduler and it is not working. I want to send the output of raid command or send raid.txt to a mail using task scheduler.
Some piece of advise for sceduled jobs :
1) Always set the working directory if you work with local files.
2) Always use the full path (not the relative one) for the programs you call and the files you manipulate. Do not expect the exe you call being in the paths pointed by $env:path.
3) Put your commands into a script file and make sure that a log (any type), with a time stamp, is composed each time your script is called.
4) Here is one way, but it exits multiples ways, to register your script in the scheduler :
# First set a Trigger
$SixInTheMorning = New-JobTrigger -Daily -At "06:00 AM"
# Second set your script
$scriptPath1 = 'C:\Batchs\WS_PowerShell\Myscript.PS1'
# Third register you job
Register-ScheduledJob -Name "AJobName" -FilePath $scriptPath1 -Trigger $scriptPath1
I have finally finished my powershell wpf application for internal use at my company, and it runs as expected when called from a powershell window, however when I try to call it from CMD, or from Visual Studio as an external tool, the GUI window never shows up.
My application functions very closely to this write up, with a few changes to the display and functions that run in the background.
Can anyone explain why my ps1 file does not display the gui when I call it from CMD, but it does when called from the powershell command window? Here is the gist of the script:
$Global:syncHash = [hashtable]::Synchronized(#{})
$newRunspace =[runspacefactory]::CreateRunspace()
$newRunspace.ApartmentState = "STA"
$newRunspace.ThreadOptions = "ReuseThread"
$newRunspace.Open()
$newRunspace.SessionStateProxy.SetVariable("syncHash",$syncHash)
[void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
$psCmd = [PowerShell]::Create().AddScript({
[xml]$xaml = #"
<valid wpf>
"#
$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$syncHash.Window=[Windows.Markup.XamlReader]::Load( $reader )
[xml]$XAML = $xaml
$xaml.SelectNodes("//*[#*[contains(translate(name(.),'n','N'),'Name')]]") | %{
#Find all of the form types and add them as members to the synchash
$syncHash.Add($_.Name,$syncHash.Window.FindName($_.Name) )
}
## Custom functions for items in syncHash here ##
$syncHash.Window.ShowDialog() | Out-Null
$syncHash.Error = $Error
})
$psCmd.Runspace = $newRunspace
$date = $psCmd.BeginInvoke()
I am calling this script in cmd.exe "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe C:\pathToFile\file.ps1"
My Execution policy has been updated so that I can run scripts as well.
Thanks for all the help, I got it figured out. For some reason I needed to run the command with the NoExit option
powershell.exe -ExecutionPolicy Bypass -NoExit -File "pathToFile\File.ps1"
Sometimes, the answer is "no". This turns out not to be a question, but more of a cri de coeur (OK, just complaining).
The Team Foundation Server 2010 PowerShell snap-in exports a cmdlet called Get-TfsPendingChange, which does what it sounds like.
If there are no pending changes, Get-TfsPendingChange echoes "There are no pending changes." on standard output. I never asked it to. The following attempts at suppressing that output all fail:
$pcs = $(Get-TfsPendingChange -Server $server "$/foo/bar" -User $env:USERNAME -r ) | out-null;
$($pcs = Get-TfsPendingChange -Server $server "$/foo/bar" -User $env:USERNAME -r ) | out-null;
$pcs = Get-TfsPendingChange -Server $server "$/foo/bar" -User $env:USERNAME -r | out-null;
I'm not getting errors, but the text output is not redirected. What IS redirected, is the normal successful output of the cmdlet, in the case where there are pending changes for it to return. The ONLY output you can suppress is the useful output. If there are zero pending changes, you get text garbage via stdout. If there are any actual pending changes, that stream (of objects, not text) can be redirected to the null device, but that's worse than useless.
So it appears that text on stdout is a side effect from this cmdlet, not the cmdlet's output, and it cannot be redirected in PowerShell. The PowerShell redirect operators > and |, without the fileno specifiers (2 in PS2, 2, 3, 4, 5, and * in PS3/PS4), work on the object stream, which is not stdout. This would be fine if stdout had been replaced by this new object stream concept (or if some genius on the TFS team hadn't decided that cmdlets should spew random text noise in all directions as a side effect).
The garbage output definitely is on stdout, not stderr. I can redirect the standard output of the whole script to nul when I run it from cmd.exe, and that works as expected, because duh:
c:\>powershell .\tfstest.ps1 > nul
However, I do not want to suppress all the output from the entire script. Just the output from one snapin cmdlet. It's not the end of the world, but it's irritating to have random stuff that I call echoing garbage I have no control over. The script otherwise works correctly (see UPDATE).
One workaround would be to prefix everything I want to echo on stdout with some arbitrary string (say "KLUDGEPREFIX"), and run the whole mess from a batch file which pipes the PowerShell script's output through find /v KLUDGEPREFIX and then through a PowerShell fragment that strips off the prefix from each line.
Instead, I'm going to write the rest of the script around the problem so it looks like what they get is what I intended, because, in fact, I was going to tell them they didn't have any pending changes anyhow. It's been a valuable exercise, however, in terms of getting my head inside PowerShell.
UPDATE: This script turned out to be fragile, unreliable, glacially slow, and impossible to run on any computer but the one where I originally wrote it.
I replaced it with a Perl script that took 1/10 the time to write, executed 10 times as fast, and does the same task better without any foolishness. PS and the TFS PS snap-in are great ideas but they both need a lot of work before they'll be ready for release.
I'm not familiar with Team Foundation Server, but most likely the cmdlet writes to the host rather than one of the output streams. Output to the host cannot be redirected in PowerShell, but if you run a PowerShell script in CMD the host output goes to STDOUT (as does the success output stream) and can be redirected there.
Demonstration:
PS C:\> cat .\test.ps1
Write-Host 'foo'
PS C:\> .\test.ps1 | Out-Null
foo
PS C:\> .\test.ps1 >$null
foo
PS C:\> & cmd.exe /c powershell.exe -File .\test.ps1
foo
PS C:\> & cmd.exe /c powershell.exe -File .\test.ps1 `>nul
PS C:\> & cmd.exe /c powershell.exe -File .\test.ps1 >$null
PS C:\> _
if you add 2 in front of the > it will only redirect error. likewise with 1 for non errors (stdout). So if you want all stdout, but not stderr to go away you can type
$pcs = Get-TfsPendingChange -Server $server "$/foo/bar" -User $env:USERNAME -r 1> $null
$null can be used as an equivalent to /dev/null. your code probably creates the file "nul" on disk.