【VBA】BackStageビューから戻るには【UI Automation】

VBAでBackStageビューの印刷プレビューを表示するには、

Application.CommandBars.ExecuteMso “PrintPreviewAndPrint”

としますが、では逆に、BackstageViewから戻るにはどうするのでしょう?調べてみました。

BackStageビューとは

BackStageビューというのは、Excelを例にお見せしますと、ファイルタブをクリックしたときに表示される画面です。

BackStageView

msoIDを調べる

BackStageViewのメニューを操作したいとき、msoIDを調べます。msoIDはGitHubで公開されているようですが(未確認)、私はいつもクイックアクセスツールーバに入れて調べています。この方が手っ取り早く確実です。

クイックアクセスツールバーに「印刷プレビューと印刷」を入れたところ。マウスポインタを合わせると、PrintOreviewAndPrintという英文字が見える。

クイックアクセスツールバーに調べたいコマンドを入れて、マウスを合わせると、ポップアップが出ます。

よく見ると、「印刷プレビューと印刷 (PrintPreviewAndPrint)」となっています。

このPrintPreviewAndPrintがmsoIDです。

VBAでコーディングすると、こんな感じです。

Public Sub backStageViewShow()
    
    'バックステージの印刷を開く
    Application.CommandBars.ExecuteMso "PrintPreviewAndPrint"

End Sub

ホームに戻るコマンドがない!

ところが、ホームに戻るコマンドが見つけられませんでした。

「←戻る」はありましたが、これはリンクをたどっていったときに使うコマンドでして(WebGoBackといううらしい)、BackStageViewの「←」(戻る)とは異なります。

手っ取り早く、SendKeys {ESC}(escapeキーを押下)で戻ってしまうこともできますが、もうちょっとエレガントなやり方はないものかと調べましたところ、UIAutomationが使えることが分かりました。

UI AutomationでBackStageViewから戻る

最初にコードを提示しておきます。

Option Explicit

Public Sub backStageViewShowAndClose()
    'バックステージの印刷を開く
    Application.CommandBars.ExecuteMso "PrintPreviewAndPrint"

    'すぐ閉じると何がなにやら分からないと思うので、少しだけWait入れる
    Application.Wait Now() + TimeValue("00:00:01")
   
    'バックステージを閉じる
    'UI Automationで閉じる
    'UIAutomationClient(UIAutomationCore.dll)への参照設定が必要です。
    
    Dim uiAuto As CUIAutomation
    Set uiAuto = New CUIAutomation  'UIAutoamtonを生成
    
    Dim uiCnd As IUIAutomationCondition '←これは次のElementを調べるときに使うために調べる
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_AutomationIdPropertyId, _
            "FileTabButton" _
        ) '↑このAutomationIDはInspect.exeで調べる
    
    Dim uiElm As IUIAutomationElement
    Set uiElm = uiAuto.GetRootElement.FindFirst( _
            TreeScope_Subtree, _
            uiCnd _
        ) '↑これでようやく戻るボタンの要素を把握できる

    Dim uiInvoke As IUIAutomationInvokePattern  'ボタンを押すときはいったんInvokePatternの箱を用意する
    Set uiInvoke = uiElm.GetCurrentPattern(UIA_InvokePatternId) '用意した箱に要素を入れる
    uiInvoke.Invoke 'ボタンを押す
    
    Set uiInvoke = Nothing
    Set uiElm = Nothing
    Set uiCnd = Nothing
    Set uiAuto = Nothing

End Sub

参考にしたサイトはこちら↓のふたつです。

[VBA]ファイルタブ(Backstage ビュー)の表示を禁止する。

https://www.ka-net.org/blog/?p=6596

【VBAでUIAutomation】アプリケーションをマクロで動かす

https://yaromai.jp/vba-uiautomation/

必要なもの

①VBEでツール→参照設定を選び、UIAutomationClientにチェックしてください。

UIAutomationClientへの参照設定

②Inspect.exeをダウンロードしてください。

Windows SDK とエミュレーターのアーカイブ

https://developer.microsoft.com/ja-jp/windows/downloads/sdk-archive/

Inspect.exeがないと何も始まらないです(コーディングができない)。

調べ方(UIAutomationの使い方)

まずは基本です。

UIAutomationを使うには、一番上の大枠となるウィンドウを特定する必要があります。

特定するためには、Inspect.exeを使います。

Inspect.exe

Inspect.exeで左側の一番上にある「デスクトップ」がUIAutomationの世界では「RootElement」となります。

これを最初に取得し(「RootElement」を取得するので「GetRootElement」)、そこから順番に一つずつツリーを下へ遷移して、目的の要素までたどっていきます。

その前に、Inspect.exeで名前を調べておきます。

AutomationIdのところに”FileTabButton”とある。

名前は、できる限り他と重複しないものを見つけ出せればよいのですが、アプリケーションによっては重複していたり、そもそも名称欄がブランクだったり、いろいろです。

今回はAutomationIdのところに”FileTabButton”とありました。これは特徴的な名称であり、他でも使っていなさそうだったので、これを使うことにします。なお、実際の現場ではAutomationIdを使わず、Nameを使ったり、ClassNameを使ったりする方が多いようです。

ツリーの一つ下へ遷移するのは、FindFirstというメソッドを使います。

FindFirstメソッドは引数が二つあり、一つ目は1つはツリー上の検索する位置を、もう1つはコンディションを指定する必要があります。

ツリー上の検索する位置の指定は、定数となっています。定数は↓こちらを参照してください。

https://learn.microsoft.com/ja-jp/dotnet/api/system.windows.automation.treescope?view=windowsdesktop-7.0&viewFallbackFrom=net-5.0

すぐ下だけを調べる場合は、TreeScope_Children(検索に要素の直接の子が含まれる)でよいのですが、一つ一つ調べるのがめんどくさいなと思ったら、TreeScope_Subtree(検索に検索のルートおよびすべての子孫が含まれる)を指定してもよいです(しかし、これを指定すると実行時に少し時間がかかります)。

今回はTreeScope_Subtreeを使っています。

コンディションは、CreatePropertyConditionで生成します。CreatePropertyConditionメソッドには引数が二つあり、一つ目は使う名前の種類を指定します。AutomationIdを使う場合は、UIA_AutomationIdPropertyIdです。Nameを使う場合は、UIA_NamePropertyId、ClassNameを使う場合は、UIA_ClassNamePropertyIdです。これらの定数はこちら↓で確認できます。

https://learn.microsoft.com/ja-jp/windows/win32/winauto/uiauto-automation-element-propids

二つ目は、Inspect.exeで調べた名前を入れます。

ツリーの要素が特定できたら、ボタンを押す動作を実装します。

ボタンを押す動作はInvokeメソッドです。

要素(Element)から直接Invokeメソッドを使えれば楽なんですが、いったんInvokeメソッド専用のUIにセットし、それからInvokeを呼び出す必要があります。

以上です。

おまけ

今回目的の要素を探すとき、GetRootElement(デスクトップ)からFindFirstでTreeScope_Subtreeを使って、一気に特定していましたが、これをTreeScope_SubtreeでなくTreeScope_Childrenを使って一つずつ遷移して調べるコードを紹介しておきます。

Option Explicit

Public Sub backStageViewShowAndClose()
    'バックステージの印刷を開く
    Application.CommandBars.ExecuteMso "PrintPreviewAndPrint"

    Application.Wait Now() + TimeValue("00:00:01")
   
    'バックステージを閉じる
    
    'UI Automationで閉じる
    'UIAutomationClient(UIAutomationCore.dll)要参照設定
    
    Dim uiAuto As CUIAutomation
    Set uiAuto = New CUIAutomation  'UIAutoamtonを生成
    
    '目的のElementまでTreeを一つずつ遷移して調べる
    '第1段階
    Dim uiCnd As IUIAutomationCondition '←これは次のElementを調べるときに使うために調べる
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_NamePropertyId, _
            "wordcontrols.xlsx - Excel" _
        ) '↑このNameはInspect.exeで調べる
    
    Dim uiElm As IUIAutomationElement
    Set uiElm = uiAuto.GetRootElement.FindFirst(TreeScope_Children, uiCnd)  '←RootElementはデスクトップ
    
    '第2段階
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "FullpageUIHost")
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
    
    '第3段階
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "NetUIFullpageUIWindow")
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
    
    '第4段階
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "NetUIScrollViewer")
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
    
    '第5段階
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "NetUIKeyboardTabElement")
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)
    
    '第6段階
    Set uiCnd = uiAuto.CreatePropertyCondition(UIA_AutomationIdPropertyId, "FileTabButton")
    Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)

    Dim uiInvoke As IUIAutomationInvokePattern  'ボタンを押すときはいったんInvokePatternの箱を用意する
    Set uiInvoke = uiElm.GetCurrentPattern(UIA_InvokePatternId) '用意した箱に要素を入れる
    uiInvoke.Invoke 'ボタンを押す
    
    Set uiInvoke = Nothing
    Set uiElm = Nothing
    Set uiCnd = Nothing
    Set uiAuto = Nothing
    
End Sub

第1段階だけuiAutoからGetRootElementを使うので他と異なりますが、第2段階から第6段階まで

Set uiElm = uiElm.FindFirst(TreeScope_Children, uiCnd)

と、全く同じコードになる点にご注目ください。

違うのは、uiCndの中身でして、これはInspect.exeで地道に調べるしかありません。

Inspect.exeで見たところ、目的の要素まで6段階あることが分かる。それぞれの要素のNameかClassNameなど特徴的な名称を探して、一つずつ遷移していくことになる。

似たようなコードが続くので冗長ですが、こちらの方がより確実に要素にたどり着けますので応用範囲は広いと思います。