Powershell script “on exit” event? - powershell

Instead of calling a function at the end of all scripts to perform cleanup tasks, I'm looking to register for an 'on return' event for when the script (not the PowerShell session) is finished.
A script can return at various points though (eg, no records to process), so the current situation is problematic.
Register-EngineEvent applies to the PowerShell session, and operators run scripts manually, thus it's problematic.
I can't find a list of built-in powershell events or an alternative solution.

#Vesper wrote it as a comment, but a try/finally block is definitely what I would suggest for this:
try {
# some code
} finally {
# this gets executed even if the code in the try block throws an exception
}

Related

How to find the current module in the DebuggerStop event handler?

I work on a PowerShell debugger implemented as a script, Add-Debugger.ps1.
It looks peculiar perhaps but there are use cases for it.
All works well except when the debugger stops at a breakpoint in a script module.
One of the debugger functions is to execute interactively typed user commands and show results.
The problem is that these commands are not invoked in the current script module scope but "somewhere else".
The problem may be worked around if the current module is known, say $module.
Then commands invoked as $module.Invoke() would work in the module scope.
But how to find/get this $module? That is the question.
NB $ExecutionContext.SessionState.Module does not seem to help, even if I get it using Get-Variable -Scope 1.
Because $ExecutionContext is for the DebuggerStop handler, not its parent in the module.
And Get-Variable is invoked "somewhere else" and does not get variables from the module.
I have found a workaround using this dynamically compiled piece of C#:
Add-Type #'
using System;
using System.Management.Automation;
public class AddDebuggerHelpers
{
public ScriptBlock DebuggerStopProxy;
public EventHandler<DebuggerStopEventArgs> DebuggerStopHandler { get { return OnDebuggerStop; } }
void OnDebuggerStop(object sender, DebuggerStopEventArgs e)
{
SessionState state = ((EngineIntrinsics)ScriptBlock.Create("$ExecutionContext").Invoke()[0].BaseObject).SessionState;
state.InvokeCommand.InvokeScript(false, DebuggerStopProxy, null, state.Module, e);
}
}
'#
It works and gives me the $module that I needed. By the way, for my
particular task I use it as $module.NewBoundScriptBlock(...) instead of
mentioned $module.Invoke(...), to preserve the current scope.
If another native PowerShell way without C# is found and posted, I'll accept it
as an answer even after accepting my own. Otherwise this workaround is the only known way so far.

Can a matlab function called within a script cause the script to break?

I am running a script which calls a function, and if a certain condition is met, inside the function, I want the whole thing just to terminate (and by that I do not mean I want to close matlab using exit). Is that possible? I know I can use return or break to return to the script, however I want the script to stop as well if this condition is met.
The only function I know of that does this is error. This throws an exception, and, if no exception handlers with try and catch are installed in the calling script, will terminate and return to command prompt. Which is what you want, as far as I understand. It prints an error message though. This could be suppressed if you guard all code in the top-level script with a try catch handler. However this will have to be specific to the one error and it makes debugging ("stop-on-error") much more difficult.
The thing is that the only use case I see for this behavior (termination of whole program on certain event) is when a non recoverable error occurs, and in that case printing an error message is indeed appropriate.
In case the script is successful termination of the whole program is not really the right way. All functions should return to give the upper layers of the code to perform some clean-up action, like saving the output data or so.

Powershell Error Catching With Static Member

For reasons I'll not get into here, I'm being forced to use powershell to call and then populate a separate application. And to integrate it into a batch file, so powershell -command "& { }", which is already painful. I've got a while loop set call, check for the process ID to come up, then wait and call again if it hasn't come up yet.
The problem here is that afterwards I utilize a static member out of visualbasic to switch the focus to that application.
Namely, [microsoft.visualbasic.interaction]::AppActivate($hwnd) -- where $hwnd is the process ID of the application in question.
I hate to put anything like an artificial timer on there to wait for the application to finish loading, and I'd love to just put a while timer in there. But static member calls don't appear to support -erroraction or -errorvariable -- and the try {} catch {} appears to just ignore the error, as I was hoping to use it to trigger a flag to trigger the while loop to cycle again after a sleep of one second.
What other ways are there to catch errors out of a static member operator ::
Disregard. The Try {} catch {} works great if I don't replace the final bracket with a close parenthesis.

PowerShell wait for function call to complete

I am calling a series of PowerShell functions from a master script (each function is a test).
I specify the tests in an XML file and I want them to run in order.
The functions to call are organized in PowerShell module files (.psm1). The master script calls Import-Module as needed and then calls the function via something like this...
$newResults = & "$runFunction" #ARGS
or this...
$newResults = Invoke-Expression $runFunctionWithArgs
I have gotten both to work just fine and the XML file parsing invokes these commands in the correct order.
Problem: The tests are apparently launched asynchronously so that the first test I launch does not necessarily get invoked and complete before the second test is invoked.
Note, the tests are functions in a PowerShell module and not commands so I do not think that Start-Process will work (but please tell me if you know how to make that work).
More Details:
It would take too much to add all the code, but essentially what each function call does is create a hashtable with one or more "TestResult" objects. "TestResult" has things like Success codes and a TimeStamp. Each test does things that take different amounts of time, but all synchronous. I would expect the timestamps to be the same order that I called each test, especially since the first thing each test does is get the timestamp so it should not depend on what the test does. When I run in the ISE, everything goes in order. When I run in the command window, the timestamps do not match my expected order.
Workaround:
My working theory is still that PowerShell is somehow parallelizing the calls. I can get consistent results by making the invocation of each call dependent on the results of the previous call. It is a dummy check because I know that what I test will always be true, but PowerShell doesn't know that
if ($newResults.Count -ne [Long]::MaxValue) { $newResults = & "$runFunction" #ARGS }
PowerShell thinks that it needs to know if the previous call count is not MaxValue.

Hosting PowerShell: PowerShell vs. Runspace vs. RunspacePool vs. Pipeline

I attempting to add some fairly limited PowerShell support in my application: I want the ability to periodically run a user-defined PowerShell script and show any output and (eventually) be able to handle progress notification and user-prompt requests. I don't need command-line-style interactive support, or (I think) remote access or the ability to run multiple simultaneous scripts, unless the user script does that itself from within the shell I host. I'll eventually want to run the script asynchronously or on a background thread, and probably seed the shell with some initial variables and maybe a cmdlet but that's as "fancy" as this feature is likely to get.
I've been reading the MSDN documentation about writing host application code, but while it happily explains how to create a PowerShell object, or Runspace, or RunspacePool, or Pipeline, there's no indication about why one would choose any of these approaches over another.
I think I'm down to one of these two, but I've like some feedback about which approach is a better one to take:
PowerShell shell = PowerShell.Create();
shell.AddCommand(/* set initial state here? */);
shell.AddStatement();
shell.AddScript(myScript);
shell.Invoke(/* can set host! */);
or:
Runspace runspace = RunspaceFactory.CreateRunspace(/* can set host and initial state! */);
PowerShell shell = PowerShell.Create();
shell.Runspace = runspace;
shell.AddScript(myScript);
shell.Invoke(/* can set host here, too! */);
(One of the required PSHost-derived class methods is EnterNestedPrompt(), and I don't know whether the user-defined script I run could cause that to get called or not. If it can, then I'll be responsible for "starting a new nested input loop" (as per here)... if that impacts which path to take above, that would also be good to know.)
Thanks!
What are they?
Pipeline
A Pipeline is a way to concatenate commands inside a powershell script. Example: You "pipe" the output from Get-ChildeItem to Where-Object with | to filter them:
Get-ChildItem | Where-Object {$_}
PowerShell Object
The PowerShell object referes to a powershell session, like the one you would get when you start powershell.exe.
Runspace
Every powershell session has its own runspace (You'll always get output from Get-Runspace). It defines the state of the powershell session. Hence the InitialSessionState object/property of a runspace. You may decide to create a new powershell session, with its own runspace from within a powershell, to enable a kind of multithreading.
RunspacePool
Last but not least the RunspacePool. Like the name says, it's a pool of runspaces (or powershell sessions) that can be used to process a lot of complecated tasks. As soon as one of the runspaces in the pool has finished its task it may take the next task till everything is done. (100 things to do with 10 runspaces: on avarage they process 10 each but one may process 8 while two others process 11...)
When to use what?
Pipeline
The pipeline is used insed of scripts. It makes it easier to build complex scripts and should be used as often as possible.
PowerShell Object
The powershell object is used when ever you need a new powershell session. You can create one inside of an existing script, be it C# or Powershell. It's usefull for easy multithreading. On its own it will create a default session.
Runspace
If you want to create a non standard session of powershell, you can manipulate the runspace object before you create a powershell session with it. It's usefull when you want to share synchronized variables, functions or classes in the extra runspaces. Slightly more complex multithreading.
RunspacePool
Like mentioned before it's a heavy tool for heavy work. When one execution of a script takes hours and you need to do it very often.E.g. In combination with remoting you could simultanly install something on every node of a big cluster and the like.
You are overthinking it. The code you show in samples is a good start. Now you just need to read the result of Invoke() and check the error and warning streams.
PowerShell host provides some hooks that RunSpace can use to communicate with user, like stream and format outputs, show progress, report errors, etc. For what you want to do you do not need PowerShell Host. You can read results back from script execution using PowerShell class, check for errors, warnings, read output streams and show notification to the user using facilities of your application. This will be much more straightforward and effective than write entire PowerShell host to show a message box if errors detected.
Also, PowerShell object HAS a Runspace when it is created, you do not need to give it one. If you need to retain the runspace to preserve the environment just keep entire PowerShell object and clear Commands and all Streams each time after you call Invoke.
The next question you should ask is how to process result of PowerShell::Invoke() and read PowerShell::Streams.