I have a functions that only works on some scensarios.
It works on Powershell ISE, but when I save the same thing in a .Ps1 file and run it, it doest not work.
I have the function as part of a big script. It works and open the Window when I run it on Windows 7 but doest not run on Windows Server 2008 R2.
Why?
The script with the function and its calling is:
Function Get-SaveFileTxt($initialDirectory)
{
[System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") |
Out-Null
$SaveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
$SaveFileDialog.initialDirectory = $initialDirectory
$SaveFileDialog.AddExtension = $true
$SaveFileDialog.DefaultExt = "txt"
$SaveFileDialog.filter = "Text Files (*.txt)| *.txt"
$SaveFileDialog.ShowDialog() | Out-Null
$SaveFileDialog.filename
}
Get-SaveFileTxt
Not sure if this is the case here but I remember a bug that the opened dialog doesn't take focus and appear behind other opened windows. Can you confirm?
UPDATE:
Set the ShowHelp property to $true.
$SaveFileDialog.ShowHelp = $true
It works in the ISE because ISE's apartment state is STA by default and your powershell mode is MTA, you can check it with:
[System.Threading.Thread]::CurrentThread.ApartmentState
Your code will work if you open powershell in STA mode:
powershell.exe -STA
Related
It seems like the command history of PowerShell is stored separately from PowerShell ISE.
But I have one question is that where is the commands history of PowerShell ISE stored at and how do I view them?
As for the location of history files; they are located here:
Get-ChildItem -Path "$env:USERPROFILE\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine" |
Format-Table -AutoSize
# Results
<#
Directory: C:\Users\YourUserName\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 17-May-21 02:23 258925 ConsoleHost_history.txt
-a---- 11-May-21 01:20 120222 Visual Studio Code Host_history.txt
-a---- 27-Jun-20 18:58 0 Windows PowerShell ISE Host_history.txt
#>
As noted, in the ISE, you can just leverage the Start-Transcript cmdlet to capture all you do, but again, it is not an out-of-box/console/ISE immediate lookup of previous commands to execute like PSReadLine. Yet, you can do that, sort of, with some creativity. Yet, that's just a kludge.
A note about using Start-Transcript. It will default to your "$env:USERPROFILE\Documents" folder.
So, I'd recommend you set to go to a specific folder. Also, though the files can be small, there will be tons of them over time, thus, you need to manage that.
They of course can just be opened in the ISE directly:
psEdit -filenames ((Get-ChildItem -Path "$env:USERPROFILE\Documents" | Sort-Object -Property LastWriteTime -Descending)[0]).FullName
Yet, since you are already in the ISE, you can just type and run all commands from the editor pane, and as needed, select anyone and just run it again.
Yet if you are just using the ISE console, and thinking it is the same as the PowerShell consolehost, then that's wrong. It's really an output window with some console skills. Back in the early ISE days, there were 3 panes. Editor, output window and true console.
The true console got removed in later ISE versions. Why, who knows? VSCode almost got us back there.
If you want to do the console stuff, then use the PowerShell console, or shell to the PowerShell console using runspaces to stay in the ISE.
For the runspaces thing, here is an example to run PowerShell Core (v6+), while still in the ISE.
Using PowerShell 7 in the Windows PowerShell ISE
https://old.ironmansoftware.com/using-powershell-core-6-and-7-in-the-windows-powershell-ise
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Clear()
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Switch to PowerShell 7", {
function New-OutOfProcRunspace {
param($ProcessId)
$ci = New-Object -TypeName System.Management.Automation.Runspaces.NamedPipeConnectionInfo -ArgumentList #($ProcessId)
$tt = [System.Management.Automation.Runspaces.TypeTable]::LoadDefaultTypeFiles()
$Runspace = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace($ci, $Host, $tt)
$Runspace.Open()
$Runspace
}
$PowerShell = Start-Process PWSH -ArgumentList #("-NoExit") -PassThru -WindowStyle Hidden
$Runspace = New-OutOfProcRunspace -ProcessId $PowerShell.Id
$Host.PushRunspace($Runspace)
}, "ALT+F5") | Out-Null
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add("Switch to Windows PowerShell", {
$Host.PopRunspace()
$Child = Get-CimInstance -ClassName win32_process | where {$_.ParentProcessId -eq $Pid}
$Child | ForEach-Object { Stop-Process -Id $_.ProcessId }
}, "ALT+F6") | Out-Null
So, with a bit of tweaking, you can do the same for Windows PowerShell 5 to get the raw console
But again,m avoid all the hoops, and use VSCode, if you are allowed. Yet on your servers, we all know ISE is still a thing. ;-}
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
My university requires all computers to perform a web-based login in order to get-access to the internet, and claims that all users will log-off automatically in the mid-night (sounds strange, but it is true), so I am trying to write a powershell script (in Windows 10) to perform automatic login at mid-night.
My script is list here. It opens an IE process in the background (in a nonvisible way), fill in the username and password, login, and kills the IE process.
# If there are existing Internet Explorer processes, close it
$IE_Process = Get-Process iexplore -ErrorAction Ignore
if ($IE_Process) {
$IE_Close = Foreach-Object { $IE_Process.CloseMainWindow() }
}
Stop-Process -Name "iexplore" -ErrorAction Ignore
# Login Information
$url = "http://xxx.xxx.xxx.xxx/"
$username = "xxxxxxxx"
$password = "xxxxxxxx"
# Open an IE process
$ie = New-Object -com internetexplorer.application;
$ie.silent = $true
$ie.navigate($url);
while ($ie.Busy -eq $true)
{
Start-Sleep -s 1;
}
# The stupid webpage needs to submit twice
$ie.Document.getElementById("loginname").value = $username
$ie.Document.getElementByID("password").value = $password
$ie.Document.getElementById("button").Click()
Start-Sleep -s 1;
$ie.Document.getElementById("loginname").value = $username
$ie.Document.getElementByID("password").value = $password
$ie.Document.getElementById("button").Click()
# Close the IE process
$IE_Process = Get-Process iexplore -ErrorAction Ignore
if ($IE_Process) {
$IE_Close = Foreach-Object { $IE_Process.CloseMainWindow() }
}
Stop-Process -Name "iexplore" -ErrorAction Ignore
Remove-Variable -Name ie,username,password,url,IE_Process -ErrorAction Ignore
The script is saved as "login_IE.ps1". It may be poorly written as I am new to powershell, but it works. If I open an cmd window and execute the following command, I am logged in.
C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe -ExecutionPolicy RemoteSigned -File C:\Users\MyName\Documents\Powershell\login_IE.ps1
However, if I create a scheduled task in windows task scheduler executing this script, it doesn't work. I fill the "Program/script:" as:
C:\WINDOWS\system32\WindowsPowerShell\v1.0\powershell.exe
and fill the "Add arguments (optional):" as:
-ExecutionPolicy RemoteSigned -File C:\Users\MyName\Documents\Powershell\login_IE.ps1
The scheduled task is run under my account (I am the only user of this computer).
If I run the scheduled task manually, in the task manager I can see two IE process opened in the "Background process", communicate with the internet, and then get killed, so I am pretty sure that the script has actually been executed. But I found I am not logged in since I don't have internet access, where could the problem be?
Any advice is really appreciated. Thanks in advance.
Similar type of issue I had, when trying to run the script directly from Powershell window it works as expected, but from the task scheduler or command line both not getting the desired results.
Commenting and adding lines like below in my script, help me to run the script from command line and task scheduler as well
$AllProtocols = [System.Net.SecurityProtocolType]'Ssl3,Tls,Tls11,Tls12'
[System.Net.ServicePointManager]::SecurityProtocol = $AllProtocols
[Net.ServicePointManager]::SecurityProtocol =
[Net.SecurityProtocolType]::Tls12
Hope this helps anyone else.
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"
disclosure
I'm a PHP/Linux developer that is having to get use to working in a Windows environment, so I very well my be missing something fundamental in the question. I've researched the heck out of this and can't seem to pinpoint a solution. Thanks for your help in advanced.
I have a batch file that calls a powershell script that doesn't work correctly when it is started by the window's task scheduler, but works perfectly when it is launched by hand.
Below is the Powershell script that the batch file is launching...
$WatchFolder = '\\networkshare\foldername'
$Filter = '*.csv'
$fsw = New-Object IO.FileSystemWatcher $WatchFolder, $Filter -Property #{IncludeSubdirectories = $false;NotifyFilter = [IO.NotifyFilters] 'LastWrite'}
Register-ObjectEvent $fsw Changed -SourceIdentifier SAS_Poll_FileChanged -Action {
$WatchFolder = '\\networkshare\foldername'
$OutputFolder_one = '\\networkshare\outputFolder_One'
$OutputFolder_two = '\\networkshare\outputFolder_Two'
$name = $Event.SourceEventArgs.Name
$lock_file = $WatchFolder + '\.' + $name + '.lock'
$test = (Test-Path "$lock_file")
if ( $test ){
return
} else {
Set-ExecutionPolicy -Scope CurrentUser Bypass
Out-File -FilePath "$lock_file"
Start-Process powershell -ArgumentList ".\General\PollingProcess-alone.ps1 $WatchFolder $MainFrameFolder $SASFolder $name $lock_file" -NoNewWindow
}
}
I know the issue occurs at the following line...
Start-Process powershell -ArgumentList ".\General\PollingProcess-alone.ps1 $WatchFolder $MainFrameFolder $SASFolder $name $lock_file" -NoNewWindow
I know this b/c when the event is triggered ( when the script is launched via the task scheduler ), the lock file is created and then the process hangs.
I therefore think that the issue has something to do with path of second powershell script I'm calling, but I don't know how to fix it. I've tried using the full path of the second script, but haven't been able to make it work.
Just to give you some more context of the script, it is sort of important the the event process spins up a new powershell script b/c I need these scripts to run concurrently.
Pretty sure the problem is with your argument list, right now you are passing a single string with everything contained within it, change that to an array and things should work properly.
so convert
".\General\PollingProcess-alone.ps1 $WatchFolder $MainFrameFolder $SASFolder $name $lock_file"
to
".\General\PollingProcess-alone.ps1",$WatchFolder,$WatchFolder,etc..
give that a shot and let us know if it works for you, also want to say that your code is impressive for being relatively new to PowerShell so kudos lol.