SysTrayHost/SysTrayHost.ps1
class SysTrayHost { hidden [System.Drawing.Bitmap] $_systrayBitmap = $null hidden [System.Drawing.Bitmap] $_powershellBitmap = $null hidden [System.Drawing.Bitmap] $_cloudBitmap = $null hidden [System.Drawing.Bitmap] $_hierarchyBitmap = $null hidden [System.Drawing.Bitmap] $_folderBitmap = $null hidden [System.Drawing.Bitmap] $_sharepointBitmap = $null hidden [System.Windows.Forms.NotifyIcon] $_systray = $null hidden [System.Windows.Forms.ContextMenuStrip] $_contextmenu = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemOperations = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemEcoSystem = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemShell = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemShellVerbose = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemScheduler = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemProfile = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemLib = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemCache = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemDocs = $null hidden [System.Windows.Forms.ToolStripMenuItem] $_menuitemExit = $null hidden [System.Collections.ArrayList] $_processes = [System.Collections.ArrayList]::new() hidden [System.Collections.ArrayList] $_hiddenprocesses = [System.Collections.ArrayList]::new() hidden $_procId = $null hidden [Boolean] $_procHide = $false hidden [Boolean] $_procTerminate = $false hidden $_command = $null hidden $_filter = $null hidden [System.Collections.Stack] $_nodesStack = $null hidden $_pipeSysTray = $null hidden $_pipeExplorer = $null hidden $_pipeCustomizer = $null hidden $_explorer = $null hidden $_customizer = $null #-------------------- # Constructor #-------------------- SysTrayHost() { $this._systrayBitmap = [System.Drawing.Bitmap]::FromFile("$PSScriptRoot\Images\icons8-engineering-50.png") $this._powershellBitmap = [System.Drawing.Bitmap]::FromFile("$PSScriptRoot\Images\icons8-powershell-filled-50.png") $this._cloudBitmap = [System.Drawing.Bitmap]::FromFile("$PSScriptRoot\Images\icons8-clouds-30.png") $this._hierarchyBitmap = [System.Drawing.Bitmap]::FromFile("$PSScriptRoot\Images\icons8-hierarchy-32.png") $this._folderBitmap = [System.Drawing.Bitmap]::FromFile("$PSScriptRoot\Images\icons8-folder-48.png") $this._sharepointBitmap = [System.Drawing.Bitmap]::FromFile("$PSScriptRoot\Images\icons8-microsoft-sharepoint-48.png") $this.initializeControls() $this.buildFormLayout() $this.initializePipes() if([boolean]($global:SRxEnv.Program.EnabledExplorer)) { $this.newExplorer() } } hidden [void] initializeControls() { $this._systray = [System.Windows.Forms.NotifyIcon]::new() $this._contextmenu = [System.Windows.Forms.ContextMenuStrip]::new() $this._menuitemShell = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemOperations = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemEcoSystem = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemShell = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemShellVerbose = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemScheduler = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemProfile = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemLib = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemCache = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemDocs = [System.Windows.Forms.ToolStripMenuItem]::new() $this._menuitemExit = [System.Windows.Forms.ToolStripMenuItem]::new() } hidden [void] buildFormLayout() { # # _systray # $this._systray.Icon = [System.Drawing.Icon]::FromHandle($this._systrayBitmap.GetHicon()) $this._systray.Text = $global:SRxEnv.DefaultSettings.EcoSystem.SysTrayUI.ts_Title # $this._systray.Visible = $global:SRxEnv.Program.EnabledSysTray $this._systray.Tag = $this $this._systray.add_MouseDown({ param($source, $e) $_this = $source.Tag $connected = Get-SRxIsConnected $_this._menuitemOperations.Enabled = $connected $_this._menuitemEcoSystem.Enabled = $connected $_this._menuitemShell.Enabled = $connected $_this._menuitemShellVerbose.Enabled = $connected $_this._menuitemScheduler.Enabled = $false $_this._menuitemLib.Enabled = $connected }) $this._systray.add_DoubleClick({ param($source, $e) $_this = $source.Tag $_this.newExplorer() }) $this._systray.add_MouseMove({ param($source, $e) $_this = $source.Tag if( Get-SRxIsConnected ) { $_this._systray.Text = "$($global:SRxEnv.DefaultSettings.EcoSystem.SysTrayUI.ts_Title) - $($global:SRxEnv.Tenancy.TermGroupName)" } else { $_this._systray.Text = "$($global:SRxEnv.DefaultSettings.EcoSystem.SysTrayUI.ts_Title) - offline" } }) $this._contextmenu.Items.Add($this._menuitemOperations) $this._contextmenu.Items.Add($this._menuitemEcoSystem) $this._contextmenu.Items.Add("-") $this._contextmenu.Items.Add($this._menuitemShell) $this._contextmenu.Items.Add($this._menuitemShellVerbose) $this._contextmenu.Items.Add("-") $this._contextmenu.Items.Add($this._menuitemScheduler) $this._contextmenu.Items.Add("-") $this._contextmenu.Items.Add($this._menuitemProfile) $this._contextmenu.Items.Add($this._menuitemLib) $this._contextmenu.Items.Add($this._menuitemCache) $this._contextmenu.Items.Add("-") $this._contextmenu.Items.Add($this._menuitemDocs) $this._contextmenu.Items.Add("-") $this._contextmenu.Items.Add($this._menuitemExit) $this._systray.ContextMenuStrip = $this._contextmenu # # _menuitemOperations # $this._menuitemOperations.Text = "Operations" #$this._menuitemOperations.DefaultItem = $true $this._menuitemOperations.Image = $this._hierarchyBitmap $this._menuitemOperations.Tag = $this $this._menuitemOperations.add_Click({ param($source, $e) $_this = $source.Tag $_this.newExplorer() }) # # _menuitemEcoSystem # $this._menuitemEcoSystem.Text = "EcoSystem" $this._menuitemEcoSystem.Tag = $this $this._menuitemEcoSystem.Image = $this._sharepointBitmap $this._menuitemEcoSystem.add_Click({ param($source, $e) #$_this = $source.Tag #try { $global:_EcoSystem.Invoke( $_this ) } catch { Write-Host ($_.Exception.Message) } if( Get-SRxIsConnected ) { $str = $global:SRxEnv.DefaultSettings.EcoSystem.ts_URL $str = $str.replace('{tenancy}', $global:SRxEnv.Tenancy.Name) $ecosystemURL = $str.replace('{ecosystem}', $global:SRxEnv.Tenancy.EcoSystem) Start-Process $ecosystemURL | Out-Null } }) # # _menuitemShell # $this._menuitemShell.Text = "Shell" $this._menuitemShell.Image = $this._powershellBitmap $this._menuitemShell.Tag = $this $this._menuitemShell.add_Click({ param($source, $e) $_this = $source.Tag try { $global:_Shell.Invoke( $_this ) } catch { Write-Host ($_.Exception.Message) } }) # # _menuitemShellVerbose # $this._menuitemShellVerbose.Text = "Shell Verbose" $this._menuitemShellVerbose.Tag = $this $this._menuitemShellVerbose.Image = $this._powershellBitmap $this._menuitemShellVerbose.add_Click({ param($source, $e) $_this = $source.Tag try { $global:_Shell_Verbose.Invoke( $_this ) } catch { Write-Host ($_.Exception.Message) } }) # # _menuitemScheduler # $this._menuitemScheduler.Text = "Scheduler" $this._menuitemScheduler.Tag = $this $this._menuitemScheduler.add_Click({ _Scheduler }) # # _menuitemProfile # $this._menuitemProfile.Text = "O365 connection" $this._menuitemProfile.Image = $this._cloudBitmap $this._menuitemProfile.Tag = $this $this._menuitemProfile.add_Click({ param($source, $e) $_this = $source.Tag try { $global:_Profile.Invoke( $_this ) } catch { Write-Host ($_.Exception.Message) } }) # # _menuitemLib # $this._menuitemLib.Text = "PowerShell Library" $this._menuitemLib.Image = $this._folderBitmap $this._menuitemLib.Tag = $this $this._menuitemLib.add_Click({ Start-Process explorer $(Join-Path $global:SRxEnv.paths.SRxRoot "Lib") }) # # _menuitemCache # $this._menuitemCache.Text = "Local cache" $this._menuitemCache.Image = $this._folderBitmap $this._menuitemCache.Tag = $this $this._menuitemCache.add_Click({ param($source, $e) $_this = $source.Tag try { $global:_Cache.Invoke( $_this ) } catch { Write-Host ($_.Exception.Message) } }) # # _menuitemDocs # $this._menuitemDocs.Text = "Help" $this._menuitemDocs.Tag = $this $this._menuitemDocs.add_Click({ param($source, $e) $_this = $source.Tag try { $global:_Docs.Invoke( $_this ) } catch { Write-Host ($_.Exception.Message) } }) # # _menuitemExit # $this._menuitemExit.Text = "Quit" $this._menuitemExit.Tag = $this $this._menuitemExit.add_Click({ param($source, $e) $_this = $source.Tag $_this.Close() }) } hidden [void] initializePipes() { $this._pipeSysTray = New-SRxPipeServer -PipeName "pipe-Shell" -DoEvents $true $this._pipeSysTray.Caller = $this $this._pipeSysTray.OnMessage($this._onSysTrayMessage) $this._pipeSysTray.OnClose({ $_this = $($args[0]).Caller if($_this._procTerminate) { $_this.terminateProcesses( $false) $_this._procTerminate = $false } }) $this._pipeExplorer = New-SRxPipeServer -PipeName "pipe-Explorer" -DoEvents $false $this._pipeExplorer.Caller = $this $this._pipeExplorer.OnMessage($this._onExplorerMessage) $this._pipeExplorer.OnClose({ $_this = $($args[0]).Caller #Powershell window was closed interactively by user if( $_this._explorer ) { $_this._explorer.Complete() } }) $this._pipeCustomizer = New-SRxPipeServer -PipeName "pipe-Customizer" -DoEvents $false $this._pipeCustomizer.Caller = $this $this._pipeCustomizer.OnMessage($this._onCustomizerMessage) $this._pipeCustomizer.OnClose({ $_this = $($args[0]).Caller #Powershell window was closed interactively by user if( $_this._customizer ) { $_this._customizer.Complete() } }) } hidden [void] startPowerShell($arguments) { if( $this._command.ts_HideWindow ) { [System.Diagnostics.Process]$proc = Start-Process PowerShell -WindowStyle Minimized -ArgumentList $arguments -PassThru $this._hiddenprocesses.Add($proc) $this._procId = $proc.Id $this._procHide = $true } else { [System.Diagnostics.Process]$proc = Start-Process PowerShell -WindowStyle Normal -ArgumentList $arguments -PassThru $this._processes.Add($proc) $this._procId = $proc.Id $this._procHide = $false } } hidden [void] newExplorer() { try { if( $global:SRxEnv.Tenancy.TermGroupID -ne "") { if( -not $this._explorer) { Write-Host "new schema explorer" $sgroupName = Remove-SRxStringSpecialCharacter -String $global:SRxEnv.Tenancy.TermGroupName $schema = $global:SRxEnv.CachePath + "\Profiles\" + $sgroupName + "\termSet.xml" Connect-SRxProvisioningDatabase_JSON $this._explorer = Explorer -schema $schema -source $($global:SRxEnv.Paths.SRxRoot) $this._explorer.Caller = $this $this._explorer.OnCommand({ $_this = $($args[0]).Caller $_this._command = $($args[1]) $_this._procId = $null $_this.startPowerShell($($args[2])) }) $this._explorer.OnClose({ $_this = $($args[0]).Caller $_this.terminateProcesses( $true) $_this._explorer = $null if( -not [boolean]($global:SRxEnv.Program.EnabledSysTray) ) { $_this.Close() } }) $this._explorer.OnCancel({ $_this = $($args[0]).Caller $_this.terminateProcesses( $false) $_this._pipeExplorer.Close() }) $this._explorer.OnHideProcess({ $_this = $($args[0]).Caller $_this._procHide = $($args[1]) #https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-showwindow if($_this._procHide) { $null = [Win32Functions.Win32Windows]::ShowWindowAsync((Get-Process -PID $($_this._procId)).MainWindowHandle, 2) } else { $null = [Win32Functions.Win32Windows]::ShowWindowAsync((Get-Process -PID $($_this._procId)).MainWindowHandle, 10) } }) $this._explorer.OnTemplateCommand({ $_this = $($args[0]).Caller $_this._customizer = $_this._explorer #$_this._command = $($args[1]) #$_this._nodesStack = $_this._command.nodesStack }) $this._explorer.OnTemplateClose({ $_this = $($args[0]).Caller $_this._customizer = $null }) #Write-Host "----- _explorer.OnEnableSysTray -----" $this._explorer.OnEnableSysTray({ $_this = $($args[0]).Caller $enabled = [boolean]$($args[1]) $_this._systray.Visible = $enabled $global:SRxEnv.Program.EnabledSysTray = $enabled $global:SRxEnv.PersistCustomProperty("Program", $global:SRxEnv.Program) }) $this._explorer.Run() } else { Write-Host "activate schema explorer" $this._explorer.Activate() } } } catch { Write-Host($_.Exception.Message) Write-Host($_.Exception) } } hidden [void] terminateProcesses([boolean]$all) { function terminateProcs ($procs) { if($procs.Count -gt 0) { for( [int]$i = $($procs.Count - 1); $i -ge 0; $i--) { $proc = $procs[$i] Write-Host "Stop-Process $($proc.Id)" Stop-Process $proc [void]$procs.RemoveAt($i) } } } if( $all) { terminateProcs($this._hiddenprocesses) terminateProcs($this._processes) } else { # $procs = $this._hiddenprocesses if($procs.Count -gt 0) { for( [int]$i = $($procs.Count - 1); $i -ge 0; $i--) { $proc = $procs[$i] [boolean]$isMinimized = [Win32Functions.Win32Windows]::IsIconic((Get-Process -PID $($proc.Id)).MainWindowHandle) if( -not $isMinimized) { Write-Host "Don't terminate Process $($proc.Id)" $this._processes.Add($proc) [void]$procs.RemoveAt($i) } } } terminateProcs($procs) } } hidden [System.Management.Automation.ScriptBlock] $_onSysTrayMessage = { try { $message = $($args[1]) $pipe = $($args[0]) $_this = $pipe.Caller #Write-Host "[this] $ths" Write-Host "[server] $($pipe.GetPipeName()) received ($($message.GetType().Name)) from client: $message" if( $($message.GetType().Name) -eq "String") { #Write-Host "switch [string]" #string #Write-Host "[server] $($pipe.GetPipeName()) received (text) from client: $message" switch($message) { "Terminate" { $_this.Close() } "Receive" { $pipe.Write("Hello client !") } "getObject" { $obj = [PSCustomObject]@{ ts_Environment = "Production" ts_Title = "Title" } $pipe.Write($obj) } "Get-Command" { if($_this._command) { $pipe.Write($_this._command) $_this._command = $null } else { $pipe.Write([pscustomobject]$false) } } "Get-NodeStack" { <#if($_this._customizer) { if($_this._nodesStack) { $pipe.Write($_this._nodesStack) $_this._nodesStack = $null } else { $pipe.Write([pscustomobject]$false) } } #> if($_this._customizer ) { #$_this._customizer === $_this._explorer Write-Host "_this._procId = $($_this._procId)" $ns = $_this._customizer.NodesPack( $_this._procId) if($ns) { Write-Host ($ns | FT | Out-String) $pipe.Write($ns) } else { Write-Host ("no nodestack available") $pipe.Write([pscustomobject]$false) } #explor will pause process $_this._procId } else { $exit ="invalid" Write-Host ("invalid") $pipe.Write([pscustomobject]$exit) } } "Complete" { if( $_this._explorer ) { $pipe.Write([pscustomobject]$true) $_this._explorer.Complete() #terminateChildProcesses -all $false #$pipe.Restart() } else { $exit ="invalid" $pipe.Write([pscustomobject]$exit) } } default { $pipe.Write([pscustomobject]$false) } } } else { #Write-Host "[server] $($pipe.GetPipeName()) received (pscustomobject) from client:" Write-Host ( $message | Format-Table | Out-String ) Write-Host "$($message.ts_ControlFile)" if( $message.ts_ControlFile ) { switch($message.ts_ControlFile) { "Open-Explorer" { $_this.newExplorer() #-command $command $pipe.Write([pscustomobject]$true) } "Open-Customizer" { $_this._explorer.ShowCustomizer($true) $_this._customizer = $_this._explorer $pipe.Write([pscustomobject]$true) } "Update-Customizer" { if($_this._customizer) { $_this._customizer.SetFilter2($message.filter) $_this._customizer.StatusBar("Ready") $pipe.Write([pscustomobject]$true) } else { #$pipe.Write([pscustomobject]$false) $exit ="invalid" $pipe.Write([pscustomobject]$exit) } } "Complete-Explorer" { if( $_this._explorer ) { Connect-SRxProvisioningDatabase_JSON $_this._explorer.Complete() [boolean]$isMinimized = [Win32Functions.Win32Windows]::IsIconic((Get-Process -PID $($_this._procId)).MainWindowHandle) Write-Host ("Complete pid = $($_this._procId) Window Minimized = $isMinimized") #Write-Host ("Complete - startup window hidden = $($_this._procHide)") if($_this._procHide -and $isMinimized) { $_this._procTerminate = $true } $_this._procHide = $false $_this._procId = $null $pipe.Write([pscustomobject]$true) } else { $exit ="invalid" $pipe.Write([pscustomobject]$exit) } } "Status-Explorer" { if( $_this._explorer ) { $_this._explorer.StatusBar($message.ts_Title) $pipe.Write([pscustomobject]$true) } else { $pipe.Write([pscustomobject]$false) } } "Status-Customizer" { if( $_this._customizer ) { $_this._customizer.StatusBar($message.ts_Title) $pipe.Write([pscustomobject]$true) } else { $pipe.Write([pscustomobject]$false) } } default { $exit ="invalid" $pipe.Write([pscustomobject]$exit) } } } else { Write-Host ( $message | Format-List | Out-String ) $pipe.Write([pscustomobject]$true) } } } catch { Write-Host($_.Exception.Message) Write-Host($_.Exception) } } hidden [System.Management.Automation.ScriptBlock] $_onExplorerMessage = { $message = $($args[1]) $pipe = $($args[0]) $_this = $pipe.Caller Write-Host "[server] $($pipe.GetPipeName()) received ($($message.GetType().Name)) from client: $message" if( $_this._explorer ) { $_this._explorer.StatusBar($message) } $pipe.Write([pscustomobject]$true) } hidden [System.Management.Automation.ScriptBlock] $_onCustomizerMessage = { $message = $($args[1]) $pipe = $($args[0]) $_this = $pipe.Caller Write-Host "[server] $($pipe.GetPipeName()) received ($($message.GetType().Name)) from client: $message" if( $_this._customizer ) { $_this._customizer.StatusBar($message) } $pipe.Write([pscustomobject]$true) } [void] Run2() { # Create an application context for it to all run within. # This helps with responsiveness, especially when clicking Exit. $appContext = New-Object System.Windows.Forms.ApplicationContext [void][System.Windows.Forms.Application]::Run($appContext) } [void] Close() { if($this._pipeSysTray) { $this._pipeSysTray.Dispose() } if($this._pipeExplorer) { $this._pipeExplorer.Dispose() } if($this._pipeCustomizer) { $this._pipeCustomizer.Dispose() } $this._systray.Visible = $false [System.Windows.Forms.Application]::Exit() #close any opened forms $this._systray.dispose() #$window.Close() #[System.Diagnostics.Process]::CloseMainWindow() [int]$pid = [System.Diagnostics.Process]::GetCurrentProcess().Id Stop-Process $pid } } |