質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
COM

COM(Component Object Model)はMicrosoftによるコンポーネントテクノロジーであり、 ソフトウェアの再利用を目的とした技術を指します。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

Q&A

解決済

1回答

4256閲覧

他のアプリケーションのFormに実装されたDataGridViewのDataGridViewCellの値をIAccesibleオブジェクトから取得したい

退会済みユーザー

退会済みユーザー

総合スコア0

COM

COM(Component Object Model)はMicrosoftによるコンポーネントテクノロジーであり、 ソフトウェアの再利用を目的とした技術を指します。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

.NET Framework

.NET Framework は、Microsoft Windowsのオペレーティングシステムのために開発されたソフトウェア開発環境/実行環境です。多くのプログラミング言語をサポートしています。

0グッド

1クリップ

投稿2019/07/03 01:36

編集2019/07/08 09:52

宜しくお願いいたします。

進捗

IAccesibleで検索して来た方ごめんなさい。UIAutomationでやってます。C#なので・・・。

現状のコードはこんな感じというのをQiitaに投げつけときました。見やすいかなあと思って。

今のところCacheRequestしてから値を取ろうとすると上手くいっていないのでそこらへんにも取り組んでみます。

前提・実現したいこと

他のアプリケーションのFormに実装されたDataGridViewDataGridViewCellの値をIAccesibleオブジェクトを利用して取得したい。
以前の質問で提案してもらったAccessiblityを利用したアプリケーションの外部操作を実現しようとしています。

Reflectionなる仕組みが.NETで構築したアプリでは有効らしく実際自作のDataGridViewにいい加減な値が入ってるだけのバカチョンアプリを外部操作できたのですが、対象アプリは起動時に何らかの引数を必要としているためにそもそもAssembly.LoadFrom()で起動させられず頓挫したので今度はAccessibilityを利用した方法を試そうとしています。

  • 対象アプリはC#で作られたマルチウィンドウアプリ
  • IAccesssibleを使う理由は対象アプリが社外品でPGと細かい話や要求が出来ないため無理やり情報を抜き出す必要があるため
  • 対象アプリには一切手出し出来ない
  • IAccessible縛りではない。もっと他に便利な仕組みがあればそちらに移行してもよい。使えれば何でもOK

現状IAccessible.accNameとかIAccessible.accValueとかの値を覗き見ることが出来てはいるのですが、以下の問題が発生しています。

発生している問題・エラーメッセージ

DataGridViewCellにあたるオブジェクトを抜き出せていない。

該当のソースコード

cs

1// テストコードの概要 2 private void ReadDataGirdView(IntPtr dataGridViewHandler) 3 { // DataGirdViewのハンドラを渡して、IAccessibleのaccNameとかaccValueの内容を列挙したものをMessageBoxで表示させる 4 var accessibleObj 5 = AccessibleObjectExtractor.ExtractAccessibleObjectFromWindow(dataGridViewHandler); 6 string msg; 7 msg = MakeMsg(accessibleObj); 8 MakeChildrenMsg(accessibleObj, ref msg); 9 MessageBox.Show(msg); 10 } 11 12 private string MakeMsg(Accessibility.IAccessible accObj) 13 { 14 // IAccessibleの情報を列挙させてみる 15 return "name = \"" + accObj.accName + "\"\t, value = \"" + accObj.accValue + "\"\t, type = \"" + accObj.GetType().ToString() + "\"\r\n"; 16 } 17 18 private void MakeChildrenMsg(Accessibility.IAccessible accObj, ref string msg) 19 { 20 // 子要素の内容も書き出す 21 Object[] accObjArr = AccessibleObjectExtractor.GetChildrenList(accObj); 22 foreach (var childObj in accObjArr) 23 { 24 if (childObj == null) continue; 25 if (childObj is int) continue; // Int32型で1とか2,3,4とかが取れてしまうのでここで弾く 26 27 Accessibility.IAccessible childAccessible = (Accessibility.IAccessible)childObj; 28 29 msg += MakeMsg(childAccessible); 30 MakeChildrenMsg(childAccessible, ref msg); // 再帰的に見ていく 31 } 32 33 }

cs

1// IAccessibleのヘルパ 名前が良くない 2using Accessibility; 3using System.Runtime.InteropServices; 4 5 class AccessibleObjectExtractor 6 { 7 #region "constants and enums" 8 9 ///<seealso cref="https://referencesource.microsoft.com/#UIAutomationClientsideProviders/MS/Win32/UnsafeNativeMethods.cs,5c8db4073b33b1b9,references"/> 10 internal static Guid IID_IAccessible = new Guid(0x618736e0, 0x3c3d, 0x11cf, 0x81, 0x0c, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71); 11 internal enum OBJID : uint 12 { 13 WINDOW = 0x00000000, 14 SYSMENU = 0xFFFFFFFF, 15 TITLEBAR = 0xFFFFFFFE, 16 MENU = 0xFFFFFFFD, 17 CLIENT = 0xFFFFFFFC, 18 VSCROLL = 0xFFFFFFFB, 19 HSCROLL = 0xFFFFFFFA, 20 SIZEGRIP = 0xFFFFFFF9, 21 CARET = 0xFFFFFFF8, 22 CURSOR = 0xFFFFFFF7, 23 ALERT = 0xFFFFFFF6, 24 SOUND = 0xFFFFFFF5, 25 } 26 #endregion 27 #region functions 28 [DllImport("oleacc.dll")] 29 internal static extern int AccessibleObjectFromWindow( 30 IntPtr hwnd, 31 uint id, 32 ref Guid iid, 33 [In, Out, MarshalAs(UnmanagedType.IUnknown)] ref object ppvObject); 34 35 36 [DllImport("oleacc.dll")] 37 public static extern uint AccessibleChildren(IAccessible paccContainer, int iChildStart, int cChildren, [Out] object[] rgvarChildren, out int pcObtained); 38 39 public static IAccessible ExtractAccessibleObjectFromWindow(IntPtr handle) 40 { 41 object obj = null; 42 //int retVal = AccessibleObjectFromWindow(handle, (uint)OBJID.WINDOW, ref IID_IAccessible, ref obj); 43 int retVal = AccessibleObjectFromWindow(handle, (uint)OBJID.CLIENT, ref IID_IAccessible, ref obj); 44 return (IAccessible)obj; 45 } 46 47 public static Object[] GetChildrenList(IAccessible parent) 48 { 49 var array = new Object[parent.accChildCount]; 50 int obtained; // C#では列挙対の最大数が不明でもforeachしたりできるため戻り値に不要 51 52 AccessibleChildren(parent, 0, parent.accChildCount - 1, array, out obtained); 53 54 return array; 55 } 56 #endregion 57 }

対象のDataGridViewと現状の出力

対象のDataGridViewの状態

hoge1fuga2
piyo3nyan4

出力

name = "DataGridView" , value = "" , type = "System.__ComObject" name = "トップの行" , value = "トップの行" , type = "System.__ComObject" name = "左上のヘッダー セル" , value = "" , type = "System.__ComObject" name = "colHead" , value = "colHead" , type = "System.__ComObject" name = "行 0" , value = "hoge1;fuga2", type = "System.__ComObject" name = "行 0" , value = "" , type = "System.__ComObject" name = "colHead 行 0" , value = "hoge1" , type = "System.__ComObject"
  • まずどれがセルなのかわからない。

プログラムで判別するプロパティを見つけるなりする予定。

  • セルっぽいのが2つある(1行目っぽいのと1セル目っぽいの?)

2行目とか2セル目が見られていないのでこのままでは全く役に立たない。

方針

  • UIAutomationを利用
  • AutomationIDプロパティ経由でのAutomationElement取得はしない

できればAutomationIDを取得したいが開発環境では肝心の対象プログラムが動かず、開発用に提供された本番機にはWindowsSDKを入れるなとの指示が降りている。(開発機では環境上の都合でソフト自体が起動しない。)

補足情報(FW/ツールのバージョンなど)

  • Visual Studio 2015 SP1
  • Windows 7 64bit

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

取り急ぎ伝えておくと前回の回答では IAccessible オブジェクトからコントロールの参照が取れると言ってましたが、アンマネージドの世界からは不可能でした。すみません。ソースで確認しました。
https://referencesource.microsoft.com/#system.windows.forms/winforms/managed/system/winforms/control.cs,13029

この WM_GETOBJECT のハンドラで返される InternalAccesibleObject はコントロールに対する参照を取得できません。取得可能なのは UIAutomation の方です。とりあえずは UIAutomation を調べると良いかもしれません。

投稿2019/07/04 16:32

atata0319

総合スコア881

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/07/05 08:10

ありがとうございます。 私もソースを見てみましたがよくわかりませんでした・・・とにかく無理だと教えていただいたのでUIAutomationを利用してみます。
atata0319

2019/07/06 12:20

UIAutomation 以外にも行けそうな案が見つかりました。こちらで検証してみますが、行けなかった場合に備えて UIAutomation の調査は続行されるのが良いかと思います。
退会済みユーザー

退会済みユーザー

2019/07/08 04:13

ありがとうございます!大変助かっております 私もDataGridViewをUIAutomationで読み取りを行う方法は調べてはいますが今のところ良さそうなソースに辿り着けていません。
atata0319

2019/07/09 16:53

3日間いろいろこねくり回してみましたが、私がやろうとしている方法では C# のみでは不可能と言う結論になりました。方法は IAccesible のポインタから mscorlib で定義されている _Object インターフェースを取得し、ここから GetType で型情報を取得し、そこから Assemly → アプリケーションドメイン → System.Windows.Forms.Control の型情報を取得して、static メソッドの FromHandle から DataGridView の参照を取得しようと試みました。最初の _Object の参照がどうやっても取れないので、この方法は使えません。UIAutomation がダメならあとはフック等の C/C++ の処理しかなさそうです。と言うか最初の話に戻りますが、EntryPoint.Invoke でも起動引数は渡せると思うんですが、そっちは何がダメだったんでしょうか?
退会済みユーザー

退会済みユーザー

2019/07/10 00:57

ありがとうございます フックの処理について少し勉強してみようかと思います。 EntryPoint.Invokeでの起動はSetCompatibleTextRenderingDefault must be called before the first と例外を投げられて起動ができずにいます。引数自体もどういった値を渡すべきか不明ですので、半ば諦めています
atata0319

2019/07/10 14:42 編集

SetCompatibleTextRenderingDefault はウィンドウが先に作成されている際に例外を出力します。該当のアセンブリを起動する前に自身のコード内でフォームを表示していませんか? 起動引数については該当アプリのファイル名を変更して、自分のアプリが起動されるようにして、自分のアプリに渡された引数で相手のアプリを起動するような処理にしてはどうでしょうか?実際にはレジストリの設定で起動するアプリを切り替える方法があります。 https://troushoo.blog.fc2.com/blog-entry-143.html この方法で起動引数を読みとってそのまま渡して起動する感じですね。
退会済みユーザー

退会済みユーザー

2019/07/11 07:39

ありがとうございます SetCompatibleTextRenderingDefaultについては、Formの呼び出し後に行っていたので起動開始直後に該当のアセンブリを起動するように変更してみます。 起動引数の件についてもその方向でアプローチしてみます。 また、UIAutomationのほうでSPY++を導入せずとも自作アプリでDataGridViewなどのAutomationIDを取得できそう(該当アプリ側でDataGridViewに表示をさせる手順がかなり大変なので後日テスト)なので一旦、ベストアンサーを付けます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問