PowerShell CloseHandle on EventWaitHandle - powershell

I have two PowerShell scripts. One of them has to wait at the other at one point. Here are the relevant parts:
WaitingScript.ps1:
$StopEventName = 'MyEvent'
function Wait-StopEvent {
$EventResetModeManualReset = 1
$StopEventObject = New-Object -TypeName System.Threading.EventWaitHandle -ArgumentList $false, $EventResetModeManualReset, $StopEventName
$StopEventObject.WaitOne()
}
SignallingScript.ps1:
$StopEventName = 'MyEvent'
function Signal-StopEvent {
$StopEventObject = [System.Threading.EventWaitHandle]::OpenExisting( $StopEventName )
$StopEventObject.Set()
}
It works well, I'm just not sure if I should call something like CloseHandle, or Close on $StopEventObject in either script.

Yes - at least I don't see a reason why you should not close the handle - otherwise the resources used by the handle would not be released. See WaitHandle.Close at Microsoft

Related

Variable being passed in as null - Powershell

So I have an issue that has been bugging me for a few hours now.
I have two functions, Write-Log, and LogProfileRemoval. In Write-Log, I pass in the two arguments as shown here.
LogProfileRemoval('$LogEventDetail', 100000)
But when I check the variables of LogProfileRemoval they are shown like this
$LogEventDetail = '$LogEventDetail' 100000
$LogMethod = $null
I am aware that I have quotes around the variable $LogEventDetail, that was part of my testing to figure this out. Really that variable could be anything and it still concats those two variables into one and leaves the 2nd parameter as a null value.
What am I doing wrong here.
Thanks
function LogProfileRemoval($LogEventDetail, $LogMethod)
{
Switch ($LogMethod)
{
'EventLog' {LogToEventLog($LogEvent)}
}
}
function Write-Log($logDetail, $logEvent=2)
{
$LogEventDetail = New-Object EventLog -Property #{EventTimeStamp=(Get-Date);EventType=$logEvent;EventDetail=$logDetail}
$LogMethod = 1
LogProfileRemoval('$LogEventDetail', 100000)
}
So by not following best practices, was the issue. Weird becuase I have always wrote my powershell scripts like this. Always been a not fan of the Param way of doing it. I changed it to best practice way (sorta) and it worked great.
I would like to know why it didn't work though but overall It just goes to show me to quick being lazy and do it the right way.
Code working shown below
function LogProfileRemoval
{
param(
$LogEventDetail,
$LogMethod
)
Switch ($LogMethod)
{
'EventLog' {LogToEventLog($LogEvent)}
}
}
function Write-Log($logDetail,$logEvent=2)
{
$LogEventDetail = New-Object EventLog -Property #{EventTimeStamp=(Get-Date);EventType=$logEvent;EventDetail=$logDetail}
$LogMethod = 1
LogProfileRemoval -LogEventDetail $LogEventDetail -LogMethod 'EventLog'
}

Using SendAndReceive() with ImapX

I am trying to use the ImapX DLL effectively. I have got $client.Folders.Inbox.Search() and $client.Folders.Inbox.Messages.Download() to work for downloading messages but they're very slow. I was looking into using $client.SendAndReceive() instead. I thought it would work like Outlook's send and receive but it wants a lot of parameters I don't understand.
This is the method definition:
bool SendAndReceive(string command, [ref] System.Collections.Generic.IList[string] data, ImapX.Parsing.CommandProcessor processor, System.Text.Encoding encoding, bool pushResultToDatadespiteProcessor)
I don't know what any of the parameters are for. I searched online for documentation for ImapX, but I couldn't find anything helpful.
Is SendAndReceive() a good method for downloading messages? If so, how do I use it?
This is my PowerShell code so far:
$DLL = [Reflection.Assembly]::LoadFile(“$PSScriptRoot\DLL\ImapX.dll”)
$client = New-Object ImapX.ImapClient
$client.IsDebug = $true
$client.ValidateServerCertificate = $true
$client.ThrowConnectExceptions = $true
$client.UseSsl = $false
$client.Behavior.MessageFetchMode = "Full"
$client.Host = $mailHost
$client.Port = $Port
$client.Connect()
$client.Login($user,$password)
# Downloads all messages but very slow
$client.Folders.Inbox.Search("ALL", $client.Behavior.MessageFetchMode)
$client.Folders.Inbox.Messages.Download()

Why weird assignment from variable inside Powershell switch statement?

I'm a beginner at Powershell and am struggling to understand some syntax from some code I found on Github. I've read the docs on Powershell assignment, and on switch statements, and can't understand what is going on with the = $Yes and = $No in this code snippet:
Switch ($Prompt3) {
Yes {
Stop-EdgePDF
Write-Output "Edge will no longer take over as the default PDF viewer."; = $Yes
}
No {
= $No
}
}
I haven't been able to find any references to this kind of syntax, and it doesn't seem to do anything in the script. So why is it there?
UPDATE: This issue has been resolved.
Looks to me like the variable name that was getting the assignment was deleted in a change back in August.
$PublishSettings = $Yes
Was changed to:
= $Yes
And:
$PublishSettings = $No
Was changed to:
= $No
Looks like poor search and replace.
I've created an issue for the problem at GitHub.
There are many characters that are valid in a function (or variable) name; this includes the = symbol. What you're observing is a function or alias.
Examples:
# standard function
function =
{
return $args
}
# accessing the function: drive
${Function:=} = {
return $args
}
# defining a new alias
New-Alias -Name = -Value Get-Variable
# using the Alias attribute
function Test-Thing
{
[Alias('=')]
param()
return $args
}

How to use a powershell function to return the expected value?

As we know, PowerShell has wacky return semantics.
Function return value in PowerShell shows there are two main ideas to wrap my head around:
All output is captured, and returned
The return keyword just indicates a logical exit point
Even things like reserving variables in outer scopes cause output, like [boolean]$isEnabled. Another good one is $someCollection.Add("toto") which spits the new collection count. Even Append() function causes output.
For example :
Function MyFunc {
$res1 = new-object System.Text.StringBuilder
$res1.Append("titi");
$res2 = "toto"
return $res2
}
$s = MyFunc
Write-Host $s
The output is : titi toto.
The expected output should be toto.
How to use a powershell function to return the expected value? (at least when viewed from a more traditional programming perspective)
Change
$res1.Append("titi");
to
$res1.Append("titi") | Out-Null
because the function returns every output which otherwise would be visible in the console.
if by using 'toto' you are trying to understand if your function succeeded, you could do
Function MyFunc {
$res1 = new-object System.Text.StringBuilder
$res1.Append("titi") | Out-Null
return $?
}
"$?" returns a boolean if the previous command succeeded (true) or failed (false). so externally it would look like
$s = MyFunc
if ($s) {
Write-Host "successful" -Foregroundcolor Green
}
else {
Write-Error "unsuccessful"
}
When PowerShell was being developed, the team wanted to make it simple to use. But, it was confusing to people who know return from other languages. The implementation in classes is an attempt to rectify that mistake.
The return keyword works very differently in methods in PowerShell classes. It works like the return statements in other languages.
In a class method, the return keyword:
Exits the current scope.
Returns the associated object (return ).
Returns only the associated object.
The object that Return returns must match the return type of the method.
It is consistent with the return keyword and analogous keywords in other languages.
class ClassMyFunc
{
[string] MyFunc
{
$res1 = new-object System.Text.StringBuilder
$res1.Append("titi")
$res2 = "toto"
return $res2
}
}
$cmf = New-Object -TypeName ClassMyFunc
$cmf.MyFunc()
The output is : toto, as expected.
Using classes solved my problem, without having to search all functions returning a value in the console and piping it to Out-Null (as suggested by #TobyU).

Removing the Add_Click from a WinForm Button in Powershell (remove_Click)

I have got an understanding problem regarding to Powershell Code in order to remove a Click Event from a WinForm button. After several hours... several days of trying, trying to understand and despairing I thought I give it a break and probably you guys can help me. I really have read several Posts regarding this theme. But that did not help me finally. So please let me ask that question again.
I have seen that there is a possibility to use Eventhandlers and this method seems to work quite fine. As my code seems to be correct, because Powershell do not throw out an error, I would like to know why the code line seems not to be affective. I really do not understand why. Because I have found several codes with remove_Click examples, but in my case it seems not to do what I expect. As I really do not understand why I would like you to help me. Please be so kind and try to explain to me why line 30 of my script has no effect or not the desired effect.
Short: What do I want to do? I just want to remove a Click Event from a button. I could add the Event to the button using Add_Click. So I thought Remove_Click would remove the "Click Code" from this Special button. But it does not seem to work. I just want to remove the Click Property from the button if the savefiledialog is closed by using the cancel button.
This is the code:
Add-Type -AssemblyName System.Windows.Forms
function form_status(){
$form_status = New-Object System.Windows.Forms.Form
$form_status.Size = New-Object System.Drawing.Size(800,530)
$form_status.StartPosition = 'CenterScreen'
$form_status.FormBorderStyle = 'FixedToolWindow'
$form_status_button_csv_logfile = New-Object System.Windows.Forms.Button
$form_status_button_csv_logfile.Location = New-Object System.Drawing.Point(1,1)
$form_status_button_csv_logfile.Size = New-Object System.Drawing.Size(50,50)
$form_status.Controls.Add($form_status_button_csv_logfile)
$form_status_button_csv_logfile.Add_Click({Choose-Folder-For-Checksumlog})
$form_status_button_csv_logfile.add_MouseHover({button_mousehover})
$form_status_button_csv_logfile.add_MouseLeave({button_mouseleave})
[System.Windows.Forms.Application]::EnableVisualStyles();
$form_status_result = $form_status.ShowDialog()
}
Function Choose-Folder-For-Checksumlog(){
$SaveChooser = New-Object -Typename System.Windows.Forms.SaveFileDialog
$SaveChooser.InitialDirectory = [Environment]::GetFolderPath("Desktop")
$SaveChooser.Filter = "CSV Logfile (*.csv)|*.csv"
$savechooser.FileName = "testfile.csv"
if($SaveChooser.ShowDialog() -eq [System.Windows.Forms.DialogResult]::CANCEL){
$savechooser.FileName = ""
$form_status_button_csv_logfile.Remove_Click({Choose-Folder-For-Checksumlog})
}
$checksumlog_folder = $SaveChooser.FileName
}
function button_mouseleave(){
$form_status.Cursor=[System.Windows.Forms.Cursors]::Default
}
function button_mousehover(){
$form_status.Cursor=[System.Windows.Forms.Cursors]::Hand
}
form_status
I appreciate any help from you guys. Please be so kind and explain to me what I do wrong. Probably my expectaions are wrong... But I do not understand it at the moment.
With kindest Regards
FernandeZ
$clickexample = {Choose-Folder-For-Checksumlog};
$form_status_button_csv_logfile.add_Click($clickexample);
$form_status_button_csv_logfile.remove_Click($clickexample);
I know that I'm a bit late to the party, but recently I came into the situtation where it was required to remove multiple event handlers from multiple elements (several different-purpose Click handlers on each of six buttons in my case).
While suggestion from #D'Artagnan works, it is still not helpful in case if your scriptblock handler is not stored in variable (or variable is unknown).
For example, this code removes handler from the button:
$ScriptBlock = {Write-Host 'Clicked'}
$MyButtonObject.Add_Click($ScriptBlock)
$MyButtonObject.Remove_Click($ScriptBlock)
But this one does not (I believe, it is because scriptblock is not referenced by variable, therefore it is a different object, which is not registered in event handler, and, as result, cannot be removed):
$MyButtonObject.Add_Click({Write-Host 'Clicked'})
$MyButtonObject.Remove_Click({Write-Host 'Clicked'})
In addition, I wasn't able to find something like .Remove_AllHandlers(), unfortunately.
Thankfully, I've discovered this question and was able to adapt the code given by #Douglas to PowerShell, so with credits to the original author, I'm happy to share the solution with anyone who wonders:
Function Remove-RoutedEventHandlers {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $True)]
[ValidateNotNullorEmpty()]
[System.Windows.UIElement]$Element,
[Parameter(Mandatory = $True)]
[ValidateNotNullorEmpty()]
[System.Windows.RoutedEvent]$RoutedEvent
)
$eventHandlersStoreProperty = $Element.GetType().GetProperty("EventHandlersStore", [System.Reflection.BindingFlags]'Instance, NonPublic')
$eventHandlersStore = $eventHandlersStoreProperty.GetValue($Element, $Null)
If ($eventHandlersStore) {
$getRoutedEventHandlers = $eventHandlersStore.GetType().GetMethod("GetRoutedEventHandlers", [System.Reflection.BindingFlags]'Instance, Public, NonPublic')
$RoutedEventHandlers = [System.Windows.RoutedEventHandlerInfo[]]$getRoutedEventHandlers.Invoke($eventHandlersStore, $RoutedEvent)
ForEach ($RoutedEventHandler in $RoutedEventHandlers) {
$Element.RemoveHandler($RoutedEvent, $RoutedEventHandler.Handler)
}
}
}
To call the function, you must provide the control and required kind of events to be cleared. For example:
Remove-RoutedEventHandlers -Element $MyButtonObject -RoutedEvent $([System.Windows.Controls.Button]::ClickEvent)
Please note that you have to put event in $() to avoid it from being treated as string. Alternatively, you could alter the function to accept string (for example, with value "Click") and re-construct such event on the provided control inside the function like this $RoutedEvent = $Element.GetType()::"${RoutedEvent}Event"
This function enumerates all handlers of the specified type on provided object and removes them.
If I'm not mistaken, if you call $Element.Remove_SomeEvent($ScriptBlockToRemove) in PowerShell, $Element.RemoveHandler($Element.GetType()::SomeEvent, $ScriptBlockToRemove) is being executed under the hood, so in given example this function is equivalent of enumerating all of the scriptblocks (referenced in Handler property of RoutedEventHandlerInfo object) and calling $MyButtonObject.Remove_Click($ScriptBlockToRemove) on each of them