こんにちは.........
お目汚し申し訳ありません 零壱(ゼロイチ)テクトです。

今回のコードは、単純に自分への戒めであり、苦渋であり
己の技術力不足に打ちひしがれたので、完全に自分宛の投稿です。
事の始まりは、タスクバーに隠れている通知領域について
「^」のボタンを押すと表示されますが
その隠れていた通知領域の特定アプリを自動クリックしたい
という業務がありまして
この1ヶ月、PowerShellでできないか100時間近く試行錯誤した結果
できない、という結論に至ったので、供養のためここに残します。
「C#+UI Automation」 という限られた方法や
「隠れた通知領域をクリックするフリーウェア」 を
間接的にPowerShellで操作するという方法では
望んでいた形で操作する事はなんとか実現できました。
しかし、それを頼りに 「PowerShell+UI Automation」 のみでできそうと思って
試行錯誤したのが運の尽きです。
「^」というシェンブロンをクリックして、
隠し通知領域を表示させるまではできましたが
表示後の全要素を取得しても
「オーバーフロー通知領域」 は表示されても
その配下にも、全要素一覧を表示しても
特定アプリの名前もボタンもアイコンも見つかりません。
PowerShellやVBSでも試行錯誤したんですが
そもそも、UI Automationを操作しないと
タスクバーの要素すら簡単に取得できなくて
どなかた知っている方がいればお教え願いいたいです。
1.概要/仕様
・隠し通知領域(実名は、オーバーフロー通知領域)の特定アプリをクリック
・PowerShellとWindowsのデフォルトライブラリのみ
・外部ツールや外部ファイルは使わない
・PowerShell 5.1
2.コード
# SendInput 定義(クリック用)
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class MouseClicker {
[DllImport("user32.dll")]
public static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll", CharSet=CharSet.Auto, CallingConvention=CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, UIntPtr dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
public static void Click(int x, int y) {
SetCursorPos(x, y);
mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, (uint)x, (uint)y, 0, UIntPtr.Zero);
}
}
"@
Start-Sleep -Milliseconds 500
Add-Type -AssemblyName UIAutomationClient, UIAutomationTypes, UIAutomationProvider
# ルート要素取得
$rootElement = [System.Windows.Automation.AutomationElement]::RootElement
# 「Shell_TrayWnd」の要素取得
$condTaskbar = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ClassNameProperty, "Shell_TrayWnd")
$taskbarElement = $rootElement.FindFirst([System.Windows.Automation.TreeScope]::Children, $condTaskbar)
$taskbarChildren = $taskbarElement.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition)
# 「TrayNotifyWnd」の要素取得
$condTrayNotify = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ClassNameProperty, "TrayNotifyWnd")
$trayNotifyElement = $taskbarElement.FindFirst([System.Windows.Automation.TreeScope]::Children, $condTrayNotify)
$trayNotifyChildren = $trayNotifyElement.FindAll([System.Windows.Automation.TreeScope]::Children,[System.Windows.Automation.Condition]::TrueCondition)
# 「SysPager」の要素取得
$condSysPager = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ClassNameProperty, "SysPager")
$sysPagerElement = $trayNotifyElement.FindFirst([System.Windows.Automation.TreeScope]::Children, $condSysPager)
$sysPagerChildren = $sysPagerElement.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition)
# 「ToolbarWindow32」の要素取得
$condToolbarWindow32 = New-Object System.Windows.Automation.PropertyCondition([System.Windows.Automation.AutomationElement]::ClassNameProperty, "ToolbarWindow32")
$toolbarWindow32Element = $sysPagerElement.FindFirst([System.Windows.Automation.TreeScope]::Children, $condToolbarWindow32)
$toolbarIcons = $toolbarWindow32Element.FindAll([System.Windows.Automation.TreeScope]::Children, [System.Windows.Automation.Condition]::TrueCondition)
# 「Shell_TrayWnd」の配下要素を一覧表示
if ($taskbarElement) {
Write-Host "`n---[ Shell_TrayWnd ]配下一覧 ---" -ForegroundColor Cyan
$taskbarChildren | ForEach-Object {
$name = $_.Current.Name
$className = $_.Current.ClassName
$controlType = $_.Current.ControlType.ProgrammaticName
$bounds = $_.Current.BoundingRectangle
Write-Host " - Name: '$name' (Class: '$className', Type: '$controlType' , xy: '$bounds')" -ForegroundColor White
}
} else {
Write-Host "[ Shell_TrayWnd ]配下が見つかりません。" -ForegroundColor Yellow
}
# 「TrayNotifyWnd」の配下要素を一覧表示
if ($trayNotifyElement) {
Write-Host "`n---[ TrayNotifyWnd ]配下一覧 ---" -ForegroundColor Cyan
$trayNotifyChildren | ForEach-Object {
$name = $_.Current.Name
$className = $_.Current.ClassName
$controlType = $_.Current.ControlType.ProgrammaticName
$bounds = $_.Current.BoundingRectangle
Write-Host " - Name: '$name' (Class: '$className', Type: '$controlType' , xy: '$bounds')" -ForegroundColor White
if($className -eq 'Button'){
if ($bounds.Width -gt 0 -and $bounds.Height -gt 0) {
$x = [int]($bounds.Left + $bounds.Width / 2)
$y = [int]($bounds.Top + $bounds.Height / 2)
[MouseClicker]::Click($x, $y)
}
}
}
} else {
Write-Host "[ TrayNotifyWnd ]配下が見つかりません。" -ForegroundColor Yellow
}
Write-Host "---------------------------------------------------------" -ForegroundColor Cyan
# 「Syspager」の配下要素を一覧表示
if ($sysPagerElement) {
Write-Host "`n---[ Syspager ]配下一覧 ---" -ForegroundColor Cyan
$sysPagerChildren | ForEach-Object {
$name = $_.Current.Name
$className = $_.Current.ClassName
$controlType = $_.Current.ControlType.ProgrammaticName
$bounds = $_.Current.BoundingRectangle
Write-Host " - Name: '$name' (Class: '$className', Type: '$controlType' , xy: '$bounds')" -ForegroundColor White
}
} else {
Write-Host "[ Syspager ]配下が見つかりません。" -ForegroundColor Yellow
}
Write-Host "---------------------------------------------------------" -ForegroundColor Cyan
# 「ToolbarWindow32」の配下要素を一覧表示
if ($toolbarWindow32Element) {
Write-Host "`n---[ ToolbarWindow32 ]配下一覧 ---" -ForegroundColor Cyan
$toolbarIcons | ForEach-Object {
$name = $_.Current.Name
$className = $_.Current.ClassName
$controlType = $_.Current.ControlType.ProgrammaticName
$bounds = $_.Current.BoundingRectangle
Write-Host " - Name: '$name' (Class: '$className', Type: '$controlType' , xy: '$bounds')" -ForegroundColor White
}
} else {
Write-Host "[ ToolbarWindow32 ]配下が見つかりません。" -ForegroundColor Yellow
}
Write-Host "---------------------------------------------------------" -ForegroundColor Cyan
<# #-- 確認用:通知領域表示の全要素の表示 --#
$rootElement2 = [System.Windows.Automation.AutomationElement]::RootElement
$children = $rootElement2.FindAll(
[System.Windows.Automation.TreeScope]::Subtree,
[System.Windows.Automation.Condition]::TrueCondition
)
$children | ForEach-Object {
$name = $_.Current.Name
$className = $_.Current.ClassName
$controlType = $_.Current.ControlType.ProgrammaticName
$bounds = $_.Current.BoundingRectangle
Write-Host " - Name: '$name' (Class: '$className', Type: '$controlType' , xy: '$bounds')" -ForegroundColor White
}
#>
▼雑説明
タスクバーから「^」というシェンブロンのボタンを探して
クリックをして、オーバーフロー通知領域(NotifyIconOverflowWindow)を表示します。
その後、通知領域内のアプリを探そうとして、ダメでした。
コードは、クリックするまでの一次コードですが
まるまるコピーしたものを後ろにつけて
通知領域が表示された後で探しても見つからず。
また、コメントアウトしている全要素表示をONにして
通知領域表示後の全要素で探してみても
「オーバーフロー通知領域」 だけが増えており
それ以外のアプリ等については、表示されず詰んだ状態です。
3.総評
・公開できない中途半端コードは他にもたくさんあるのですけど
これについては悔しすぎたので、供養させてください。。。
・なまじシェンブロンをクリックして
オーバーフロー通知領域を表示できてしまったので
その通知領域から探せば、と頑張ったのですが見つからず。
・「UIAutomationSpy」 というツールを使って
要素名や座標、タイプなどを調べ尽くした結果
・少なくとも、僕が調査/解析した限りで
PowerShellからのUI Automationでは
全く対象アプリと同じ名称や座標すら見つかりませんでした。
・僕の解析/試行錯誤結果では、
PowerShellからのUI Autometionが完璧ではなく
隠し通知領域のアプリのアイコン情報については
「取得できない」が答えになります。
今回は惨敗でした。
1ヶ月さすがに無駄に時間を使ったと思いたくなくてこんな無様に供養投稿いたします。
お目汚し本当に申し訳ありません。