I can't use get-member to display an object event whereas getevents() method works.
Why ?
PS>([System.Timers.Timer] |gm -MemberType event |measure).count
0
PS>[System.Timers.Timer].getevents().count
2
PS>[System.Timers.Timer].getevents() |select name
Name
----
Elapsed
Disposed
ok I've seen my error
$t=New-Object -TypeName system.timers.timer
PS>$t |gm -MemberType event |select name
Name
----
Disposed
Elapsed
PS>$t.GetType().fullname
System.Timers.Timer
PS>[system.timers.timer].GetType().FullName
System.RuntimeType
Related
I want to implement a function is about monitoring format volume.
Is there any way to monitor the event of volume be formatted by using WMI or without using WMI?
I can use Register-WmiEvent to register a Wmi Event monitor to detect the plug/unplug of device (Below is sample code).
#Query for finding all device arrival events
$query = "SELECT * FROM Win32_VolumeChangeEvent WHERE EventType=2"
#Register an event subscription
Register-WmiEvent -SourceIdentifier "Qsirch-Monitor-Volume-Add" -Query $query -MessageData $yao -Action {
$volumeName = (Get-WMIObject -Class Win32_LogicalDisk -Filter "DeviceID='$($Event.SourceEventArgs.NewEvent.DriveName)'").VolumeName
Write-Host "$($Event.SourceEventArgs.NewEvent.DriveName) ($($volumeName)) was added"
} | Out-Null
When user create a new volume, this will print D: (New Volume) was added.
So can I use Wmi Event to achieve my goal?
Print D: was formatting when volume D was formatting.
I am trying to create a permanent WMI Event subscription to run a script when a USB Drive gets connected.
Here is how I create the Filter, Consumer and the binding:
$computer = "xxx"
$filterNS = "root\cimv2"
$wmiNS = "root\subscription"
$query = "Select * from __InstanceCreationEvent within 5 where targetinstance isa 'win32_logicaldisk'"
$filterName = "TestFilter"
$filterPath = Set-WmiInstance -Class __EventFilter `
-ComputerName $computer -Namespace $wmiNS -Arguments `
#{name=$filterName; EventNameSpace=$filterNS; QueryLanguage="WQL";
Query=$query}
$consumerPath = Set-WmiInstance -Class CommandLineEventConsumer `
-ComputerName $computer -Namespace $wmiNS `
-Arguments #{
name="TestConsumer";
ExecutablePath= "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe";
CommandLineTemplate = "C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe -executionpolicy bypass -file D:\\reassignDriveletter.ps1"
}
Set-WmiInstance -Class __FilterToConsumerBinding -ComputerName $computer `
-Namespace $wmiNS -arguments #{Filter=$filterPath; Consumer=$consumerPath} |
out-null
Everything gets created without errors, i can see the Filter, consumer and binding in WMI event viewer but if i attach a USB drive nothing happens (first line of the script writes a log entry, thats how i know).
For testing purposes i created a normal event subscription like this:
$job = Register-WmiEvent -Query "Select * from __InstanceCreationEvent within 5 where targetinstance isa 'win32_logicaldisk'" -SourceIdentifier usb -Timeout 1000 -Action $scriptblock
which works absolutely fine.
Am I missing something obvious? I´d be grateful for any help.
Regards
Update: Just tested on a non-domain joined Win7 Computer and the same code works fine. (My workstation is Win8.1 domain-joined for the record). I will test on the target system and report back.
OK, after a couple of days of trial and error, I ended up adapting/using a different approach found here.
This will launch a script stored in D:\scripts everytime a USB is connected and it's permanent.
$filter = ([wmiclass]"\\.\root\subscription:__EventFilter").CreateInstance()
$filter.QueryLanguage = "WQL"
$filter.Query = "Select * from __InstanceCreationEvent within 5 where targetinstance isa 'win32_logicaldisk'"
$filter.Name = "USBFilter"
$filter.EventNamespace = 'root\cimv2'
$result = $filter.Put()
$filterPath = $result.Path
$consumer = ([wmiclass]"\\.\root\subscription:CommandLineEventConsumer").CreateInstance()
$consumer.Name = 'USBConsumer'
$consumer.CommandLineTemplate = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe –ExecutionPolicy Bypass -file D:\scripts\reassignDriveletter.ps1"
$consumer.ExecutablePath = "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$consumer.WorkingDirectory = "D:\scripts"
$result = $consumer.Put()
$consumerPath = $result.Path
$bind = ([wmiclass]"\\.\root\subscription:__FilterToConsumerBinding").CreateInstance()
$bind.Filter = $filterPath
$bind.Consumer = $consumerPath
$result = $bind.Put()
$bindPath = $result.Path
To delete these Perma Events, do this:
([wmi]$filterPath).Delete()
([wmi]$consumerPath).Delete()
([wmi]$bindPath).Delete()
My test script created a folder every time a USB drive was plugged in, so I could test it and it worked.
I'm running Windows 8.1 btw.
Here is a PowerShell script to trigger Internet Explorer, open LinkedIn login page and enter some text in the username text field.
$ie = New-Object -Com "InternetExplorer.Application"
$ie.Navigate("www.linkedIn.com")
$ie.Visible = $true
$doc = $ie.document
$usernameElement = $doc.getElementByTagName("input") | Where-Object {$_.id = "session_key-login""}
$usernameElement.Click()
Get-Process iexplore | Foreach-Object {$_.CloseMainWindow()}
Unfortunately, I keep getting the following error:
You cannot call a method on a null-valued expression.
At C:\Users\Pinku\Desktop\Untitled1.ps1:7 char:23
+ $usernameElement.Click <<<< ()
+ CategoryInfo : InvalidOperation: (Click:String) [], RuntimeExcepti
on
+ FullyQualifiedErrorId : InvokeMethodOnNull
I have tried but have not been able to alleviate myself from this issue.Please suggest!
Instead of using $doc.getElementsByTagName("input") and then trying to filter through the results, try retrieving the ID directly using getElementById:
$usernameElement = $doc.getElementById("session_key-login")
$usernameElement.Click()
---Edit---
Response to still getting the null-valued expression after using the above:
The error message is that it can't find any elements called "session_key-login", and so it returns $null, and hence, when you try to invoke the Click() method, it throws the error. Some things to try:
-Check to see if the id exists. Run the following code after creating your $ie object, and see if there is an ID that matches "session_key-login":
$ie = New-Object -Com "InternetExplorer.Application"
$ie.Navigate("www.linkedIn.com")
$ie.Visible = $true
$doc = $ie.document
$doc.getElementsByTagName("Input") | Select Id, Name
-Try running your PowerShell session as Administrator. I know I wasn't able to launch IE properly until I ran PowerShell as Administrator. For ex. even though the iexplore process was created, the physical Internet Explorer window didn't open for me.
I'm trying to understand how .GetNewClosure() works within the context of a script cmdlet in PowerShell 2.
In essence I have a function that returns an object like so:
function Get-AnObject {
param(
[CmdletBinding()]
[Parameter(....)]
[String[]]$Id
..
[ValidateSet('Option1','Option2')]
[String[]]$Options
)
...
$T = New-Object PSCustomObject -Property #{ ..... }
$T | Add-Member -MemberType ScriptProperty -Name ExpensiveScriptProperty -Value {
$this | Get-ExpensiveStuff
}.GetNewClosure()
..
}
Providing I do not have the validate set options the closure appears to work fine. If it is included however the new closure fails with the following error.
Exception calling "GetNewClosure" with "0" argument(s): "Attribute cannot be added because it would cause the variable Options with value to become invalid."
Presumably the closure is trying to capture the context of the call to the Cmdlet. Since the parameter "Options" is not bound at all this is not nicely with the parameter validation.
I imagine it's possible to avoid this by placing validation as code within the body of the Cmdlet instead of making use of the [Validate*()] decorators -- but this seems nasty and quite obscure. Is there a way of fusing these two ideas?
The "Attribute cannot be added" message is (or was) a PowerShell bug, I've submitted it to Microsoft with this bug report. That particular issue seems to have been fixed, (perhaps around V5.1. but anyone interested in Powershell Closures may still find info below interesting.
There is a workaround which works in earlier versions, but first here's a simplified repro case that produces the same error:
function Test-ClosureWithValidation {
[CmdletBinding()]
param(
[Parameter()]
[ValidateSet('Option1','Option2')]
[String[]]$Options
)
[scriptblock] $closure = {"OK"}.GetNewClosure();
$closure.Invoke()
}
Test-ClosureWithValidation -Options Option1
The workaround depends on the fact that GetNewClosure() works by iterating over the local variables in the calling script's context, binding these local variables into the script's context. The bug occurs because its copying the $Options variable including the validation attribute. You can work around the bug by creating a new context with only the local variables you need. In the simple repro above, it is a one-line workaround:
[scriptblock] $closure = &{ {"OK"}.GetNewClosure();}
The line above now creates a scope with no local variables. That may be too simple for your case; If you need some values from the outer scope, you can just copy them into local variables in the new scope, e.g:
[scriptblock] $closure = &{
$options = $options;
{"OK $options"}.GetNewClosure();
}
Note that the second line above creates a new $options variable, assigning it the value of the outer variable, the attributes don't propagate.
Finally, I'm not sure in your example why you need to call GetNewClosure at all. The variable $this isn't a normal local variable, it will be available in your script property whether or not you create a closure. Example:
function Test-ScriptPropertyWithoutClosure {
[CmdletBinding()]
param(
[Parameter()]
[ValidateSet('Option1','Option2')]
[String[]]$Options
)
[pscustomobject]#{ Timestamp= Get-Date} |
Add-Member ScriptProperty ExpensiveScriptProperty {
$this | get-member -MemberType Properties| % Name
} -PassThru
}
Test-ScriptPropertyWithoutClosure -Options Option1 | fl
I believe this might work:
function Get-AnObject {
param(
[CmdletBinding()]
[Parameter(....)]
[String[]]$Id
..
[ValidateSet('Option1','Option2')]
[String[]]$Options
)
...
$sb = [scriptblock]::create('$this | Get-ExpensiveStuff')
$T = New-Object PSCustomObject -Property #{ ..... }
$T | Add-Member -MemberType ScriptProperty -Name ExpensiveScriptProperty -Value $sb
.. }
That delays creation of the script block until run time.
Regarding service's recovery tab properties that can be seen here:
Is there an API to get the following property values:
First failure for example value: "Take no action"
Second failure
Subsequent failures
Reset fail count
I prefer a way to do so in PowerShell but would like to know about other options as well.
I am not familiar with PowerShell, but there is a Win32 API available: QueryServiceConfig2(). Set the dwInfoLevel parameter to SERVICE_CONFIG_FAILURE_ACTIONS, and pass a pointer to a buffer in the lpBuffer parameter that is large enough to receive a SERVICE_FAILURE_ACTIONS struct.
One needs to modify the services reg key, under
HKLM\System\CurrentControlSet\services\<service name>\
Adding a value of type binary with the name FailureActions. I don't know how it's structured you'd have to play around with that, but as it relates to powershell it would simply be grabbing to real name of the service (maybe using get-service if all you have is the display name), and navigating to that regkey and creating a new value, for example:
PS C:\Users\*\Desktop> $ByteArray = 0,0,0,144,10,23,253,33
PS C:\Users\*\Desktop> Set-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\services\AdobeARMservice -Name FailureActions -Type Binary -Value $ByteArray -Force
PS C:\Users\*\Desktop> Get-ItemProperty -Path HKLM:\SYSTEM\CurrentControlSet\services\AdobeARMservice -Name FailureActions
FailureActions : {0, 0, 0, 144...}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\AdobeARMservice
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services
PSChildName : AdobeARMservice
PSDrive : HKLM
PSProvider : Microsoft.PowerShell.Core\Registry
Adding a byte[ ], but like I mentioned you'd have to either reverse engineer the meaning of the array, or just copy an existing one or something similar.
You can control it for instance with cs.exe
Get-Service -DisplayName YourService | % { sc.exe failure $_.Name actions= /0 reset= 0 }