PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Wednesday, October 26, 2022

[FIXED] How to implement event handling in PowerShell with classes

 October 26, 2022     event-handling, oop, powershell     No comments   

Issue

I want to be able to create a customized event that will trigger to functions or objects in general that are subscribed to that an event. I have looked at some functions such as: New-Event and Register-ObjectEvent, but I am not entirely sure how to put them together efficiently.

The idea of what I want is for a function to automatically execute and perform an operation automatically when a certain event is raised. For example, I have attached a small program that will - by idea - raise an event (TestEvent::SendMessage()) to subscribers (TestSubscribeEvent::DisplayMessage()). This is only by idea, but I am really unsure as to how I can properly implement this functionality within PowerShell.

I am currently using PowerShell 6.2. If any other information is necessary, please do let me know.

class TestSubscribeEvent
{
    # This function would be subscribed to SendMessage from the class TestEvent.
    [void] DisplayMessage([string] $msg)
    {
        Write-Host "EVENT REPLIED WITH THIS MESSAGE:`r`n$($msg)";
    } # DisplayMessage()
} # CLASS :: TestSubscribeEvent



class TestEvent
{
    [void] SendMessage([string] $msg)
    {
        # SEND THIS MESSAGE VIA EVENTS
        #  Allow all subscribers to see the message
        # Message can be any literal string, such as "Hello".
    } # SendMessage()
} # CLASS :: TestEvent



function main()
{
    # Create a new instance of these objects
    [TestEvent] $testEvent = [TestEvent]::new();
    [TestSubscribeEvent] $testSub = [TestSubscribeEvent]::new();

    # Send the message; TestSubscribeEvent should already be subscribed to TestEvent.
    $testEvent.SendMessage("All these squares make a circle.");
} # main()



# Jump to main, start the program.
main;

In this code, though only by idea, $testSub.DisplayMessage() should automatically execute once $testEvent.SendMessage() raises a new event. The output from $testSub.DisplayMessage() will depend on what message is provided from the $testEvent.SendMessage() via Event Message.

Hopefully this makes sense; if needed - I can elaborate a bit further if requested.

Thank you for your time,


Solution

First my apologies for the wall of text. You should look into the New-Event, Register-EngineEvent, Unregister-Event, Get-Event, Get-EventSubscriber, and Remove-Event cmdlets. Here is a quick sample on working with these events to fire off and handle your own events.

First we will setup 2 small functions. This writes some automatic variables to the console:

function Write-EventToConsole ($evt)
{
    Write-Host "Write info to the console from a function"
    Write-Host $Evt.MessageData
    Write-Host $Evt.Sender
    Write-Host $Evt.TimeGenerated      
    Write-Host $Evt.SourceArgs
}

This one just appends them to a file:

function Save-EventToFile ($evt, $filepath)
{
    "Writing to file"                            | Out-File $filepath -Append
    "Message Data   : {0}" -f $Evt.MessageData   | Out-File $filepath -Append
    "Sender         : {0}" -f $Evt.Sender        | Out-File $filepath -Append
    "Time Generated : {0}" -f $Evt.TimeGenerated | Out-File $filepath -Append     
    "Source Args    : {0}" -f $Evt.SourceArgs    | Out-File $filepath -Append
}

Next we will setup 3 event handlers. We will do these 1 at a time, check the subscribers, and fire an event after each one.

Register-EngineEvent -SourceIdentifier "MyEventIdentifier" -Action {
    Write-EventToConsole $Event    
} -SupportEvent

Next check to see if our event subscriber is there.

Get-EventSubscriber -Force

SubscriptionId   : 1
SourceObject     : 
EventName        : 
SourceIdentifier : MyEventIdentifier
Action           : System.Management.Automation.PSEventJob
HandlerDelegate  : 
SupportEvent     : True
ForwardEvent     : False

Then we fire the event

$null = New-Event -SourceIdentifier "MyEventIdentifier" `
                  -Sender "MyObject" `
                  -EventArguments "Some arguments that I want to pass" `
                  -MessageData "This is some message data I want to pass"

And see the data written to console.

just write to console
This is some message data I want to pass
MyObject
MyObject
4/5/2019 1:54:27 PM
Some arguments that I want to pass
Some arguments that I want to pass

Adding the additional events:

Register-EngineEvent -SourceIdentifier "MyEventIdentifier" -Action {
    Write-EventToConsole $Event    
} -SupportEvent

Register-EngineEvent -SourceIdentifier "MyEventIdentifier" -Action {
    Save-EventToFile $Event C:\temp\event.txt
} -SupportEvent

Check the events subscribers:

Get-EventSubscriber -Force

SubscriptionId   : 1
SourceObject     : 
EventName        : 
SourceIdentifier : MyEventIdentifier
Action           : System.Management.Automation.PSEventJob
HandlerDelegate  : 
SupportEvent     : True
ForwardEvent     : False

SubscriptionId   : 2
SourceObject     : 
EventName        : 
SourceIdentifier : MyEventIdentifier
Action           : System.Management.Automation.PSEventJob
HandlerDelegate  : 
SupportEvent     : True
ForwardEvent     : False

SubscriptionId   : 3
SourceObject     : 
EventName        : 
SourceIdentifier : MyEventIdentifier
Action           : System.Management.Automation.PSEventJob
HandlerDelegate  : 
SupportEvent     : True
ForwardEvent     : False

Now if we fire an event:

$null = New-Event -SourceIdentifier "MyEventIdentifier" `
                  -Sender "MyObject" `
                  -EventArguments "Some arguments that I want to pass" `
                  -MessageData "This is some message data I want to pass"

We can see that the 2 event subscribers that write to console fired:

just write to console
This is some message data I want to pass
MyObject
MyObject
4/5/2019 1:54:27 PM
Some arguments that I want to pass
Some arguments that I want to pass

Write info to the console from a function
This is some message data I want to pass
MyObject
4/5/2019 1:54:27 PM
Some arguments that I want to pass

And the event subscriber that wrote to file fired:

Get-Content C:\temp\event.txt

Writing to file
Message Data   : This is some message data I want to pass
Sender         : MyObject
Time Generated : 4/5/2019 1:54:27 PM
Source Args    : Some arguments that I want to pass

Finally to remove the event subscribers you can use:

Get-EventSubscriber -Force | `
    Where-Object { $_.SourceIdentifier -eq 'MyEventIdentifier' } | `
    Unregister-Event -Force

Hopefully this helps explain this a bit. If not, let me know and I will update the answer to address any questions.

Edit: This also works with classes. Here is a quick sample. Note that if you return a value it may not be usable if you use -SupportEvent. Removing SupportEvent will run the action as a PSEventJob. This will let you use Get-Job and Receive-Job to retrieve the value.

# Define a class
class TestSubscribeEvent
{
   # Static method
   static [void] DisplayMessage($evt)
   {
        Write-Host "Written from static handler"
        Write-Host $Evt.MessageData
        Write-Host $Evt.Sender
        Write-Host $Evt.TimeGenerated      
        Write-Host $Evt.SourceArgs
        Write-Host '----------------------------'
   }

   [string] DisplayMessage2($evt)
   {
        return "You wont see this if you use supportevent"
   }

   [void] SetSomeProperty($evt)
   {
        $this.SomeProperty ="Set from internal instance`n`t{0}`n`t{1}`n`t{2}`n`t{3}" -f $Evt.MessageData, $Evt.Sender, $Evt.TimeGenerated, $Evt.SourceArgs
   }

   [string] $SomeProperty
}

# Define a class
class TestEvent
{
   # Static method
   static [void] SendMessage($msg)
   {
        $null = New-Event -SourceIdentifier "MyEventIdentifier" `
                  -Sender "MyObject" `
                  -EventArguments "Sent from static" `
                  -MessageData $msg
   }

   [void] SendMessage2($msg)
   {
        $null = New-Event -SourceIdentifier "MyEventIdentifier" `
                  -Sender "MyObject" `
                  -EventArguments "Sent from instance" `
                  -MessageData $msg
   }
}

Make some objects:

$subObj = New-Object TestSubscribeEvent
$testEvent = New-Object TestEvent

Register some event handlers

#register static handler
Register-EngineEvent -SourceIdentifier "MyEventIdentifier" -Action {
    [TestSubscribeEvent]::DisplayMessage($event)   
} -SupportEvent

#register instance handler
Register-EngineEvent -SourceIdentifier "MyEventIdentifier" -Action {
    $subObj.DisplayMessage2($event) 
} -SupportEvent

#register instance handler without Support Event
#this creates a PSEventJob which we swallow with $null, we check it later
$null = Register-EngineEvent -SourceIdentifier "MyEventIdentifier" -Action {
    $subObj.DisplayMessage2($event) 
}

#register instance handler that updates a property
Register-EngineEvent -SourceIdentifier "MyEventIdentifier" -Action {
    $subObj.SetSomeProperty($event)
} -SupportEvent

Now we will call the event generators:

#call static generator
[TestEvent]::SendMessage("Static called message")

This generates the following to the console:

Written from static handler
Static called message
MyObject
4/9/2019 8:51:20 AM
Sent from static
----------------------------

We can see that it was a static method that was called, that the new event was created in the static method, and that the static handler got it.

We can also use get-job to see the PSEventJob running.

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command                  
--     ----            -------------   -----         -----------     --------             -------                  
47     MyEventIdent...                 Running       True                            ...                      

You can then use Get-Job 47 | Receive-Job -keep to get the output

You wont see this if you use supportevent

Note that the other handler that called the same method that returned a value and did use SupportEvent basically lost all output. No PSEventJob was created so the output had nowhere to go.

We can also see the property was set on the instance object.

$subObj.SomeProperty

Set from internal instance
    Static called message
    MyObject
    4/9/2019 9:05:26 AM
    System.Object[]

The instance version does essentially the same thing all over just passing the different values.

#call instance generator
$testEvent.SendMessage2("Instance called message")

This give the following:

Written from static handler
Instance called message
MyObject
4/9/2019 9:02:02 AM
Sent from instance
----------------------------


Answered By - StephenP
Answer Checked By - Mildred Charles (PHPFixing Admin)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing