読者です 読者をやめる 読者になる 読者になる

お茶漬けびより

学んだことを整理する場所です。主に、C++, Unreal Engine 4 (UE4) を扱います。

C# と .Net Framework で 他の Windows アプリを操作するアプリ

簡単にですが作りました。

経緯

最近、C# を学び始めて .Net Framework を使うようになったのですが、これがけっこう便利で楽しくて、 ふと、WindowsAPI なら ハンドル取ってこれそうだなと思い、調べるとすでにやってる人がいたので、 マネをしてみました。

目的

アプリ一つ一つに割り当てられているハンドル(ウィンドウハンドル)を取得して、 外部のアプリからハンドル先に値を送ることでアプリを操作することが目的です。
まだ可能かどうかは分かりませんが、ゲームのテストや同じ操作の繰り返しをプログラムで 制御できればいいなと思って作りました。

こちらを参考にしたので、試したい方はこちらを見た方が早いかと思います。

普通、アプリだと複数のウィンドウやボタンがあるので、操作をするのは難しいのですが、 自分の場合は、ゲームの操作が目的だったので、ハンドルを取ってしまえばあとはコントローラの キーにあたる値を送ることで操作することが可能です。

操作するためのキーの値はこちらを見ればいいかと
Virtual-Key Codes (Windows)

環境

環境は以下です。

以下は、今回使う言語やフレームワーク、その他出てくる技術です。
もし読んでて分からないところがあれば、以下のキーワードを加えて検索するといいかも……?

GUI の作成

WPFGUI を作ります。 重要な部分ではないので飛ばします。以下のように作りました。

f:id:pickles-ochazuke:20170326231753p:plain

各ボタンの処理

Get Handle というボタンは、隣にある Process Name の入力内容を元に関係するアプリを探して、ウィンドウハンドルを取得します。Process Name は、タスクマネージャを開き、詳細タブに並んでいる .exe を除いたアプリの名前を入力します。

ボタン入力時の処理は以下です。

private void GetHandleButton_Click(object sender, RoutedEventArgs e)
{
    process = System.Diagnostics.Process.GetProcessesByName(ProcessName.Text);
    foreach (System.Diagnostics.Process ps in process) {
        LogBox.Text += ps.Id + ps.MainWindowTitle + ps.MainWindowHandle + "\n";
    }
}

GetProcessesByName でアプリのハンドルを取得します。 取得したら、内容をテキストボックスに出力します(これは確認用)。

Button というボタンは、Message の内容をハンドル先に送ります。つまり、Message に操作用の値を書き、 Button を押すことでアプリを操作します。が、これはまだ未実装です。現状は、予め用意した値を送るようにしています。

IntPtr hWnd = process[0].MainWindowHandle; 
LogBox.Text += SendMessage(hWnd, 0x0100, 0x20, 0x00);

SendMessage() で、どう操作したいかを指示しています。その後、確認用にテキストボックスに出力しています。 SendMessage(ハンドル, イベント, 内容1, 内容2) のように指定します。
イベントは、キーが押されたキーが離されたマウスのボタンがクリックされたなどです。内容1,2はイベントに合わせて指定します。何もない場合は 0x00 でいいと思います。 上記だと、hWnd に、スペースキー押されたという情報を送ります。 SendMessage は、.Net FrameworkAPI ではなく、Win32APIAPI です。これは、以下のように宣言することで使えるようになります。

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);

DllImport という属性(Attribute)を使うことで、外部のライブラリを使えるようにしています。 ここら辺は、以下を参照してください。(正直、自分はちゃんと理解出来ていません……)

@IT:.NET TIPS Win32 APIやDLL関数を呼び出すには? - C#
キーワード:Attribute, Reflection, Annotation

これだけのコードで、別のアプリにプログラムで好きなキー入力を行うことができます。

コード

XAML 側のコードがないので、これだけでは動かないのですが、一応全文載せておきます。

using System;
using System.Windows;

namespace GetHandle
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        System.Diagnostics.Process[] process;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void GetHandleButton_Click(object sender, RoutedEventArgs e)
        {
            process = System.Diagnostics.Process.GetProcessesByName(ProcessName.Text);
            foreach (System.Diagnostics.Process ps in process) {
                LogBox.Text += ps.Id + ps.MainWindowTitle + ps.MainWindowHandle + "\n";

            }
        }

        private void MessageButton_Click(object sender, RoutedEventArgs e)
        {
            IntPtr hWnd = process[0].MainWindowHandle; 
            LogBox.Text += SendMessage(hWnd, 0x0100, 0x20, 0x00);
        }

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int SendMessage(IntPtr hWnd, uint Msg, uint wParam, uint lParam);
    }
}

デモ