QA@IT

他アプリの指定したテキストボックスへ値を渡す方法

26717 PV

他のアプリ(空のテキストボックスが3つ並ぶフォーム)の
指定したテキストボックスに値を入力したいです。
2番目とか3番目とか。

FindWindowでフォームのハンドルを取得し、
テキストボックスのハンドルを取得するまではできました。

SetForegroundWindowでウィンドウをアクティブにし、
SendKeys.SendWait("{TAB}");
などで、移動させながらの入力もできましたが、
テキストボックスを指定しての入力の方法が分かりません。

テキストボックスのクラス名はSPY++で表示されたクラス名を
直接記述しています。

++++++++++++++++++++++++++++++++++++++++++++++++++++
//指定フォームのハンドル取得
IntPtr Handle = FindWindow(null, txtName.Text);

//テキストボックスのハンドル取得
IntPtr HandleC = FindWindowEx(Handle, IntPtr.Zero, "WindowsForms10.EDIT.app.0.2bf8098_r13_ad1", "");
++++++++++++++++++++++++++++++++++++++++++++++++++++

この後、どのテキストボックスへと指定するかの方法が分かりません。

ハンドルも起動する度に変わるし、クラス名もアプリによっては
変わっています。
どのように取得し、指定するのでしょうか。

開発環境:
Window7
Microsoft Visual Studio 2010 C#

回答

SetWindowTextは失礼しました。

Win8 x64, Win8 x32で確認しましたが、以下のメソッドで別に起動した .NET WindowsFormsApplicationのテキストボックスにテキストがセットされることを確認しました。

ただ、unibonさんのおっしゃるように環境によってはアクセス制御が必要になることもあるかもしれませんね。
(このサンプルを試したときは特にUACをオフにしたりは していません 。普通の環境だと思います。設定する側も設定される側もexeをたたいて起動しても大丈夫でした。)

WM_SETTEXTのサンプル

SetTextForOtherAppsTextbox.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace qaatit_control_other_apps_textbox
{
    class SetTextForOtherAppsTextbox
    {
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern IntPtr SendMessage(int hWnd, UInt32 Msg, IntPtr ptr, StringBuilder lParam);

        public const uint WM_SETTEXT = 0x000C;


        public static void SendText(int hWnd, string text)
        {

            StringBuilder sb = new StringBuilder(text);
            SendMessage(hWnd, WM_SETTEXT, IntPtr.Zero, sb);
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="targetTextboxHandle">対象のテキストボックスのハンドル</param>
        public void SetText(int targetTextboxHandle)
        {
            // ミリ秒付きで時刻の文字列表現を設定
            SendText(targetTextboxHandle, System.DateTime.Now.ToString("HH:mm:ss.fff"));
        }

    }
}

targetTextboxHandleは普通のウィンドウハンドル(いわゆる hWnd)です。
.NETからとれる値としては TextBoxの Handleプロパティでとれる値をセットすればOKです。

編集 履歴 (0)
  • 回答ありがとうございます。

    SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr ptr, StringBuilder lParam)

    で指定したテキストボックスにデータ送信ができました。
    ありがとうございます。

    お二人が仰っていたアクセス制御も問題ありませんでした
    -

テキストボックスの指定方法がうまくできませんでしたので、
子ウィンドウのハンドルの中から、クラス名でテキストボックスのものを
取得することにしました。

矩形にあります「高さ」を求めて、何番目のテキストボックスと判断することに
しました。

これでテキストボックスを指定することはできましたが、
データを渡すのがうまくいきません。

SendMessage関数を使用して送信しようと思っています。
下記のように記述しますが、きちんとデータが行きません。

hwndはテキストボックスのハンドルです。

//宣言
public const Int32 WM_COPYDATA = 0x004A;

public const Int32 WM_PASTE = 0x0302;

//送信データをByte配列に格納
byte[] bytearry = System.Text.Encoding.Default.GetBytes(txtValue.Text);
Int32 len = bytearry.Length;
COPYDATASTRUCT cds;

cds.dwData = 0;        //使用しない
cds.lpData = txtValue.Text; //テキストのポインターをセット
cds.cbData = len + 1;    

SendMessage(hwnd, WM_COPYDATA, 0, ref cds);

第三引数を"WM_PASTE"にした場合は、クリップボードにあるデータが
指定のテキストボックスに出力することはできました。

編集 履歴 (0)
  • WM_COPYDATAは受け取り側もそのメッセージを受け取る実装が必要になります。テキストを渡すならSetWindowTextなどを利用したほうがよいでしょう -
  • 回答ありがとうございます。
    さっそく"SetWindowText"を使用してみましたが、指定のテキストボックスに値は渡らないようです。説明に「ただし、ほかのアプリケーション内のコントロールのテキストを変更することはできません。」とありますので、別アプリのテキストにはいかないようです。
    -
  • 「ほかのプロセス内のコントロールのテキストを設定するには、SetWindowText 関数を呼び出すのではなく、直接 WM_SETTEXT メッセージを送ります。」とありましたので、SendMessage関数で使用してみました。

    WM_COPYDATAでは0が返りましたが、こちらは1が返って進んだかのようにみえましたが、テキストボックスに値は渡りませんでした。
    -
  • あいまいな記憶ですが、WM_SETTEXT でできると思います。ただし、自分のプログラムを起動する際に「管理者としてこのプログラムを実行する」という特権レベルを付けて実行しないと弾かれるのではないかと思います。Visual Studio のデバッガー上で動かす時は、Visual Studio の起動時にこの特権レベルを付けるのが楽です。 -

Spy++ だと、テキストボックスのウィンドウのプロパティを見ると、「一般」タブの中に、「コントロールID」があるので、これを使って指定します。
Windows API は GetDlgItem を使います。この API の名前で検索するといろいろ情報が得られると思います。

編集 履歴 (0)
  • .NETで普通に作った場合コントロールIDは起動毎に異なり固定されなかった様に思います。 -
  • 回答ありがとうございます。

    GetDlgItem もいろいろと調べました。
    第二引数の”コントロールID”がよく分かりません。取得したいハンドルのコントロールIDですが、どうやって取得しますか?

    SPY++を使用すればそのテキストボックスのコントロールIDが分かりますが、毎回同じIDではないですよね?

    GetDlgCtrlID()を使用して、取得するのでしょうか?
    -
  • 回答の投稿時に「他アプリ」が .NET のアプリケーションだということを見落としていました。 -

子ウィンドウ(テキストボックス)はFindWinowExよりはGetWindowでGW_CHILD指定するとか、EnumChildWindowsを利用した方がいいように思います。

z-orderかなにかの順で取得できたと思いますので、順番も安定しているんではないかと思います(対象のアプリによるでしょうが)。

編集 履歴 (0)
  • 指定したフォーム内のすべての子ウィンドウ(テキストボックス)を取得にはEnumChildWindowsを使用した方がいいのですね。

    取得した子Wウィンドウのハンドルから、テキストボックスで
    あることを見分けるには、何で判断しますか?
    -
ウォッチ

この質問への回答やコメントをメールでお知らせします。