inspectで見えるのにUIAutomationで捕捉できないオブジェクト

タグの編集
投稿者 でふぁいあんと  (社会人) 投稿日時 2022/12/28 17:20:36
お世話になっております。重ね重ねの投稿、誠にお手数をおかけいたします。
前回、UIAutomationの遷移について大変丁寧な解説を頂きましたので、
さらに考え方だけでもお聞きできないかと思いました。

例えば、Edgeにて、msnのトップページを開きます。
それをinspect.exeにて参照すると、
[デスクトップ]ウィンドウから、UIAutomationの階層を表示できます。
vbにて、その階層を追いかけます。

'デスクトップを取得
Set uiAuto = New UIAutomationClient.CUIAutomation
Set elmDesktop = uiAuto.GetRootElement
'デスクトップから、Nameをつかってedgeを取得
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
Set aryElm = elmDesktop.FindAll(TreeScope_Subtree, uiCnd)
  For lopI = 0 To aryElm.Length - 1
    'Nameで探す
    If LCase(aryElm.GetElement(lopI).CurrentName) Like "新しいタブ*" Then
    Set elmEdge = aryElm.GetElement(lopI)
    Exit For
    End If
  Next
'ClassName -> Viewを一気に取得
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "View")
Set aryElm = elmEdge.FindAll(TreeScope_Descendants, uiCnd)

'edgeのClassName->Viewは、2個めの要素
Set aryElm = aryElm.GetElement(1).FindAll(TreeScope_Children, uiAuto.CreateTrueCondition)
Set elmMSN = aryElm.GetElement(0)

'ここで、inspectを見ると、Viewの階層の下は、
'Classname => WebViewとChrome_RenderWidgetHostHWND がありますが、
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "Chrome_RenderWidgetHostHWND")
'または
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "WebView")
'のどちらを使っても
Set elmMSN = elmMSN.FindFirst(TreeScope_Children, uiCnd)
elmMSN is nothing  が trueになってしまいます。

ここで、上記2つのクラスは無視して
Viewから 全数取得
Set aryElm = elmMSN.FindAll(TreeScope_Children, uiAuto.CreateTrueCondition)
Set elmMSN = aryElm.GetElement(0)
すると、
elmMSNは、2段下の Name:新しいタブ を拾います。

つまり、WebViewと、Chrome_RenderWidgetHostHWND が無いことになっているのですが
vbだからでしょうか?
あるいは、プログラムのUIAutomationで無視されるオブジェクトは気にしなくてもいい
など
どう解釈したらよいのでしょうか。

最終的には、ブラウザのテキスト拾ったり、クリックしたりができればよいです。


「補足1」最初のブラウザを取得するところNameが環境によって違うかもしれません。
「補足2」実際のソースでは、「新しいタブ」からVIewまで、
1つずつ階層を下ってViewまで拾いましたが、ソースの掲載上2番目のviewと決め打ち
しています。
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/12/29 15:23:21
環境依存性の高いコードになっているので、
どの階層の話をしているのか、正直、状況が見えてきません…。

手元の環境では、Edge を起動しても、あるいはそこで MSN を開いても、
 「"新しいタブ" で始まる Name」が存在しませんでした。(この辺りは設定依存)

なので、その子孫の「"View"という ClassName」の要素が
何を示しているのか検討が付きませんでした。


> それをinspect.exeにて参照すると、
Inspact のような一括フェッチが必要な場合はキャッシュを使用します。
https://learn.microsoft.com/ja-jp/windows/win32/winauto/uiauto-cachingforclients

※TreeScope_Parent / TreeScope_Ancestors は使用できないので、
 親階層を辿りたいような場合にもキャッシュが必要になってきます。


> Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ControlTypePropertyId, UIA_WindowControlTypeId)
> Set aryElm = elmDesktop.FindAll(TreeScope_Subtree, uiCnd)

デスクトップからこの条件だけで Subtree を絞り込むのは、若干大雑把過ぎませんか?
今回は Inspect の調査ということで、あえてそうしているのかもしれませんが、
仮に、Edge 本体を捕らえることが目的なのであれば
 ・ClassName が "Chrome_WidgetWin_1" と一致
 ・ControlType が UIA_WindowControlTypeId と一致
 ・Name の末尾が "- Microsoft" & ChrW(&H200B) & " Edge"
という AND 条件で絞り込めます。
末尾検索はできないので、そこはループで If 判定が必要ですけれどね。

Edge や Chrome は、どのタブがアクティブになっているのかどうかで、
Chrome_RenderWidgetHostHWND などの階層が動的に変化する点にも注意。
https://twitter.com/benshi_orator/status/1396770128012791818?lang=ja



> Set elmMSN = elmMSN.FindFirst(TreeScope_Children, uiCnd)
> elmMSN is nothing  が trueになってしまいます。
この手の検証時には、elmMSN 変数を使いまわさず、別変数に Set した方が良いでしょう。

また、今回は子孫探索という大雑把な絞り込みをしているので、
elmEdge や FindFirst 前の elmMSN で受け取った要素が
inspect.exe で表示しているものと同じものであるかどうか、
RuntimeId を比較検査した方が良いかもしれません。
※逆に UIA_RuntimeIdPropertyId 条件で絞り込むという手もあり。

rid = ""
For Each x In elmEdge.GetRuntimeId()
    rid = rid & (" " & Hex(x))
Next
rid = Replace(Trim(rid), " "".")
Debug.Print rid



> 最終的には、ブラウザのテキスト拾ったり、クリックしたりができればよいです。
ブラウザーによっては、 Accessibility の設定変更が必要になるケースがあるようです。
Edge の場合は、アドレスバーに「edge://accessibility/」を入力します。
Chrome だと「chrome://accessibility/」ですね。

https://knowledge.bizrobo.com/hc/ja/articles/4402840941197
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/1/1 16:05:32
明けましておめでとうございます。

今まで UIAutomation に触れる機会が殆ど無かったので、年越しで
UIAutomation と戯れて、いろいろなブラウザーのリロード処理を作っていました。
https://gist.github.com/Benshi/514f1873f1c61280b25f1fe85d935dfc

IE/Edge/Chrome/FireFox 対応、シークレットモードの有無や IEMode かどうかの判定も搭載。
VBA ではなく VB.NET ですけれどね。

ーー
①タブブラウザのタブを分離・結合すると、以前にそのブラウザーで取得済みの
 IUIAutomationElement は切断状態になり、使えなくなる模様

 → Inspect においても、自作プログラムにおいても同じ状況になった
  改めてルートから再取得して、最新の階層を得れば再操作できる

 → オブジェクトが使えなくなった(切断状態となった)ことを知る方法があるのかは不明


②IE11 の場合、最小化していると、最小限の階層にしかアクセスできなくなるらしい
 Inspect から見た場合も自作アプリから見た場合も、同じ結果となった

⊟"{ブラウザのタイトルバー}" ウィンドウ (ControlType=Window, ClassName="IEFrame")
 ┣⊟"" ウィンドウ(ControlType=Pane, ClassName="CommandBarClass")
 ┗⊟(null) タイトルバー(ControlType=Pane, AutomationId="TitleBar")
   ┣⊟"システム" メニューバー(ControlType=Button, AutomationId="Close")
   ┃ ┗⊡"システム" メニュー項目(ControlType=MenuItem)
   ┣⊡"元のサイズに戻す" ボタン(ControlType=Button, AutomationId="Minimize-Restore")
   ┣⊡"最大化" ボタン(ControlType=Button, AutomationId="Maximize-Restore")
   ┗⊡"閉じる" ボタン(ControlType=Button, AutomationId="Close")

 → ところが、その IE をアクティブにした後で Inspect を Refresh すると、階層が急に増える
  (IE 以外のアプリでも、類似の事象が発生)

⊟"{ブラウザのタイトルバー}" ウィンドウ (ControlType=Window, ClassName="IEFrame")
 ┣⊟"" ウィンドウ(ControlType=Pane, ClassName="Client Caption")
 ┣⊟"ナビゲーション バー" ウィンドウ(ControlType=Pane, ClassName="WorkerW")
 ┃ ┗⊟"" ウィンドウ(ControlType=Pane, ClassName="ReBarWindow32")
 ┃   ┣⊞"" ウィンドウ(ControlType=Pane, ClassName="CommandBarClass")
 ┃   ┣⊟"アドレス バー" ウィンドウ(ControlType=Pane, ClassName="Address Band Root")
 ┃   ┃ ┣⊞"" ウィンドウ(ControlType=Pane, ClassName="AddressDisplay Control")
 ┃   ┃ ┣⊡"" 編集(ControlType=Edit, ClassName="Edit")
 ┃   ┃ ┣⊞"アドレス コンボ コントロール" ツール バー(ControlType=ToolBar, ClassName="ToolbarWindow32")
 ┃   ┃ ┗⊞"ページ コントロール" ツール バー(ControlType=ToolBar, ClassName="ToolbarWindow32")
 ┃   ┣⊞"検索バー" ウィンドウ(ControlType=Pane, ClassName="Address Band Root")
 ┃   ┃ ┣⊡"" 編集(ControlType=Edit, ClassName="Edit")
 ┃   ┃ ┗⊞"アドレス コンボ コントロール" ツール バー(ControlType=ToolBar, ClassName="ToolbarWindow32")
 ┃   ┣⊞"" ウィンドウ(ControlType=Pane, ClassName="ControlBandClass")
 ┃   ┃ ┗⊞"お気に入りとツール バー" ツール バー(ControlType=ToolBar, ClassName="ToolbarWindow32")
 ┃   ┗⊟"" ウィンドウ(ControlType=Pane, ClassName="ControlBandClass")
 ┃     ┗⊞"" ウィンドウ(ControlType=Pane, ClassName="DirectUIHWND")
 ┣⊡"" ウィンドウ(ControlType=Pane, ClassName="CommandBarClass")
 ┣⊟"" ウィンドウ(ControlType=Pane, ClassName="ControlBandClass")
 ┃ ┣⊡"ITBarHost" ウィンドウ(ControlType=Pane, ClassName="InternetToolbarHost")
 ┃ ┗⊞"{ブラウザのタイトルバー}" ツール バー(ControlType=ToolBar, ClassName="TabWindowClass")
 ┗⊟(null) タイトルバー(ControlType=Pane, AutomationId="TitleBar")
   ┣⊟"システム" メニューバー(ControlType=Button, AutomationId="Close")
   ┃ ┗⊡"システム" メニュー項目(ControlType=MenuItem)
   ┣⊡"元のサイズに戻す" ボタン(ControlType=Button, AutomationId="Minimize-Restore")
   ┣⊡"最大化" ボタン(ControlType=Button, AutomationId="Maximize-Restore")
   ┗⊡"閉じる" ボタン(ControlType=Button, AutomationId="Close")

 → しかし、プログラムから IEFrame の要素を見た場合、微妙に異なる階層構造が得られる

⊟"{ブラウザのタイトルバー}" ウィンドウ (ControlType=Window, ClassName="IEFrame")
 ┣⊟"" ウィンドウ(ControlType=Pane, ClassName="Client Caption")/子要素0
 ┣⊞"" ウィンドウ(ControlType=Pane, ClassName="ReBarWindow32")/子要素5 … "WorkerW" の階層が消えた!
 ┣⊡"" ウィンドウ(ControlType=Pane, ClassName="CommandBarClass")/子要素0
 ┣⊞"" ウィンドウ(ControlType=Pane, ClassName="Frame Tab")/子要素2 … "ControlBandClass" ではない!
 ┗⊞"" タイトルバー(ControlType=Pane, AutomationId="TitleBar")/子要素4

 → Inspect の 32bit/64bit 版での差異ではなさそう
 → Cache で一括取得したかどうかや、TreeWalkerView で得たかどうかで変わる?
 → あるいは Unmanaged な COM 版と Managed な .NET 版の違いによるもの?
 → 現時点では原因不明のため、Inspect は参考情報と割り切っている


③Firefox のブラウザーを Inspect で開くと、 FrameworkId が "Gecko" と表示されるが
 プログラムから取得した FrameworkId は "Win32" となる

 → Inspect では本来は、右側のリストがゴシック体で表示されるが、
  対 Firefox の場合、何故か先頭の How found: のみがゴシック体で、
  それ以外の項目はすべて 明朝体のフォントで描画されている

 → Firefox インストール後、まだ OS を再起動していないのと何か関係あるだろうか?
  何にせよ FrameworkId が得られない原因は分からなかったので
  FrameworkId は検索条件として使わないことにした (それ以外の条件で絞り込んで回避)
投稿者 でふぁいあんと  (社会人) 投稿日時 2023/1/2 21:49:42
魔界の仮面弁士様 正月早々のお返事ありがとうございます。

まずは、環境ですが、
職場がwin10 64bit + edge
自宅が、win11 64bit + edgeです。

>デスクトップからこの条件だけで Subtree を絞り込むのは、若干大雑把過ぎませんか?
おっしゃる通りです。手を抜いて申し訳ないです。
とりあえず過去に動作したソースを利用しました。

また、msnだと画面が動くWebなので、
改めてこちらの
[VisualBasic中学校]のトップページで確認しました。
edgeのタブは1つのみです。

そこで再度、Edgeから、ClassNameで、追いかけると
Chrome_WidgetWin_1
 BrowserRootView
  NonClientView
   GlassBrowserFrameView
    BrowserView
     SidebarContentsSplitView
      SidebarContentsSplitView
       SidebarContentsSplitView
        SidebarContentsSplitView
         View

ここまでは、追えるのですが、
Inspectにて、Viewの下を確認すると

View(Class)
 |--WebView(Class)
 |--Chrome_RenderWidgetHostHWND(Class)
     |-Visual Basic 中学校(Name)---->(a)
となっていますが

プログラムから
Viewの配下をClassNameにて検索すると
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "WebView")
または
Set uiCnd = uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "Chrome_RenderWidgetHostHWND")
両方とも、存在が無いことになります。
そこで
Viewから
Set aryElm = elmOne.FindAll(TreeScope_Children, uiAuto.CreateTrueCondition)
Set elmSecond = aryElm.GetElement(0)
こうすると、
elmSecondは=Visual Basic 中学校(Name)(a)の要素を取得します。

このClassName:WebView
及び ClassName;Chrome_RenderWidgetHostHWND
を、拾えないのは何故なのでしょうか

>Chrome_RenderWidgetHostHWND などの階層が動的に変化する点にも注意。
Visual Basic 中学校 を開いたままブラウザを更新して
Inspectも更新して、階層を確認すると
やはり
ClassName;Chrome_RenderWidgetHostHWNDは存在しています。


  


投稿者 (削除されました)  () 投稿日時 2023/1/3 06:51:11
(削除されました)
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/1/3 10:17:09
Excel VBA の UserForm 上に、 UIAutomation の TreeWalker での列挙内容を
エクスプローラーのように TreeView + ListView で表現するサンプルを書いてみました。
https://gist.github.com/Benshi/be84f9a9ea2f467b43703f9e8e409f1d


> そこで再度、Edgeから、ClassNameで、追いかけると
上記の VBA 版だと拾えているようです。TreeWalker は試されましたか?

⊟[Window]"Chrome_WidgetWin_1"
 ┣⊡[Pane]"Intermediate D3D Window"
 ┣⊡[Pane]"Intermediate D3D Window"
 ┗⊟[Pane]"BrowserRootView"
   ┣⊟[Pane]"NonClientView"
   ┃ ┣⊟[Pane]"GlassBrowserFrameView"
   ┃ ┃ ┣⊡[Pane]"InkDropContainerView"
   ┃ ┃ ┣⊡[Text]"Label"
   ┃ ┃ ┣⊞[Pane]"GlassBrowserCaptionButtonContainer"
   ┃ ┃ ┗⊟[Pane]"BrowserView"
   ┃ ┃   ┣⊞[Pane]"TopContainerView"
   ┃ ┃   ┣⊞[Pane]"EdgeHubAppsTowerView"
   ┃ ┃   ┣⊟[Pane]"SidebarContentsSplitView"
   ┃ ┃   ┃ ┣⊟[Pane]"SidebarContentsSplitView"
   ┃ ┃   ┃ ┃ ┣⊟[Pane]"SidebarContentsSplitView"
   ┃ ┃   ┃ ┃ ┃ ┣⊟[Pane]"SidebarContentsSplitView"
   ┃ ┃   ┃ ┃ ┃ ┃ ┣⊞[Group]"InfoBarContainerView"
   ┃ ┃   ┃ ┃ ┃ ┃ ┣⊡[Pane]"AccessiblePaneView"
   ┃ ┃   ┃ ┃ ┃ ┃ ┣⊞[Pane]"View" ← ★今回の問題の View
   ┃ ┃   ┃ ┃ ┃ ┃ ┃ ┣⊟[Document]"WebView"
   ┃ ┃   ┃ ┃ ┃ ┃ ┃ ┃ ┗⊡[Pane]"NativeViewHost"
   ┃ ┃   ┃ ┃ ┃ ┃ ┃ ┗⊞[Pane]"Chrome_RenderWidgetHostHWND"
   ┃ ┃   ┃ ┃ ┃ ┃ ┃   ┗⊞[Document]"" ← Name = "Visual Basic 中学校"
   ┃ ┃   ┃ ┃ ┃ ┃ ┗⊞[Pane]"ReactiveSearchView"
   ┃ ┃   ┃ ┃ ┃ ┗⊞[Pane]"SidePaneRootContainer"
   ┃ ┃   ┃ ┃ ┗⊞[Pane]"SidePaneRootContainer"
   ┃ ┃   ┃ ┗⊞[Pane]"SidePaneRootContainer"
   ┃ ┃   ┣⊞[Tab]"EdgeVertialTabContainerView"
   ┃ ┃   ┣⊡[Pane]"View"
   ┃ ┃   ┗⊡[Pane]"View"
   ┃ ┗⊡[Pane]"TopContainerOverlayView"
   ┗⊡[Text]"AnnounceTextView"
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/1/3 10:36:50
検索条件指定でヒットしない理由は、 "WebView" と "Chrome_RenderWidgetHostHWND" の
IsControlElement が false である点にあるようです。

FindFirst は RawTree ではなく ControlTree を探索するため、
ターゲット要素が IsControlElement = true でないと拾えないのだとか。
https://stackoverflow.com/questions/14187110/uiautomation-wont-retrieve-children-of-an-element

過去には、IsControlElement が予期せず変化してしまう不具合もあったそうな…。
https://github.com/microsoft/microsoft-ui-xaml/issues/3259

> Viewから
> Set aryElm = elmOne.FindAll(TreeScope_Children, uiAuto.CreateTrueCondition)
> Set elmSecond = aryElm.GetElement(0)
> こうすると、
> elmSecondは=Visual Basic 中学校(Name)(a)の要素を取得します。

Inspect の [Options] メニューを [Raw View] から [Control View] に切り替えてみてください。

[Options] - [Control View] 時の見え方
┣{ClassName = "View", Name = "", ControlType = Pane(0xC371)}
┃┗{ClassName = "", Name = "Visual Basic 中学校", ControlType = Document(0xC36E)}

[Options] - [Raw View] 時の見え方
┣{ClassName = "View", Name = "", ControlType = Pane(0xC371)}
┃┣{ClassName = "WebView", Name = "", ControlType = Document(0xC36E)}
┃┗{ClassName = "Chrome_RenderWidgetHostHWND", Name = "Chrome Legacy Window", ControlType = Pane(0xC371)}
┃ ┗{ClassName = "", Name = "Visual Basic 中学校", ControlType = Document(0xC36E)}
┃  ┣{ClassName = "MainTitle", Name = "Visual Basic 中学校", ControlType = Text(0xC364)}
┃  :


Dim uiAuto As CUIAutomation8: Set uiAuto = New CUIAutomation8
Dim ui(0 To 15) As IUIAutomationElement9

' {ClassName = "View", Name = "", ControlType = UIA_PaneControlTypeId (0xC371)} 
Set ui(0) = GetTargetView(uiAuto)  ' ← 目標の ClassName = "View" を取得する関数  

' Nothing 
Set ui(1) = ui(0).FindFirst(TreeScope_Children, uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "WebView"))
Set ui(2) = ui(0).FindFirst(TreeScope_Children, uiAuto.CreatePropertyCondition(UIA_ClassNamePropertyId, "Chrome_RenderWidgetHostHWND"))

' {ClassName = "WebView", Name = "", ControlType = UIA_DocumentControlTypeId(0xC36E)} 
Set ui(3) = uiAuto.RawViewWalker.GetFirstChildElement(ui(0))

' {ClassName = "Chrome_RenderWidgetHostHWND", Name = "Chrome Legacy Window", ControlType = UIA_PaneControlTypeId (0xC371)} 
Set ui(4) = uiAuto.RawViewWalker.GetNextSiblingElement(ui(3))

' {ClassName = "", Name = "Visual Basic 中学校", ControlType = UIA_DocumentControlTypeId(0xC36E)} 
Set ui(5) = uiAuto.ControlViewWalker.GetFirstChildElement(ui(0))
Set ui(6) = uiAuto.ContentViewWalker.GetFirstChildElement(ui(0))
Set ui(7) = ui(0).FindFirst(TreeScope_Children, uiAuto.RawViewCondition)
Set ui(8) = ui(0).FindFirst(TreeScope_Children, uiAuto.ControlViewCondition)
Set ui(9) = ui(0).FindFirst(TreeScope_Children, uiAuto.ContentViewCondition)

' {ClassName = "MainTitle", Name = "Visual Basic 中学校", ControlType = UIA_TextControlTypeId(0xC364)} 
Set ui(10) = ui(4).FindFirst(TreeScope_Children, uiAuto.CreatePropertyConditionEx(UIA_NamePropertyId, "中学", PropertyConditionFlags_MatchSubstring))
Set ui(11) = ui(4).FindFirst(TreeScope_Children, uiAuto.CreateTrueCondition())
Set ui(12) = ui(4).FindFirst(TreeScope_Children, uiAuto.RawViewCondition)
Set ui(13) = ui(4).FindFirst(TreeScope_Children, uiAuto.ControlViewCondition)
Set ui(14) = ui(4).FindFirst(TreeScope_Children, uiAuto.ContentViewCondition)
Set ui(15) = uiAuto.RawViewWalker.GetFirstChildElement(ui(5))


 <Inspect の Raw View で確認できる階層> 先頭の■は IsControlElement の 🟩true/🟥false を示す
⊟🟩[Pane]"View" ←………………………………………………… ui(0)
┣⊞🟥[Document]"WebView" ←……………………………………… ui(3)
┗⊞🟥[Pane]"Chrome_RenderWidgetHostHWND" ←………………… ui(4)
  ┗⊟🟩[Document]"" {Name = "Visual Basic 中学校"} ←… ui(5~9)
    ┣⊞🟩[Text]"MainTitle" ←……………………………… ui(10~15)
    :
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2023/1/3 11:03:56
> ③Firefox のブラウザーを Inspect で開くと、 FrameworkId が "Gecko" と表示されるが
>  プログラムから取得した FrameworkId は "Win32" となる

Inspect.exe (Visual C++ 製) や VBA の場合は FrameworkId: "Gecko" だったので、
上記は COM と .NET の差で生じた問題なのだろうと予想しましたが……。

VisualUIAVerifyNative.exe (C#, WinForms) や 
AccessibilityInsights.exe (C#, .WPF) においても、
FrameworkId は "Gecko" と表示されていました。
https://learn.microsoft.com/ja-jp/windows/win32/winauto/ui-automation-verify
https://accessibilityinsights.io/

これらは .NET (System.Windows.Automation) を使っているという点は同じはずなのに、
なぜ自分の環境では "Win32" 表記なのか…なかなか奥が深いです。


上記以外にも、C# と PowerShell で結果が異なる事象がある模様。
https://qiita.com/mima_ita/items/3f2aa49fceca7496c587
投稿者 でふぁいあんと  (社会人) 投稿日時 2023/1/4 17:12:59
魔界の仮面弁士様 正月早々 お世話になりました、。

またもや永久保存版の資料を作成してくださり
誠に感謝いします。
なんか、入力だけでも相当な手間をおかけしまして恐縮です。。。ほんとうに

大変、大変ありがとうございました。