C# と VB.NET の質問掲示板
ASP.NET、C++/CLI、Java 何でもどうぞ
C# と VB.NET の入門サイト
掲示板トップ
C# と VB.NET のサンプル
新規作成
利用方法/規約
ツリー表示
トピック表示
ランキング
記事検索
過去ログ
■103692
/ 2階層)
EXCEL VBAからのWindows.Forms呼出方法
□投稿者/ PATIO
(18回)-(2025/05/26(Mon) 09:30:21)
魔界の仮面弁士 さん、お久しぶりです。
色々有用な情報をいただけたのでインラインで返信します。
■
No103690
(魔界の仮面弁士 さん) に返信
> ■
No103689
(PATIO さん) に返信
>>EXCEL VBAからWindows.Formsのクラスライブラリを操作したいのですが、可能でしょうか?
>>とある自動化案件で対象のプログラムが.net Frameworkをベース開発した独自GUIライブラリを使用しており、
> その .NET Framework 製の「独自GUIライブラリ」というのは、どういったものなのでしょうか。
>
> それが VSTO なのか ActiveX DLL なのか分かりませんが、
> そのライブラリ内から System.Windows.Forms.Form を呼んでいるのではないのでしょうか。
某社が.net frameworkベースで開発してるGUIライブラリです。
標準のGUIの見た目が気に入らないので色々装飾を付けたりする為にラップしているイメージです。
例で言うとタイトルバーのバックグラウンドの塗潰しにグラデーションを付けたいとかそういう類の物です。
ボタンの背景色を変えたいとか。
問題は、この辺の実装をする為に従来のWindowsの処理の流れを全く無視した形で実装しているので
Win32APIで情報を取得しようとしても取得できない。設定しようとしても設定できない状態にある事です。
>>Win32APIを使ったファンクションでは、情報が拾えないケースが出てきています。
> 何に対して、どんな情報を拾おうとしているのでしょうか?
> 相手が System.Windows.Forms 名前空間のコントロールなのであれば、
> その DLL 自体に操作用のメソッドを追加するのが手っ取り早いと思うのですが…。
他社が開発した物なので勝手にインターフェイスを追加する事も出来ませんし、
そもそもライブラリの仕様すら公開されていません。
見えている範囲で外部のアプリケーションから操作しようとしています。
具体的には作ってもらったアプリは人間が操作して使う事しか想定されておらず、
リストに基づいて処理を進めるようなインターフェイスが無いので
EXCELマクロを利用してその部分の自動化をしようとしています。
> 既存の DLL に手を入れらないまま外部操作を試みているなら、
> Win32、WinForms、WPF を外部操作可能な「Codeer Friendly」で操作できるか試してみるとか。
>
https://ishikawa-tatsuya.hatenablog.com/entry/2014/12/12/011333
>
https://github.com/Codeer-Software/Friendly/blob/master/README.jp.md
>
https://qiita.com/murasuke/items/936c4e12af314777e7b2
> VBA からの操作が必要となると、Codeer Friendly をカプセル化する必要があるかもしれないので
> 今回の要件を満たすかどうかは何とも言えませんが。
これは見つけられていませんでした。
可能性があるのであれば、探して調査してみます。
>>具体的には対象のコントロールのウインドウハンドルを取得しようとしても特定が出来ない。
> その「対象のコントロール」というのが、どこにある何者なのかも不明ですし、
> どうやって hWnd を取得しようとしているのかについての言及も無いので、
> すべて憶測でしか答えられませんが、もしもウィンドウハンドルが得られないのだとすれば、
> そもそもウィンドウレスコントロールという可能性もありますね。
> その場合は、UI Automation か MSAA に頼ることになりそう。
さすがにウインドウレスというわけではないです。
一応、SPY++で参照は出来ますので。
但し、コントロールIDはパーマネントではなく、ウインドウハンドルが突っ込まれているようで
コントロールIDも使えませんし、ベースのウインドウにタイトルが設定されていない状態なので
対象のウインドウを狙い撃ちでウインドウハンドルを取得できません。
よくやる方法としてベースのウインドウのハンドルに関してはタイトルを使って判別し、
状況によっては特定のコントロールを子供に持っているかを使っています。
>>使用されているコントロールの内、同時実装分の物でWM_GETTEXTが全く効かない為、判別が出来ません。
> ウィンドウハンドルは取得できるが、WM_GETTEXT で情報が得られかったという話をしているのでしょうか。
> ウィンドウハンドルがあるからといって、WM_GETTEXT で情報が得られる保証は無いですよね。
> それはターゲットのウィンドウ次第なわけで。
その通りです。
ターゲットウインドウが通常のウインドウの構成と全く異なる為に取得できない状態にあります。
そもそもコントロールIDが使えない時点で怪しかったんですが、確認してみたら案の定という感じです。
>>Accessibility Insights For Windowsでみた所、Nameプロパティで拾えるケースがある事はわかりましたが、
>>EXCEL VBAから拾う方法がわかりません。
> VBA からであれば、MSAA の IAccessible インターフェイスで取得できると思います。
> VBA からだと、IAccessible の accName プロパティかな。
>
> IAccessible を得るには、
> 方法1) 座標が分かっている場合は、AccessibleObjectFromPoint API
> 方法2) hWnd が分かっている場合は、AccessibleObjectFromWindow API
> 方法3) hWnd が無い場合は、ウィンドウを持つ祖先オブジェクトから子孫要素を辿る
> といった方法があります。
> 昔は、OLEACC.DLL を参照設定する必要がありましたが、
> 最近の Excel なら、標準で Dim objAcc As Office.IAccessible と書けるので、そっちでも良いかも。
> 'Call AccessibleObjectFromPoint32(x, y, objAcc, vnt)
> 'Call AccessibleObjectFromPoint64(y * &H100000000^ Or x, objAcc, vnt)
> Call AccessibleObjectFromWindow(ByVal hWnd, OBJID_NATIVEOM, guidIDispatch, objAcc)
> Debug.Print objAcc.accName
>
> ※AccessibleObjectFromPoint の第一引数は POINT 構造体を「値渡し」する仕様なので、
> 32bit VBA から呼ぶ場合は、第一引数を X 引数と Y 引数に分割したり、
> 64bit VBA から呼ぶ場合は、X と Y を単一の 64bit 整数型にまとめてから値渡しするなどの工夫が必要。
>
>
> あるいは、MSAA の後継たる UI Automation 経由でも得られるかと。
> 参照設定で UIAutomationClient を加えておいたうえで、
> UIAutomationClient クラスの ElementFromHandle メソッドか ElementFromPoint メソッドを使うと、
> 操作対象の IUIAutomationElement を得られるので、そこから
> Dim objUIAuto As UIAutomationClient.CUIAutomation
> Set objUIAuto = New UIAutomationClient.CUIAutomation
> Dim objIUIAutomationElement As UIAutomationClient.IUIAutomationElement
> Set objIUIAutomationElement = objUIAuto.ElementFromHandle(ByVal hWnd) 'もしくは ElementFromPoint
> Dim objlegacy As UIAutomationClient.IUIAutomationLegacyIAccessiblePattern
> Set objlegacy = objIUIAutomationElement.GetCurrentPattern(UIA_LegacyIAccessiblePatternId)
> Debug.Print legacy.CurrentName
> といった感じかと。
有用な情報ありがとうございます。
糸口になりそうなのでこちらも調べてみます。
社内で作成している業務用のツールに関しては見た目に凝ったりしないので
素直にMicrosoftのライブラリを使用しているのでWin32APIで処理できる事がほとんどなのですが、
外部で開発されたアプリに関しては見た目を飾る為に通常の実装とは異なる実装をされているケースがあり、
自動化を行う場合の障害になっています。
自動化の要件が結構複雑で単なる固定の項目を選択すれば済むというような物ではないので
画面のコントロールから情報を拾う必要があるんですが、これがほとんどできない状態なので
うまく行かない状況なのです。
>>拾ったウインドウハンドルからForm.FromHandleで紐づけて拾えるのではと思っていますが、
> NativeWindows.FromHandle メソッドや Control.FromHandle の話をされているのですか?
イメージとしてはControl.FromHandleです。
これでウインドウハンドルからFormに紐づけられれば、Nameプロパティを参照できないかと考えました。
編集キー/
編集
前の記事
(元になった記事)
次の記事
(この記事の返信)
←Re[1]: EXCEL VBAからのWindows.Forms呼出方法
/魔界の仮面弁士
→Re[3]: EXCEL VBAからのWindows.Forms呼出方法
/魔界の仮面弁士
上記関連ツリー
EXCEL VBAからのWindows.Forms呼出方法
/ PATIO
(25/05/23(Fri) 11:53)
#103689
└
Re[1]: EXCEL VBAからのWindows.Forms呼出方法
/ 魔界の仮面弁士
(25/05/23(Fri) 14:09)
#103690
└
EXCEL VBAからのWindows.Forms呼出方法
/ PATIO
(25/05/26(Mon) 09:30)
#103692
←Now
└
Re[3]: EXCEL VBAからのWindows.Forms呼出方法
/ 魔界の仮面弁士
(25/05/26(Mon) 10:44)
#103694
上記ツリーを一括表示
/
上記ツリーをトピック表示
上記の記事へ返信
入力内容にタグは利用できません。
他人を中傷する記事は管理者の判断で予告無く削除されます。
半角カナは使用しないでください。文字化けの原因になります。
名前、コメントは必須記入項目です。記入漏れはエラーになります。
入力内容の一部は、次回投稿時の手間を省くためブラウザに記録されます。
URL は自動的にリンクされます。
記事中に No*** のように書くとその記事にリンクされます(No は半角英字/*** は半角数字)。
名前
(*必須)
E-Mail (任意)
Web サイト (任意)
投稿モード
通常モード
図表モード (ソースコードを貼る場合はこちらを使用)
本文
(質問の場合は、開発環境・使用言語のバージョンなどを最初に書いてください)
■No103692 (PATIO さん) に返信 > 魔界の仮面弁士 さん、お久しぶりです。 > 色々有用な情報をいただけたのでインラインで返信します。 > > ■No103690 (魔界の仮面弁士 さん) に返信 >>■No103689 (PATIO さん) に返信 > >>EXCEL VBAからWindows.Formsのクラスライブラリを操作したいのですが、可能でしょうか? > >>とある自動化案件で対象のプログラムが.net Frameworkをベース開発した独自GUIライブラリを使用しており、 >>その .NET Framework 製の「独自GUIライブラリ」というのは、どういったものなのでしょうか。 >> >>それが VSTO なのか ActiveX DLL なのか分かりませんが、 >>そのライブラリ内から System.Windows.Forms.Form を呼んでいるのではないのでしょうか。 > > 某社が.net frameworkベースで開発してるGUIライブラリです。 > 標準のGUIの見た目が気に入らないので色々装飾を付けたりする為にラップしているイメージです。 > 例で言うとタイトルバーのバックグラウンドの塗潰しにグラデーションを付けたいとかそういう類の物です。 > ボタンの背景色を変えたいとか。 > 問題は、この辺の実装をする為に従来のWindowsの処理の流れを全く無視した形で実装しているので > Win32APIで情報を取得しようとしても取得できない。設定しようとしても設定できない状態にある事です。 > > > >>Win32APIを使ったファンクションでは、情報が拾えないケースが出てきています。 >>何に対して、どんな情報を拾おうとしているのでしょうか? >>相手が System.Windows.Forms 名前空間のコントロールなのであれば、 >>その DLL 自体に操作用のメソッドを追加するのが手っ取り早いと思うのですが…。 > > 他社が開発した物なので勝手にインターフェイスを追加する事も出来ませんし、 > そもそもライブラリの仕様すら公開されていません。 > 見えている範囲で外部のアプリケーションから操作しようとしています。 > 具体的には作ってもらったアプリは人間が操作して使う事しか想定されておらず、 > リストに基づいて処理を進めるようなインターフェイスが無いので > EXCELマクロを利用してその部分の自動化をしようとしています。 > > >>既存の DLL に手を入れらないまま外部操作を試みているなら、 >>Win32、WinForms、WPF を外部操作可能な「Codeer Friendly」で操作できるか試してみるとか。 >>https://ishikawa-tatsuya.hatenablog.com/entry/2014/12/12/011333 >>https://github.com/Codeer-Software/Friendly/blob/master/README.jp.md >>https://qiita.com/murasuke/items/936c4e12af314777e7b2 >>VBA からの操作が必要となると、Codeer Friendly をカプセル化する必要があるかもしれないので >>今回の要件を満たすかどうかは何とも言えませんが。 > > これは見つけられていませんでした。 > 可能性があるのであれば、探して調査してみます。 > > >>具体的には対象のコントロールのウインドウハンドルを取得しようとしても特定が出来ない。 >>その「対象のコントロール」というのが、どこにある何者なのかも不明ですし、 >>どうやって hWnd を取得しようとしているのかについての言及も無いので、 >>すべて憶測でしか答えられませんが、もしもウィンドウハンドルが得られないのだとすれば、 >>そもそもウィンドウレスコントロールという可能性もありますね。 >>その場合は、UI Automation か MSAA に頼ることになりそう。 > > さすがにウインドウレスというわけではないです。 > 一応、SPY++で参照は出来ますので。 > 但し、コントロールIDはパーマネントではなく、ウインドウハンドルが突っ込まれているようで > コントロールIDも使えませんし、ベースのウインドウにタイトルが設定されていない状態なので > 対象のウインドウを狙い撃ちでウインドウハンドルを取得できません。 > よくやる方法としてベースのウインドウのハンドルに関してはタイトルを使って判別し、 > 状況によっては特定のコントロールを子供に持っているかを使っています。 > > >>使用されているコントロールの内、同時実装分の物でWM_GETTEXTが全く効かない為、判別が出来ません。 >>ウィンドウハンドルは取得できるが、WM_GETTEXT で情報が得られかったという話をしているのでしょうか。 >>ウィンドウハンドルがあるからといって、WM_GETTEXT で情報が得られる保証は無いですよね。 >>それはターゲットのウィンドウ次第なわけで。 > > その通りです。 > ターゲットウインドウが通常のウインドウの構成と全く異なる為に取得できない状態にあります。 > そもそもコントロールIDが使えない時点で怪しかったんですが、確認してみたら案の定という感じです。 > > >>Accessibility Insights For Windowsでみた所、Nameプロパティで拾えるケースがある事はわかりましたが、 > >>EXCEL VBAから拾う方法がわかりません。 >>VBA からであれば、MSAA の IAccessible インターフェイスで取得できると思います。 >>VBA からだと、IAccessible の accName プロパティかな。 >> >>IAccessible を得るには、 >> 方法1) 座標が分かっている場合は、AccessibleObjectFromPoint API >> 方法2) hWnd が分かっている場合は、AccessibleObjectFromWindow API >> 方法3) hWnd が無い場合は、ウィンドウを持つ祖先オブジェクトから子孫要素を辿る >>といった方法があります。 >>昔は、OLEACC.DLL を参照設定する必要がありましたが、 >>最近の Excel なら、標準で Dim objAcc As Office.IAccessible と書けるので、そっちでも良いかも。 >> 'Call AccessibleObjectFromPoint32(x, y, objAcc, vnt) >> 'Call AccessibleObjectFromPoint64(y * &H100000000^ Or x, objAcc, vnt) >> Call AccessibleObjectFromWindow(ByVal hWnd, OBJID_NATIVEOM, guidIDispatch, objAcc) >> Debug.Print objAcc.accName >> >>※AccessibleObjectFromPoint の第一引数は POINT 構造体を「値渡し」する仕様なので、 >> 32bit VBA から呼ぶ場合は、第一引数を X 引数と Y 引数に分割したり、 >> 64bit VBA から呼ぶ場合は、X と Y を単一の 64bit 整数型にまとめてから値渡しするなどの工夫が必要。 >> >> >>あるいは、MSAA の後継たる UI Automation 経由でも得られるかと。 >>参照設定で UIAutomationClient を加えておいたうえで、 >>UIAutomationClient クラスの ElementFromHandle メソッドか ElementFromPoint メソッドを使うと、 >>操作対象の IUIAutomationElement を得られるので、そこから >> Dim objUIAuto As UIAutomationClient.CUIAutomation >> Set objUIAuto = New UIAutomationClient.CUIAutomation >> Dim objIUIAutomationElement As UIAutomationClient.IUIAutomationElement >> Set objIUIAutomationElement = objUIAuto.ElementFromHandle(ByVal hWnd) 'もしくは ElementFromPoint >> Dim objlegacy As UIAutomationClient.IUIAutomationLegacyIAccessiblePattern >> Set objlegacy = objIUIAutomationElement.GetCurrentPattern(UIA_LegacyIAccessiblePatternId) >> Debug.Print legacy.CurrentName >>といった感じかと。 > > 有用な情報ありがとうございます。 > 糸口になりそうなのでこちらも調べてみます。 > 社内で作成している業務用のツールに関しては見た目に凝ったりしないので > 素直にMicrosoftのライブラリを使用しているのでWin32APIで処理できる事がほとんどなのですが、 > 外部で開発されたアプリに関しては見た目を飾る為に通常の実装とは異なる実装をされているケースがあり、 > 自動化を行う場合の障害になっています。 > 自動化の要件が結構複雑で単なる固定の項目を選択すれば済むというような物ではないので > 画面のコントロールから情報を拾う必要があるんですが、これがほとんどできない状態なので > うまく行かない状況なのです。 > > > >>拾ったウインドウハンドルからForm.FromHandleで紐づけて拾えるのではと思っていますが、 >>NativeWindows.FromHandle メソッドや Control.FromHandle の話をされているのですか? > > イメージとしてはControl.FromHandleです。 > これでウインドウハンドルからFormに紐づけられれば、Nameプロパティを参照できないかと考えました。 >
編集キー
自分の投稿を編集する時に必要です
(半角8文字以内)
解決済み
/
解決したらチェックしておきましょう。
プレビュー/
管理者用
-
Child Tree
-