first of all, I am really new to PowerShell and C# and currently trying out stuff to learn it. I was creating form with the System.Windows.Forms Class and made a small window including two buttonds and a check box. One of the buttons will stay hidden while the other is visible to kinda swap actions as I did not figure another way to do it.
The first Button activates, if the check box is $true, then it triggers the shutdown.exe /s process and changes a variable for the second button. I got it to make the first button work properly, but the second does not seem to do anything at all. Would you mind going over my code to tell me where I made a mistake?
PowerShell.exe -windowstyle hidden {
# Start of code
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
# Declaration of the form
$folderForm = New-Object System.Windows.Forms.Form
$folderForm.StartPosition = 'Manual'
$folderForm.Location = '2267,1107'
# Declaration of the Check Box
$shutdownCheck = New-Object System.Windows.Forms.CheckBox
$shutdownCheck.Location = New-Object System.Drawing.Size(23,23)
$shutdownCheck.Size = '27,27'
$folderForm.Controls.Add($shutdownCheck)
# Declaration of the Shutdown-Button
$shutdownButton = New-Object System.Windows.Forms.Button
$shutdownButton.Text = 'Feierabendmodus aktivieren'
$shutdownButton.Location = '120,23'
$shutdownButton.Size = '160,23'
$folderForm.Controls.Add($shutdownButton)
# Declaration of the Stop-Button
$stopButton = New-Object System.Windows.Forms.Button
$stopButton.Text = 'Sequenz abbrechen!'
$stopButton.Location = '120,23'
$stopButton.Size = '160,23'
$stopButton.Visible = $false
$folderForm.Controls.Add($stopButton)
function TestClick
{
$varX = $true
#Start
if ($shutdownCheck.Checked -eq $true -and $varX -eq $true)
{
# BalloonTip
[reflection.assembly]::loadwithpartialname('System.Windows.Forms')
[reflection.assembly]::loadwithpartialname('System.Drawing')
$notify = new-object system.windows.forms.notifyicon
$notify.Icon = [System.Drawing.SystemIcons]::Information
$notify.visible = $true
$notify.showballoontip(20,'Filler','More Filler',
[system.windows.forms.tooltipicon]::None)
# Shutdown-Prozess
Start-Process shutdown.exe /s
#Stop-Process powershell_ise -force
$shutdownButton.Visible = $false
$stopButton.Visible = $true
$shutdownCheck.Checked = $false
$varX = $false
}
elseif ($shutdownCheck.Checked -eq $false -and $varX -eq $false)
{
# Stop
Start-Process shutdown.exe /a
$shutdownButton.Text = 'Filler'
$stopButton.Visible = $false
$shutdownButton.Visible = $true
$varX = $true
}
}
$shutdownButton.Add_Click({TestClick})
$stopButton.Add_Click({TestClick})
$folderForm.ShowDialog()
# End of code
}
The code below creates a button whose text changes after it's clicked - it's your code with some modifications. Try the following to see if it meets your needs:
PowerShell.exe -windowstyle normal {
# Start of code
[System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms") | Out-Null
# Declaration of the form
$folderForm = New-Object System.Windows.Forms.Form
$folderForm.StartPosition = 'Manual'
$folderForm.Location = '267,107'
$folderForm.Size = '315, 200'
$folderForm.Text = 'Shutdown'
# Declaration of the Shutdown-Button
$shutdownButton = New-Object System.Windows.Forms.Button
$shutdownButton.Text = 'Shutdown'
$shutdownButton.Location = '75,23'
$shutdownButton.Size = '150,23'
$folderForm.Controls.Add($shutdownButton)
function TestClick
{
#Start
if ($shutdownButton.Text -eq 'Shutdown')
{
# BalloonTip
[reflection.assembly]::loadwithpartialname('System.Windows.Forms')
[reflection.assembly]::loadwithpartialname('System.Drawing')
$notify = new-object system.windows.forms.notifyicon
$notify.Icon = [System.Drawing.SystemIcons]::Information
$notify.visible = $true
$notify.showballoontip(20,'Shutdown','Shutting down the computer.',
[system.windows.forms.tooltipicon]::None)
#ToDo: replace the code below with desired code
# Shutdown-Prozess
$shutdownButton.Text = 'Abort Shutdown'
#Start-Process shutdown.exe /t 60
Start-Process shutdown.exe /?
}
else
{
# BalloonTip
[reflection.assembly]::loadwithpartialname('System.Windows.Forms')
[reflection.assembly]::loadwithpartialname('System.Drawing')
$notify = new-object system.windows.forms.notifyicon
$notify.Icon = [System.Drawing.SystemIcons]::Information
$notify.visible = $true
$notify.showballoontip(20,'Shutdown Aborted','Shutdown has been aborted.',
[system.windows.forms.tooltipicon]::None)
# Stop
$shutdownButton.Text = 'Shutdown'
Start-Process shutdown.exe /a
}
}
$shutdownButton.Add_Click({TestClick})
$folderForm.ShowDialog()
# End of code
}
Two issues:
$varX is getting set to true every time you click
The TestClick function is only updating a local-scope $varX (only within that function)
# your current code (shortened)
function TestClick
{
$varX = $true ## getting set every button click (in local scope)
if ($shutdownCheck.Checked -eq $true -and $varX -eq $true)
{
$varX = $false ## local scope
}
elseif ($shutdownCheck.Checked -eq $false -and $varX -eq $false)
{
$varX = $true ## local scope
}
}
To fix, try doing something like this instead:
function TestClick
{
if ($shutdownCheck.Checked -eq $true -and $varX -eq $true)
{
$Script:varX = $false ## Scope the variable so the function sets it properly
}
elseif ($shutdownCheck.Checked -eq $false -and $varX -eq $false)
{
$Script:varX = $true ## here too
}
}
$varX = $true ## Move the initial setting outside the function
I suggest skipping the function and global status all together, and simply use the different buttons you created:
# shutdown button
$shutdownButton.Add_Click({
if ($shutdownCheck.Checked) {
Start-Process shutdown.exe /s
$shutdownButton.Visible = $false
$shutdownCheck.Checked = $false
$stopButton.Visible = $true
}
})
# stop button (hidden until shutdown clicked)
$stopButton.Add_Click({
Start-Process shutdown.exe /a
$shutdownButton.Visible = $true
$shutdownButton.Text = 'Stopped'
$stopButton.Visible = $false
})
$folderForm.ShowDialog()
# End of code
edit: Running a function in a script creates a child scope that inherits elements like variables from the script level. When you set a variable inside a function (without setting a scope), then that variable only exists within that function's scope while it's executing, and is not kept. I think it's easier to see in practice:
$A = 'Hello'
$B = 'World'
Function Set-Goodbye {
$B = 'Goodbye'
# The default scope for variables set inside a script
Write-Output "function - local: $A $B"
# Manually check the higher script-level scope
Write-Output "function - script: $A $Script:B"
}
# print output
Set-Goodbye
"script - local: $A $B"
function - local: Hello Goodbye
function - script: Hello World
script - local: Hello World
For a better explanation, check out the about_Scopes page
Related
Have another head scratcher here, simply put my balloon notification does not work when fired from inside of a function that is inside of a job, the icon shows up for a fraction of a second and then disappears, tried several compinations of -wait -sleep -sleep-start to see if i could delay its "completion" since i feel the function is not running it's full course before ending, it works outside of the job/function, but inside it does not, here is the snippet I am calling
function MakeToolTip
{
[CmdletBinding(SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory=$true)]
$Text,
[Parameter(Mandatory=$true)]
$Title,
[ValidateSet(‘None’, ‘Info’, ‘Warning’, ‘Error’)]
$Icon = ‘Info’, $Timeout = 10000
)
Add-Type -AssemblyName System.Windows.Forms
if ($script:balloon -eq $null)
{
$script:balloon = New-Object System.Windows.Forms.NotifyIcon
}
$path = Get-Process -id $pid | Select-Object -ExpandProperty Path
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
$balloon.BalloonTipIcon = $Icon
$balloon.BalloonTipText = $Text
$balloon.BalloonTipTitle = $Title
$balloon.Visible = $true
$balloon.ShowBalloonTip($Timeout)
}
This is called with: MakeToolTip "Testing" "VNC Connections" Info
The full code of the script is here, lines 239-269: https://pastebin.com/7Q4gwLJe
I'm considering running the balloon in a separate job? Maybe the pipeline is completing it too quickly because there too much other stuff going on?
EDIT
Finding that when wrapped in a job a balloon notification does not work, very strange behavior
$MakeToolTip =
{
function Makeit {
[CmdletBinding(SupportsShouldProcess = $true)]
param
(
[Parameter(Mandatory=$true)]
$Text,
[Parameter(Mandatory=$true)]
$Title,
[ValidateSet(‘None’, ‘Info’, ‘Warning’, ‘Error’)]
$Icon = ‘Info’, $Timeout = 10000
)
Add-Type -AssemblyName System.Windows.Forms
if ($balloon -eq $null)
{
$balloon = New-Object System.Windows.Forms.NotifyIcon
}
$path = Get-Process -id $pid | Select-Object -ExpandProperty Path
$balloon.Icon = [System.Drawing.Icon]::ExtractAssociatedIcon($path)
$balloon.BalloonTipIcon = $Icon
$balloon.BalloonTipText = $Text
$balloon.BalloonTipTitle = $Title
$balloon.Visible = $true
$balloon.ShowBalloonTip($Timeout)
write-host $Text, $Title
}
}
$balloonjob = Start-Job -ScriptBlock {MakeIt 'Hi' 'There'} -InitializationScript $MakeToolTip -Name "BalloonJob"
Register-ObjectEvent $balloonjob -EventName StateChanged -Action {
if ($eventArgs.JobStateInfo.State -eq [System.Management.Automation.JobState]::Failed)
{
Write-Host "Closing the Balloon"
# This command removes the original job
$sender | Remove-Job -Force
# These commands remove the event registration
$eventSubscriber | Unregister-Event -Force
$eventSubscriber.Action | Remove-Job -Force
}
}
$archive = ("*www*","*Backup*","*inetpub*","*wwwroot*","*archive*","*Archive*","*ARCHIVE*","*WINDOWS*","*Program Files*","*JioMediaShare*","*thumbnails*");
function Log-Message {
[CmdletBinding()]
Param (
[Parameter(Mandatory=$true, Position=0)]
[string]$LogMessage
)
Write-Output ("{0} - {1}" -f (Get-Date), $LogMessage)
}
function Exclude-Directories {
Process {
$allowThrough = $true
foreach ($directoryToExclude in $archive) {
$directoryText = "*\" + $directoryToExclude
$childText = "*\" + $directoryToExclude + "\*"
if (($_.FullName -like $directoryText -and $_.PsIsContainer) -or $_.FullName -like $childText) {
$allowThrough = $false
break
}
}
if ($allowThrough) {
return $_
}
}
}
Log-Message "Starting Search"
Get-WMIObject Win32_LogicalDisk | ForEach-Object {
Log-Message $_.DeviceID
Get-ChildItem -Path $_.DeviceID -Include *.jpg, *.png, *.bmp, *.jpeg, *.gif, *.webp -Recurse |
Exclude-Directories |
where {!$_.PSIsContainer} |
Select-Object FullName, Name, BaseName, CreationTime, LastWriteTime, Length |
Export-Csv -NoTypeInformation -Path Images.csv -Encoding UTF8 -Append |
% {$_.Replace('"','')}
}
Log-Message "Search completed."
If I run the above script independently, that script is running without errors and search Images in C:, : and E: drive.
string text = System.IO.File.ReadAllText(#"New-Item.ps1");
using (PowerShell PowerShellInstance = PowerShell.Create()) {
// use "AddScript" to add the contents of a script file to the end of the execution pipeline.
// use "AddCommand" to add individual commands/cmdlets to the end of the execution pipeline.
PowerShellInstance.AddScript(text);
Collection<PSObject> PSOutput = PowerShellInstance.Invoke();
foreach (PSObject outputItem in PSOutput) {
// if null object was dumped to the pipeline during the script then a null
if (outputItem != null) {
Console.WriteLine(outputItem.BaseObject.ToString() + "\n");
}
}
if (PowerShellInstance.Streams.Error.Count > 0) {
Console.Write("Error");
}
Console.ReadKey();
}
When running the PowerShell script in a C# program, it is skipping search in the C: drive.
I think the issue is with -Path $_.DeviceID. As $_.DeviceID is not a full path, but just something like 'C:', PowerShell is trying to interpret what you mean and I think it is defaulting to the working directory for that drive (e.g. the value of $pwd).
One option to get around this is to run Set-Location "$($_.DeviceID)\" before the enumeration and drop the -Path parameter from the call to Get-ChildItem. That is:
Set-Location "$($_.DeviceID)\"
Get-ChildItem -Include *.jpg, *.png, *.bmp, *.jpeg, *.gif, *.webp -Recurse ...
I need to get the Current Windows Keyboard Layout for my WPF application to map each key correctly and handle AZERTY as well as QWERTY and QWERTZ (and so on...)
I noticed a problem since I am working with a French layout (azerty) but my windows is displayed in English.
I tried various methods to get the layout correctly but without results :
var test1 = InputLanguageManager.Current.CurrentInputLanguage;
and
var test2 = CultureInfo.CurrentCulture;
I tried by having ENG language with AZERTY layout, ENG language with QWERTY layout and FRA language with AZERTY layout but the output from my tests were always different. I could get the language displayed correctly (en-GB) but not the layout.
The following PowerShell1 script declares Get-KeyboardLayoutForPid function which reliably gets the Current Windows Keyboard Layout for any process2.
if ( $null -eq ('Win32Functions.KeyboardLayout' -as [type]) ) {
Add-Type -MemberDefinition #'
[DllImport("user32.dll")]
public static extern IntPtr GetKeyboardLayout(uint thread);
'# -Name KeyboardLayout -Namespace Win32Functions
}
Function Get-KeyboardLayoutForPid {
[cmdletbinding()]
Param (
[parameter(Mandatory=$False, ValueFromPipeline=$False)]
[int]$Id = $PID,
# used formerly for debugging
[parameter(Mandatory=$False, DontShow=$True)]
[switch]$Raw
)
$InstalledInputLanguages = [System.Windows.Forms.InputLanguage]::InstalledInputLanguages
$CurrentInputLanguage = [System.Windows.Forms.InputLanguage]::DefaultInputLanguage # CurrentInputLanguage
$CurrentInputLanguageHKL = $CurrentInputLanguage.Handle # just an assumption
### Write-Verbose ('CurrentInputLanguage: {0}, 0x{1:X8} ({2}), {3}' -f $CurrentInputLanguage.Culture, ($CurrentInputLanguageHKL -band 0xffffffff), $CurrentInputLanguageHKL, $CurrentInputLanguage.LayoutName)
Function GetRealLayoutName ( [IntPtr]$HKL ) {
$regBase = 'Registry::' +
'HKLM\SYSTEM\CurrentControlSet\Control\Keyboard Layouts'
$LayoutHex = '{0:x8}' -f ($hkl -band 0xFFFFFFFF)
if ( ($hkl -band 0xFFFFFFFF) -lt 0 ) {
$auxKeyb = Get-ChildItem -Path $regBase |
Where-Object {
$_.Property -contains 'Layout Id' -and
(Get-ItemPropertyValue -Path "Registry::$($_.Name)" `
-Name 'Layout Id' `
-ErrorAction SilentlyContinue
) -eq $LayoutHex.Substring(2,2).PadLeft(4,'0')
} | Select-Object -ExpandProperty PSChildName
} else {
$auxKeyb = $LayoutHex.Substring(0,4).PadLeft(8,'0')
}
$KbdLayoutName = Get-ItemPropertyValue -Path (
Join-Path -Path $regBase -ChildPath $auxKeyb
) -ErrorAction SilentlyContinue -Name 'Layout Text'
$KbdLayoutName
# Another option: grab localized string from 'Layout Display Name'
} # Function GetRealLayoutName
Function GetKbdLayoutForPid {
Param (
[parameter(Mandatory=$True, ValueFromPipeline=$False)]
[int]$Id,
[parameter(Mandatory=$False, DontShow=$True)]
[string]$Parent = ''
)
$Processes = Get-Process -Id $Id
$Weirds = #('powershell_ise','explorer') # not implemented yet
$allLayouts = foreach ( $Proces in $Processes ) {
$LayoutsExtra = [ordered]#{}
$auxKLIDs = #( for ( $i=0; $i -lt $Proces.Threads.Count; $i++ ) {
$thread = $Proces.Threads[$i]
## The return value is the input locale identifier for the thread:
$LayoutInt = [Win32Functions.KeyboardLayout]::GetKeyboardLayout( $thread.Id )
$LayoutsExtra[$LayoutInt] = $thread.Id
})
Write-Verbose ('{0,6} ({1,6}) {2}: {3}' -f $Proces.Id, $Parent,
$Proces.ProcessName, (($LayoutsExtra.Keys |
Select-Object -Property #{ N='Handl';E={('{0:x8}' -f ($_ -band 0xffffffff))}} |
Select-Object -ExpandProperty Handl) -join ', '))
foreach ( $auxHandle in $LayoutsExtra.Keys ) {
$InstalledInputLanguages | Where-Object { $_.Handle -eq $auxHandle }
}
$ConHost = Get-WmiObject Win32_Process -Filter "Name = 'conhost.exe'"
$isConsoleApp = $ConHost | Where-Object { $_.ParentProcessId -eq $Proces.Id }
if ( $null -ne $isConsoleApp ) {
GetKbdLayoutForPid -Id ($isConsoleApp.ProcessId) -Parent ($Proces.Id -as [string])
}
}
if ( $null -eq $allLayouts ) {
# Write-Verbose ('{0,6} ({1,6}) {2}: {3}' -f $Proces.Id, $Parent, $Proces.ProcessName, '')
} else {
$allLayouts
}
} # GetKbdLayoutForPid
$allLayoutsRaw = GetKbdLayoutForPid -Id $Id
if ( $null -ne $allLayoutsRaw ) {
if ( ([bool]$PSBoundParameters['Raw']) ) {
$allLayoutsRaw
} else {
$retLayouts = $allLayoutsRaw |
Sort-Object -Property Handle -Unique |
Where-Object { $_.Handle -ne $CurrentInputLanguageHKL }
if ( $null -eq $retLayouts ) { $retLayouts = $CurrentInputLanguage }
$RealLayoutName = $retLayouts.Handle |
ForEach-Object { GetRealLayoutName -HKL $_ }
$ProcessAux = Get-Process -Id $Id
$retLayouts | Add-Member -MemberType NoteProperty -Name 'ProcessId' -Value "$Id"
$retLayouts | Add-Member -MemberType NoteProperty -Name 'ProcessName' -Value ($ProcessAux | Select-Object -ExpandProperty ProcessName )
# $retLayouts | Add-Member -MemberType NoteProperty -Name 'WindowTitle' -Value ($ProcessAux | Select-Object -ExpandProperty MainWindowTitle )
$retLayouts | Add-Member -MemberType NoteProperty -Name 'RealLayoutName' -Value ($RealLayoutName -join ';')
$retLayouts
}
}
<#
.Synopsis
Get the current Windows Keyboard Layout for a process.
.Description
Gets the current Windows Keyboard Layout for a process. Identify the process
using -Id parameter.
.Parameter Id
A process Id, e.g.
- Id property of System.Diagnostics.Process instance (Get-Process), or
- ProcessId property (an instance of the Win32_Process WMI class), or
- PID property from "TaskList.exe /FO CSV", …
.Parameter Raw
Parameter -Raw is used merely for debugging.
.Example
Get-KeyboardLayoutForPid
This example shows output for the current process (-Id $PID).
Note that properties RealLayoutName and LayoutName could differ (the latter is wrong; a bug in [System.Windows.Forms.InputLanguage] implementation?)
ProcessId : 2528
ProcessName : powershell
RealLayoutName : United States-International
Culture : cs-CZ
Handle : -268368891
LayoutName : US
.Example
. D:\PShell\tests\Get-KeyboardLayoutForPid.ps1 # activate the function
Get-Process -Name * |
ForEach-Object { Get-KeyboardLayoutForPid -Id $_.Id -Verbose }
This example shows output for each currently running process, unfortunately
even (likely unusable) info about utility/service processes.
The output itself can be empty for most processes, but the verbose stream
shows (hopefully worthwhile) info where current keboard layout is held.
Note different placement of the current keboard layout ID:
- console application (cmd, powershell, ubuntu): conhost
- combined GUI/console app (powershell_ise) : the app itself
- classic GUI apps (notepad, notepad++, …) : the app itself
- advanced GUI apps (iexplore) : Id ≘ tab
- "modern" GUI apps (MicrosoftEdge*) : Id ≟ tab (unclear)
- combined GUI/service app (explorer) : indiscernible
- etc… (this list is incomplete).
For instance, iexplore.exe creates a separate process for each open window
or tab, so their identifying and assigning input languages is an easy task.
On the other side, explorer.exe creates the only process, regardless of
open visible window(s), so they are indistinguishable by techniques used here…
.Example
gps -Name explorer | % { Get-KeyboardLayoutForPid -Id $_.Id } | ft -au
This example shows where the function could fail in a language multifarious environment:
ProcessId ProcessName RealLayoutName Culture Handle LayoutName
--------- ----------- -------------- ------- ------ ----------
5344 explorer Greek (220);US el-GR -266992632 Greek (220)
5344 explorer Greek (220);US cs-CZ 67699717 US
- scenario:
open three different file explorer windows and set their input languages
as follows (their order does not matter):
- 1st window: let default input language (e.g. Czech, in my case),
- 2nd window: set different input language (e.g. US English),
- 3rd window: set different input language (e.g. Greek).
- output:
an array (and note that default input language window isn't listed).
.Inputs
No object can be piped to the function. Use -Id pameter instead,
named or positional.
.Outputs
[System.Windows.Forms.InputLanguage] extended as follows:
note the <…> placeholder
Get-KeyboardLayoutForPid | Get-Member -MemberType Properties
TypeName: System.Windows.Forms.InputLanguage
Name MemberType Definition
---- ---------- ----------
ProcessId NoteProperty string ProcessId=<…>
ProcessName NoteProperty System.String ProcessName=powershell
RealLayoutName NoteProperty string RealLayoutName=<…>
Culture Property cultureinfo Culture {get;}
Handle Property System.IntPtr Handle {get;}
LayoutName Property string LayoutName {get;}
.Notes
To add the `Get-KeyboardLayoutForPid` function to the current scope,
run the script using `.` dot sourcing operator, e.g. as
. D:\PShell\tests\Get-KeyboardLayoutForPid.ps1
Auhor: https://stackoverflow.com/users/3439404/josefz
Created: 2019-11-24
Revisions:
.Link
.Component
P/Invoke
<##>
} # Function Get-KeyboardLayoutForPid
if ( -not ('System.Windows.Forms.InputLanguage' -as [type]) ) {
Add-Type -AssemblyName System.Windows.Forms
}
The Get-KeyboardLayoutForPid function contains a Comment-Based Help placed at the end of its body. I hope that its principle is implementable in C# easy.
The main idea of my approach:
Suppose that the current keyboard layout (CurrentInputLanguage) for a given process is the (user-dependant) default one (DefaultInputLanguage).
Collect keyboard layouts associated with every thread of given process (allLayoutsRaw). Note this trick for a console application: collect keyboard layouts associated with every thread of child conhost process as well.
If there is a keyboard layout different from DefaultInputLanguage in the allLayoutsRaw collection then it's the sought-after one (retLayouts).
1 Does not work in PowerShell Core (pwsh.exe).
2 Might fail for explorer process in a language multifarious environment, see an example of failing scenario in the Comment-Based help.
I am not sure of the ask - whether you want to know the current keyboard layout or you want to set the keyboard layout.
In both cases, InputLanguageManager should help.
You can try setting input language manager to appropriate cultureInfo object.
This should change the keyboard layout for your WPF application
InputLanguageManager.Current.CurrentInputLanguage = new CultureInfo("fr-FR");
I'm hoping someone more experienced than me can post a Powershell script example (using the simplest possible method) that will...
Identify all messages that use "Text Message" OR "Multimedia Message" forms... then, move them to the Inbox/SMS subfolder.
Basically, I'm just trying to reproduce what my Outlook 2010 Rule does, except via command line.
If there's a pre-made cmdlet that does this already; with an example of what Im trying to do, that would be ideal.
You should be able to do that with something like this
$MailboxName = $args[0]
## Load Managed API dll
Add-Type -Path "C:\Program Files\Microsoft\Exchange\Web Services\2.1\Microsoft.Exchange.WebServices.dll"
## Set Exchange Version
$ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2010_SP2
## Create Exchange Service Object
$service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService($ExchangeVersion)
## Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
#Credentials Option 1 using UPN for the windows Account
$psCred = Get-Credential
$creds = New-Object System.Net.NetworkCredential($psCred.UserName.ToString(),$psCred.GetNetworkCredential().password.ToString())
$service.Credentials = $creds
#Credentials Option 2
#service.UseDefaultCredentials = $true
## Choose to ignore any SSL Warning issues caused by Self Signed Certificates
## Code From http://poshcode.org/624
## Create a compilation environment
$Provider=New-Object Microsoft.CSharp.CSharpCodeProvider
$Compiler=$Provider.CreateCompiler()
$Params=New-Object System.CodeDom.Compiler.CompilerParameters
$Params.GenerateExecutable=$False
$Params.GenerateInMemory=$True
$Params.IncludeDebugInformation=$False
$Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
$TASource=#'
namespace Local.ToolkitExtensions.Net.CertificatePolicy{
public class TrustAll : System.Net.ICertificatePolicy {
public TrustAll() {
}
public bool CheckValidationResult(System.Net.ServicePoint sp,
System.Security.Cryptography.X509Certificates.X509Certificate cert,
System.Net.WebRequest req, int problem) {
return true;
}
}
}
'#
$TAResults=$Provider.CompileAssemblyFromSource($Params,$TASource)
$TAAssembly=$TAResults.CompiledAssembly
## We now create an instance of the TrustAll and attach it to the ServicePointManager
$TrustAll=$TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
[System.Net.ServicePointManager]::CertificatePolicy=$TrustAll
## end code from http://poshcode.org/624
## Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
#CAS URL Option 1 Autodiscover
$service.AutodiscoverUrl($MailboxName,{$true})
"Using CAS Server : " + $Service.url
#CAS URL Option 2 Hardcoded
#$uri=[system.URI] "https://casservername/ews/exchange.asmx"
#$service.Url = $uri
## Optional section for Exchange Impersonation
#$service.ImpersonatedUserId = new-object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $MailboxName)
function FolderIdFromPath{
param (
$FolderPath = "$( throw 'Folder Path is a mandatory Parameter' )"
)
process{
## Find and Bind to Folder based on Path
#Define the path to search should be seperated with \
#Bind to the MSGFolder Root
$folderid = new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
$tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
#Split the Search path into an array
$fldArray = $FolderPath.Split("\")
#Loop through the Split Array and do a Search for each level of folder
for ($lint = 1; $lint -lt $fldArray.Length; $lint++) {
#Perform search based on the displayname of each folder level
$fvFolderView = new-object Microsoft.Exchange.WebServices.Data.FolderView(1)
$SfSearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint])
$findFolderResults = $service.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)
if ($findFolderResults.TotalCount -gt 0){
foreach($folder in $findFolderResults.Folders){
$tfTargetFolder = $folder
}
}
else{
"Error Folder Not Found"
$tfTargetFolder = $null
break
}
}
if($tfTargetFolder -ne $null){
return $tfTargetFolder.Id.UniqueId.ToString()
}
}
}
$TextMessageClass = "IPM.Note.Mobile.SMS"
$MMSMessageClass = "IPM.Note.Mobile.MMS"
$sfItemSearchFilter1 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,$TextMessageClass)
$sfItemSearchFilter2 = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass,$MMSMessageClass)
#SearchFilter collection Or
$sfCollection = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+SearchFilterCollection([Microsoft.Exchange.WebServices.Data.LogicalOperator]::Or);
$sfCollection.Add($sfItemSearchFilter1)
$sfCollection.Add($sfItemSearchFilter2)
# Bind to the Inbox Folder
$folderid= new-object Microsoft.Exchange.WebServices.Data.FolderId([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox,$MailboxName)
$Inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderid)
# Get Folder to Move Item to
$fldId = FolderIdFromPath -FolderPath "\Inbox\SMS"
$SubFolderId = new-object Microsoft.Exchange.WebServices.Data.FolderId($fldId)
$SubFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$SubFolderId)
$Itemids = #()
if($SubFolder -ne $null){
#Define ItemView to retrive just 1000 Items
$ivItemView = New-Object Microsoft.Exchange.WebServices.Data.ItemView(1000)
$fiItems = $null
do{
$fiItems = $service.FindItems($Inbox.Id,$sfCollection,$ivItemView)
#[Void]$service.LoadPropertiesForItems($fiItems,$psPropset)
foreach($Item in $fiItems.Items){
Write-Host ("Moving " + $Item.Subject)
$Itemids += $Item
}
$ivItemView.Offset += $fiItems.Items.Count
}while($fiItems.MoreAvailable -eq $true)
#Total Items Processed Varible
$nmbProcessed = 0
if($Itemids.Count -gt 0){
write-host ("Move " + $Itemids.Count + " Items")
#Create Collection for Move Batch
$type = ("System.Collections.Generic.List"+'`'+"1") -as "Type"
$type = $type.MakeGenericType("Microsoft.Exchange.WebServices.Data.ItemId" -as "Type")
$BatchItemids = [Activator]::CreateInstance($type)
#Varible to Track BatchSize
$batchSize = 0
foreach($iiID in $Itemids){
$nmbProcessed++
$BatchItemids.Add($iiID.Id)
if($iiID.Size -ne $null){
$batchSize += $iiID.Size
}
#if BatchCount greator then 50 or larger the 10 MB Move Batch
if($BatchItemids.Count -eq 50 -bor $batchSize -gt (10*1MB)){
$Result = $null
$Result = $service.MoveItems($BatchItemids,$SubFolder.Id)
[INT]$collectionCount = 0
[INT]$Rcount = 0
[INT]$Errcount = 0
if($Result -ne $null){
foreach ($res in $Result){
if ($res.Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success){
$Rcount++
}
else{
$Errcount++
}
$collectionCount++
}
}
else{
Write-Host -foregroundcolor red ("Move Result Null Exception")
}
Write-host ($Rcount.ToString() + " Items Moved Successfully " + "Total Processed " + $nmbProcessed + " Total Folder Items " + $Itemids.Count)
if($Errcount -gt 0){
Write-Host -foregroundcolor red ($Errcount.ToString() + " Error failed Moved")
}
$BatchItemids.Clear()
$batchSize = 0
}
}
if($BatchItemids.Count -gt 0){
$Result = $service.MoveItems($BatchItemids,$SubFolder.Id)
[INT]$Rcount = 0
[INT]$Errcount = 0
foreach ($res in $Result){
if ($res.Result -eq [Microsoft.Exchange.WebServices.Data.ServiceResult]::Success){
$Rcount++
}
else{
$Errcount++
}
}
Write-host ($Rcount.ToString() + " Items Moved Successfully")
if($Errcount -gt 0){
Write-Host -foregroundcolor red ($Errcount.ToString() + " Error failed Moved")
}
}
}
}
Cheers
Glen
Hi I've got a old VBscript that I've got a implemented function inside.
this function contain a object that can execute and stop a program called QTP as well as other more advanced parts up against this program.
as I've not been able to find any way to do this in Powershell I figured why not just reuse that one function from my old VBscript file. well as it turns out this is slightly more complicated than what I thought.
as I can't use MSScriptControl.ScriptControl to access my VBscript I thought well maybe I can find something in .net preferable C#
but the few ways I found execute the entire script and not just that one function. for example:
System.Diagnostics.Process.Start(#"cscript //B //Nologo c:\yourfile.vbs");
so I'm asking here now How can I either execute that one function from my Powershell file or translate it to Powershell here is the functions code:
Public Sub ExecuteTest(ByVal TestPath , ByVal TestName)
Dim qtApp 'As QuickTest.Application ' Declare the Application object variable
Dim qtTest 'As QuickTest.Test ' Declare a Test object variable
Dim qtResultsOpt 'As QuickTest.RunResultsOptions ' Declare a Run Results Options object variable
strLog = "Start " & TestName
WriteLogToFile(strLog)
Set qtApp = CreateObject("QuickTest.Application") ' Create the Application object
qtApp.Launch ' Start QuickTest
qtApp.Visible = False ' Make the QuickTest application invisible
qtApp.Visible = True ' Make the QuickTest application visible
' Set QuickTest run options:
' Always--Captures images for all steps in the run.
' OnError--Captures images only for failed steps.
' OnWarning--Captures images only for steps that return a failed or warning status.
' Never--Never captures images.
qtApp.Options.Run.ImageCaptureForTestResults = "Always"
' qtApp.Options.Run.ImageCaptureForTestResults = "OnError"
' qtApp.Options.Run.ImageCaptureForTestResults = "OnWarning"
' qtApp.Options.Run.ImageCaptureForTestResults = "Never"
qtApp.Options.Run.RunMode = "Fast"
qtApp.Options.Run.ViewResults = False
'This will Open a script
' Wscript.Echo TestPath
qtApp.Open TestPath , False ' Open the test in edit mode
' qtApp.Open TestPath , True ' Open the test in read-only mode
' set run settings for the test
Set qtTest = qtApp.Test
' qtTest.Settings.Run.IterationMode = "rngIterations" ' Run only iterations 2 to 4
' qtTest.Settings.Run.StartIteration = 1
' qtTest.Settings.Run.EndIteration = 1
' qtTest.Settings.Run.OnError = "NextStep" ' Instruct QuickTest to perform next step when error occurs
'For Viewing Results
Set qtResultsOpt = CreateObject("QuickTest.RunResultsOptions") ' Create the Run Results Options object
' WScript.Echo letter & testresult & TestName & "\Result_" & GetDateTimeStr
qtResultsOpt.ResultsLocation = letter & testresult & TestName & "\Result_" & GetDateTimeStr ' Set the results location
qtTest.Run qtResultsOpt ' Run the test
While qtTest.IsRunning
'Wait For Test To Finish
Wend
strLog = "Slut " & TestName & qtTest.LastRunResults.Status
' qtApp.Options.Run.ViewResults = True
WriteLogToFile(strLog)
'Close QTP
qtTest.Close() ' Close the test
'Set the options to nothing
Set qtResultsOpt = Nothing ' Release the Run Results Options object
Set qtTest = Nothing ' Release the Test object
Set qtApp = Nothing ' Release the Application object
End Sub
Thanks for the help I've used what I got from David Martin below here and found the solutions to the problem with the invisible methods and properties. here is the working method:
function ExecuteTest($TestPath,$letter,$testresult,$TestName)
{
#Update-TypeData -Prepend .\ComObject.Types.ps1xml
#param($TestPath=[string],$TestName=[string])
$qtApp = New-Object -comobject QuickTest.Application -strict
$strLog = "Start $TestName"
WriteLogToFile($strLog)
# Start QuickTest
[System.__ComObject].InvokeMember(“Launch”,[System.Reflection.BindingFlags]::InvokeMethod,$null,$qtApp,$null,$null,$null,$null)
Start-Sleep -Seconds 10
$obj = New-Object System.Object
# Make the QuickTest application invisible
$obj = $False
#Make the QuickTest application visible
$obj = $True
$myArray = #($nul)
$myArray[0] =$obj
#Wait for the aplication to be initialized
#Apply the Visibility Variable to the QuickTest application
[System.__ComObject].InvokeMember("Visible",[System.Reflection.BindingFlags]::SetProperty,$null,$qtApp,$myArray)
#Make the QuickTest application visible
#$qtApp.Visible = $True
# Set QuickTest run options:
# Always--Captures images for all steps in the run.
# OnError--Captures images only for failed steps.
# OnWarning--Captures images only for steps that return a failed or warning status.
# Never--Never captures images.
$qtApp.Options.Run.ImageCaptureForTestResults = "Always"
# $qtApp.Options.Run.ImageCaptureForTestResults = "OnError"
# $qtApp.Options.Run.ImageCaptureForTestResults = "OnWarning"
# $qtApp.Options.Run.ImageCaptureForTestResults = "Never"
$obj = "Fast"
$myArray[0] =$obj
[System.__ComObject].InvokeMember("RunMode",[System.Reflection.BindingFlags]::SetProperty,$null,$qtApp.Options.Run,$myArray)
#$qtApp.Options.Run.RunMode = "Fast"
#$qtApp.Options.Run.ViewResults = $False
$obj = $False
$myArray[0] =$obj
[System.__ComObject].InvokeMember("ViewResults",[System.Reflection.BindingFlags]::SetProperty,$null,$qtApp.Options.Run,$myArray)
#This will Open a script
# Wscript.Echo TestPath
$obj = $False
$myArray[0] = $TestPath
$myArray += $obj
#Invoke-Method - inputobject $qtApp -MethodName Open -MethodParameters $myArray -Static
$qtApp.GetType().InvokeMember(“Open”,#Method Name
[System.Reflection.BindingFlags]::InvokeMethod,#BindingFlag
$null,#Binder
$qtApp,#Target
($TestPath),#Args
$null,#Modifiers
$null,#culture
$null) #[string[]]("TestPath"))#namedParameters
# [System.__ComObject].InvokeMember(“Open”,[System.Reflection.BindingFlags]::InvokeMethod,$null,$qtApp,([Object[]]$myArray),$null,$null,$null)
#$qtApp.Open #$TestPath, $False #' Open the test in edit mode
# $qtApp.Open $TestPath , $True #' Open the test in read-only mode
# set run settings for the test
$qtTest = $qtApp.GetType().InvokeMember("Test",[System.Reflection.BindingFlags]::GetProperty,$null,$qtApp,$null)
# $qtTest.Settings.Run.IterationMode = "rngIterations" ' Run only iterations 2 to 4
# $qtTest.Settings.Run.StartIteration = 1
# $qtTest.Settings.Run.EndIteration = 1
# $qtTest.Settings.Run.OnError = "NextStep" ' Instruct QuickTest to perform next step when error occurs
# WScript.Echo letter & testresult & TestName & "\Result_" & GetDateTimeStr
# The following variables have not been defined
#letter
#testresult
# This function doesn't exist
#GetDateTimeStr
$qtResultsOpt = New-Object -comobject QuickTest.RunResultsOptions -strict
#$qtResultsOpt = FileSystemObject QuickTest.RunResultsOptions
$ParentPath = $letter + $testresult + $TestName
$ChildPath = "\Result_" + $("{0:yyyymmddTHHMMss}" -f $(get-date))
$ResultOptArgs = join-path -path $ParentPath -childpath $ChildPath
[System.__ComObject].InvokeMember(“ResultsLocation”,[System.Reflection.BindingFlags]::SetProperty,$null,$qtResultsOpt,$ResultOptArgs)
#$qtResultsOpt.GetType().InvokeMember(“ResultsLocation”,#Method Name
# [System.Reflection.BindingFlags]::InvokeMethod,#BindingFlag
# $null,#Binder
# $qtResultsOpt,#Target
#($ResultOptArgs),#Args
# $null,#Modifiers
#$null,#culture
# $null) #[string[]]("TestPath"))#namedParameters
# Run the test
$qtTest.GetType().InvokeMember(“Run”,[System.Reflection.BindingFlags]::InvokeMethod,$null,$qtTest,($qtResultsOpt),$null,$null,$null)
#$qtTest.Run # $qtResultsOpt
do
{
Start-Sleep -m 100
}
while($qtTest.IsRunning)
$strLog = "Slut $TestName $(qtTest.LastRunResults.Status)"
# qtApp.Options.Run.ViewResults = True
WriteLogToFile($strLog)
#Close QTP
#$qtTest.Close()
[System.__ComObject].InvokeMember(“Close”,[System.Reflection.BindingFlags]::InvokeMethod,$null,$qtTest,$null,$null,$null,$null)
#Set the options to nothing
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($qtResultsOpt)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($qtTest)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($qtApp)
}
I've had a quick stab at translating this for you, I don't have QuickTest so I can't test this for you.
function WriteLogToFile
{
param ($message)
write-host message
}
function ExecuteTest
{
param
(
$TestPath,
$TestName
)
$qtApp = New-Object -comobject QuickTest.Application -strict
$qtTest = New-Object -comobject QuickTest.Test -strict
$qtResultsOpt = New-Object -comobject QuickTest.RunResultsOptions
$strLog = "Start $TestName"
WriteLogToFile($strLog)
# Start QuickTest
$qtApp.Launch
# Make the QuickTest application invisible
$qtApp.Visible = $false
#Make the QuickTest application visible
$qtApp.Visible = True
# Set QuickTest run options:
# Always--Captures images for all steps in the run.
# OnError--Captures images only for failed steps.
# OnWarning--Captures images only for steps that return a failed or warning status.
# Never--Never captures images.
$qtApp.Options.Run.ImageCaptureForTestResults = "Always"
# $qtApp.Options.Run.ImageCaptureForTestResults = "OnError"
# $qtApp.Options.Run.ImageCaptureForTestResults = "OnWarning"
# $qtApp.Options.Run.ImageCaptureForTestResults = "Never"
qtApp.Options.Run.RunMode = "Fast"
qtApp.Options.Run.ViewResults = False
#This will Open a script
write-host "TestPath: [$TestPath]"
$qtApp.Open $TestPath , $false ' Open the test in edit mode
# $qtApp.Open TestPath , True ' Open the test in read-only mode
# set run settings for the test
$qtTest = $qtApp.Test
# $qtTest.Settings.Run.IterationMode = "rngIterations" ' Run only iterations 2 to 4
# $qtTest.Settings.Run.StartIteration = 1
# $qtTest.Settings.Run.EndIteration = 1
# $qtTest.Settings.Run.OnError = "NextStep" ' Instruct QuickTest to perform next step when error occurs
# WScript.Echo letter & testresult & TestName & "\Result_" & GetDateTimeStr
# The following variables have not been defined
#letter
#testresult
# This function doesn't exist
#GetDateTimeStr
$qtResultsOpt.ResultsLocation = join-path -path "c:\results" -childpath $("{0:yyyymmddTHHMMss}" -f $(get-date))
# Run the test
$qtTest.Run qtResultsOpt
do
Start-Sleep -m 100
while($qtTest.IsRunning)
$strLog = "Slut $TestName $(qtTest.LastRunResults.Status)"
# qtApp.Options.Run.ViewResults = True
WriteLogToFile($strLog)
#Close QTP
$qtTest.Close()
#Set the options to nothing
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($qtResultsOpt)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($qtTest)
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($qtApp)
}