よろしくお願いいたします。
前提・実現したいこと
以前の質問「既存のアプリでDataGridViewなどを用いて表示されている内容を取得したい」の続き・・・というより、同じ前提で別のことがしたいために投稿した質問です。
C#で作成されたと聞いているアプリケーションを外部から自動化する必要があり、実行ファイルを参照してコントロールを参照する方法を教えてもらいました。
ウィンドウハンドルを利用した場合はTextBox
の読み書きやButton
の押下が行えたのですが、今回はDataGridView
を参照する必要性から上記の方法に方針を変更しました。
Invoke()
を利用する方法などに行き着いたのは別スレッドのButton
コントロール等のUIを操作する方法を調べたためです。
ほかのアプローチがあるならばそれでも構いません。
既存アプリケーションをAssembly
オブジェクトから実行したときにスレッドを分けないと自作アプリ側から何もできないのでスレッド分けは回避不能だと思っています。
なお、デリゲート関数を宣言して使う方法はうまくいったのですが関数の外に必要な表記があるのが汚くて嫌なので回避したいです。
リフレクションという機能は前回の質問で初めて知りました。
機能や用語について調べてもピンとこない場合質問させていただくかもしれません。
発生している問題・エラーメッセージ
cs
1 Button b = (Button)c; // 変数cについては ### 該当のソースコードを 参照ください。 2 object buttonInstance = Activator.CreateInstance(buttonType); 3 MethodInfo PerformClickInfo = buttonType.GetMethod("PerformClick", 4 BindingFlags.Public | BindingFlags.InvokeMethod); 5 6 PerformClickInfo.Invoke( buttonInstance, new object[0]); 7// PerformClick()の引数はvoidだからnew object[0]で実行可能なはずなのに... 8 9// NullReferenceException{"オブジェクト参照がオブジェクト インスタンスに設定されていません。"} 10// 型 'System.NullReferenceException' のハンドルされていない例外が MSClacUIAutomation.exe で発生しました 11// 12// 追加情報:オブジェクト参照がオブジェクト インスタンスに設定されていません。
cs
1 Button b = (Button)c; // 変数cについては ### 該当のソースコードを 参照ください。 2 Delegate local_InvokePerformClick = Delegate.CreateDelegate(typeof(Action), 3 this, 4 b.GetType().GetMethod("PerformClick")); 5// どの引数が何を欲しているのかわからない・・・ 6 7// ArgumentException{"ターゲット メソッドとデリゲート型との間に、シグネチャまたはセキュリティ透過性の互換性がないため、ターゲット メソッドにバインドできません。"} 8// 型 'System.ArgumentException' のハンドルされていない例外が mscorlib.dll で発生しました 9// 10// 追加情報:ターゲット メソッドとデリゲート型との間に、シグネチャまたはセキュリティ透過性の互換性がないため、ターゲット メソッドにバインドできません。 11 12 Invoke(local_InvokePerformClick); 13
該当のソースコード
c#
1using System.Threading; 2using System.Reflection; 3 4public partial class Form1 : Form 5{ 6 ThreadUITarget m_uiAutomationTargetThread; 7 DataGridView m_uiAutoTargetDGV; 8 9 public Form1() 10 { 11 InitializeComponent(); 12 } 13 14 private void Form1_Load(object sender, EventArgs e) 15 { 16 // Assembly.EntryPoint.Invoke()するとAssembly.Load()したやつにスレッドを占有されちゃうのでサブスレッドを生成 17 new Thread(new ThreadStart( RunApp)); 18 } 19 20 private void RunApp() 21 { 22 // このプログラムのプロジェクトからexeファイルを参照したtargetAppって名前のWindows Formアプリを立ち上げる 23 Assembly assemblyTargetApp = Assembly.Load("targetApp"); 24 assemblyTargetApp.EntryPoint.Invoke(null, new object[0]); 25 } 26 27 private void button1_Click(object sender, EventArgs e) 28 { 29 FormCollection fc = Application.OpenForms; 30 // FormCollectionには自分も含まれちゃうので外部操作を行いたいアプリの親フォームを捜してあげる 31 Form targetForm = null; 32 foreach(Form f in fc) 33 { 34 // 外部操作を行いたいアプリのFormのTextプロパティに"targetApp"が登録されてる前提 35 if (f.Text == "targetApp") 36 { 37 targetForm = f; 38 break; 39 } 40 } 41 // 必ず見つかる(確信) 42 foreach (Control c in targetForm.Controls) 43 { 44 if (c is DataGridView) 45 { 46 string msg; 47 DataGridView dgv = (DataGridView)c; 48 foreach( DataGridViewRow r in dgv.Rows) 49 foreach( DataGridViewCell c in r.cells) 50 msg += c.Value.ToString() + "\n"; // DataGridViewはDataGridViewCells.Value==nullなセルが発生することがあるけどここでは無視 51 52 Messagebox.Show(msg); 53 } 54 else if(c is Button) 55 { 56// ここに ### 発生している問題・エラーメッセージ に乗せたコードが載るイメージ 57 } 58 } 59}
実際に実行させてるコードを元に読みやすくなるよう改変したコードなのでまんまで動かすことはできないと思います。参考まで。
試したこと
兼参考にしたページ集
InvokeMemberを使ってみる
MethodInfo.Invokeを使ってみる
補足情報(FW/ツールのバージョンなど)
ここにより詳細な情報を記載してください。
- Visual Studio 2015 SP1
- Windows 7 64bit
回答2件
あなたの回答
tips
プレビュー