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

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

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

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

Visual Studio

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

.NET Framework

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

Q&A

解決済

2回答

3842閲覧

リフレクションを利用して既存アプリのButtonをクリックした際の動作を行わせたい

退会済みユーザー

退会済みユーザー

総合スコア0

C#

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

Visual Studio

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

.NET Framework

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

0グッド

0クリップ

投稿2019/06/19 06:51

編集2019/06/20 23:51

よろしくお願いいたします。

前提・実現したいこと

以前の質問「既存のアプリで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

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

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

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

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

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

tamoto

2019/06/19 08:20

質問文の前提にあるリンク先間違ってます?以前の質問の続きでしょうか?
退会済みユーザー

退会済みユーザー

2019/06/19 08:45

以前の質問の続きです。質問の内容が変わるので新しく質問しなおしました。
tamoto

2019/06/19 10:37

他人の質問にリンクしているようですが。
退会済みユーザー

退会済みユーザー

2019/06/20 03:57

ありがとうございます。修正しました。
guest

回答2

0

ベストアンサー

こんにちは。

if 文の型検査で cButton であることが確定しているのですよね?であれば、PerformClick したいだけなら、

csharp

1{ 2 var cButton = (Button)c; 3 cButton.PerformClick(); 4}

これだけでいけると思います。
リフレクションを利用しなければならないという制約があるならダメですが。


あともう一つ、「C#で作成されたと聞いているアプリケーション」の実行ファイルが手元にあるなら、それを新しいプロジェクトから「参照の追加」で登録することで型情報を扱えるようになります。
アプリケーション固有のクラスに対しても、public なメソッドやプロパティを普通に呼び出せるようになります。

投稿2019/06/19 08:28

tamoto

総合スコア4103

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

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

退会済みユーザー

退会済みユーザー

2019/06/19 09:20 編集

ありがとうございます。 ですが、 {"有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'button1' がアクセスされました。"} というエラーが発生することを確認済みです。 また、参照は以前の質問のほうで今回質問の対象になっているプロジェクトで行っているAssembly.Load()のために行っています。
tamoto

2019/06/19 10:19 編集

なるほど。別スレッドで稼働してるなら、クリックの行を `targetForm.Invoke((Action)(() => cButton.PerformClick()));` でできませんかね? 以前の質問を見る限りでは、文字列指定で動的ロードだけしており静的参照していないように見えましたが、参照していたということですね。
退会済みユーザー

退会済みユーザー

2019/06/20 07:12

ありがとうございます。いけました! targetForm.Invoke()がtargetFormのインスタンス(の存在するスレッド)で実行する その引数の匿名関数が実行する内容・・・って感じですね? リフレクションの仕組みを利用するためにさらによくわからない関数を使って・・・みたいな危なっかしい方法よりもエレガントですね!
guest

0

なんか動きました。
なんでだろう?

cs

1 // これでなぜか動く 2 // よく見たらこれ使ってないじゃん//Type buttonType = b.GetType(); 3 // 使ってないじゃん //MethodInfo PerformClickInfo = buttonType.GetMethod("PerformClick", BindingFlags.Public | BindingFlags.InvokeMethod); 4 // なぜActionなどという型に変換しないといけないのかはわからない。 5 6 7// Action型、Func型はDelegate型の子分?Delegateをインタフェースとした関数ポインタ機能をC#で実現するクラス?? 8// CreateDelegate()の第一引数がActionまたはFuncの二択からの選択? 9// 第二引数は実行を担当するインスタンス?(または実行するメモリ上の位置とか?) 10// 第三引数は実行したい関数のメタデータ? 11 var local_InvokePerformClick = 12               (Action)Delegate.CreateDelegate(typeof(Action), // Typeを指定するがよく意味がわからない 13                   b, // 別の方法だと「別スレッドからコントロールを触っちゃだめなんだよ!」って怒られるんだけどCreateDelegateだと参照するだけなんだろうか? 14 // ていうかButtonのインスタンスじゃなくてFormのインスタンスのほうがよかったんだろうか 15                   b.GetType().GetMethod("PerformClick")); // たぶんメモリのどこら辺からどこら辺を実行してねみたいな指定なんだと思う。 16 Invoke(local_InvokePerformClick); 17

意味もわからず書いたコードなので数日置きます
回答がいただけないようならばもうこれがベストアンサーです。

投稿2019/06/19 07:30

編集2019/06/20 07:22
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問