C# と .Net Framework で 他の Windows アプリを操作するアプリ
簡単にですが作りました。
経緯
最近、C# を学び始めて .Net Framework
を使うようになったのですが、これがけっこう便利で楽しくて、
ふと、Windows の API なら ハンドル取ってこれそうだなと思い、調べるとすでにやってる人がいたので、
マネをしてみました。
目的
アプリ一つ一つに割り当てられているハンドル(ウィンドウハンドル)を取得して、
外部のアプリからハンドル先に値を送ることでアプリを操作することが目的です。
まだ可能かどうかは分かりませんが、ゲームのテストや同じ操作の繰り返しをプログラムで
制御できればいいなと思って作りました。
こちらを参考にしたので、試したい方はこちらを見た方が早いかと思います。
普通、アプリだと複数のウィンドウやボタンがあるので、操作をするのは難しいのですが、 自分の場合は、ゲームの操作が目的だったので、ハンドルを取ってしまえばあとはコントローラの キーにあたる値を送ることで操作することが可能です。
操作するためのキーの値はこちらを見ればいいかと
Virtual-Key Codes (Windows)
環境
環境は以下です。
- Windows 10
- VisualStudio 2017
以下は、今回使う言語やフレームワーク、その他出てくる技術です。
もし読んでて分からないところがあれば、以下のキーワードを加えて検索するといいかも……?
- C#
- .Net Framework 4.0 以上
- WPF
- Win32API
GUI の作成
WPF で GUI を作ります。 重要な部分ではないので飛ばします。以下のように作りました。
各ボタンの処理
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 Framework
の API ではなく、Win32API
の API です。これは、以下のように宣言することで使えるようになります。
[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); } }
デモ
ウィンドウハンドル取得して、キー入力をハンドル先に送るアプリを作ったらそのままUE4でも使えた。動画は、スペースキーの値を送ってる #UE4 #UE4Study pic.twitter.com/rdBUnvPXyA
— 漬茶漬け (@tauto_otya) 2017年3月25日
UE4 で http 通信を行う
少し前に UE4 で http 通信をしようと思い、ネットで方法を探していたのですが、 バージョンの違いか、手順が悪いのか、上手くいかず悩んだ末、UE4 の公式ドキュメントの方法で出来たので、その方法をまとめます。
以下、自己責任で行って下さい。
UE4.10 How To Make HTTP GET Request in C++ - Epic Wiki
上記のURLの方法をそのまま行えば出来ます。
ただ、php 周りの説明が分かっている前提なので、その辺補足出来たらいいなと思い、この記事を書くことにしました。
環境
- Windows 10
- UE4.14.3
- VisualStudio 2015
- XAMPP 3.2.2
プロジェクトの作成
まず、新規プロジェクトでC++のタブを選択します。 そして、基本コード(BASIC CODE)を選択します。
すると、VisualStudio が立ち上がると思います。気長に全ファイルを読み込むのを待ちます。
読み込めました? では、ビルドしましょう。
とくに問題が起こらないとは思うので、次に進みます。
アクタの作成
UE4 のエディタで http を取得するアクタを作ります。
まず、コンテンツブラウザの新規追加
を選び、新規 C++ファイル…
を選択します。親クラスは、Actor
を選択します。
ファイル名は何でもいいです。今回は、GetHttpActor
としました。あと、パブリック、プライベートは特に選択しませんでした。
クラスを作成
をクリックして、リコンパイルが終わるのを待ちます。
ソースコードの編集
では、プロジェクトの中身をいじっていきます。
Build.cs
”プロジェクトファイル名”.Build.cs
という名前のファイルが VisualStudio のソリューションエクスプローラー
にあると思います。
今回、自分のプロジェクト名は、MyProject
(つまりデフォルトの名前)にしたので、MyProject.Build.cs
というファイルがあるはずです……ありました。
見つかりましたか? よく探してくださいね。ソリューションエクスプローラーの検索を利用するといいかもしれません。例えば、.Build.cs
で検索してみましょう。Games
のフォルダ内を探して下さい。もう見つかった? お疲れ様です。では、次に行きましょう。
先ほど見つけたファイル(画像だと MyProject.Build.cs
)を開いてください。
MyProject.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore"});
"Http", "Json", "JsonUtilities"
という一文があると思います。バージョンによっては違うかも……。
PublicDependencyModuleNames.AddRange
を探すといいかもしれません。見つけたら、{}内に以下を追加します。
すると以下のようになると思います。
MyProject.Build.cs
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Http", "Json", "JsonUtilities" });
このファイルはこれで以上です。
Engine.ini
次は、Engine.ini
ファイルを探します。VisualStudio は使いません。そこには表示されないからです。
プロジェクトフォルダが保存されている場所に移動してください。そして、今回のプロジェクト名のフォルダの中に移動します。Saved/Config/Windows
と移動して下さい。もしかしたら Windows
がないかもしれません。Config
以下まで移動したら、Engine.ini
を探して下さい。自分は、MyProject/Saved/Config/Windows
の下に Engine.ini
がありました。そして、てきとうなテキストエディタで開きます。
そして、一番下に以下を追加します。
Engine.ini
[HTTP] HttpTimeout=300 HttpConnectionTimeout=-1 HttpReceiveTimeout=-1 HttpSendTimeout=-1 HttpMaxConnectionsPerServer=16 bEnableHttp=true bUseNullHttp=false HttpDelayTime=0
追加したら、保存して閉じて下さい。またVisualStudio に戻ります。
GetHttpActor.h
http を取得するアクタを作ったと思います。そのヘッダファイルを開きます。
今回だと、GetHttpActor.h
です。
まず、以下のようにインクルードファイルを追加します。
GetHttpActor.h
#include "Runtime/Online/HTTP/Public/Http.h"
public: 以下に次の内容を追加します。
FHttpModule* Http; /* The actual HTTP call */ UFUNCTION() void MyHttpCall(); /*Assign this function to call when the GET request processes sucessfully*/ void OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful);
ヘッダファイルは以上です。
GetHttpActor.cpp
次は、ソースファイル GetHttpActor.cpp
をいじります。まず、コンストラクタ AGetHttpActor()
に以下を追加します。
Http = &FHttpModule::Get();
そして BeginPlay()
に以下を追加します。
MyHttpCall();
以下のメンバメソッドを追加します。
GetHttpActor.cpp
/*Http call*/ void AGetHttpActor::MyHttpCall() { TSharedRef<IHttpRequest> Request = Http->CreateRequest(); Request->OnProcessRequestComplete().BindUObject(this, &AGetHttpActor::OnResponseReceived); //This is the url on which to process the request Request->SetURL("http://localhost:8081/WebApi/getint.php"); Request->SetVerb("GET"); Request->SetHeader(TEXT("User-Agent"), "X-UnrealEngine-Agent"); Request->SetHeader("Content-Type", TEXT("application/json")); Request->ProcessRequest(); } /*Assigned function on successfull http call*/ void AGetHttpActor::OnResponseReceived(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bWasSuccessful) { //Create a pointer to hold the json serialized data TSharedPtr<FJsonObject> JsonObject; //Create a reader pointer to read the json data TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(Response->GetContentAsString()); //Deserialize the json data given Reader and the actual object to deserialize if (FJsonSerializer::Deserialize(Reader, JsonObject)) { //Get the value of the json object by field name int32 recievedInt = JsonObject->GetIntegerField("customInt"); //Output it to the engine GEngine->AddOnScreenDebugMessage(1, 2.0f, FColor::Green, FString::FromInt(recievedInt)); } }
ひとまずは、コピペで構いません。
お疲れ様です。C++ に追加することは以上で終了です。
ビルドが通るか確認しましょう。
これで、GetHttpActor をレベル上に置けば http を取得してくれるのですが、今の状態で行っても http は取得できません。http を発行してくれるサーバがないからですね。では、サーバの準備をしましょう。
XAMPP
サーバを立てる方法はいろいろありますが、今回は XAMPP を使います。 インストールの方法は、ネット上にたくさんあるので、そちらにお任せします。 例えばこれを見るといいでしょう。
XAMPP を立てるときにセキュリティが気になる人は、セキュリティの設定を行うか、ネット回線を切ってしまいましょう。
たぶん XAMPP を使って Apache を立てるとエラーが発生すると思います。 その場合、以下の記事を見ると解決するかもしれません。
余裕があれば、別記事にまとめたいと思います……。
PHP ファイルの作成
XAMPP を上手く入れることが出来ましたでしょうか? お疲れ様です。あと少しで完了です。では、XAMPP を無事インストールしたので、PHP のファイルを作ります。
以下の内容をコピペして下さい。
getint.php
<?php //Create a variable to be used in $theVar = array('customInt' => 5); //Set the headers header('Content-Type: application/json'); //Encode the variable, and save the encoded string $encoded = json_encode($theVar); //Output it echo $encoded; ?>
そして、getint.php
というファイル名にして保存します。
このファイルを以下に置いて下さい。
C:\xampp\htdocs\WebApi
WebApi フォルダは作る必要があると思います。XAMPP をインストールするときに、保存場所を変更した場合は、 C:\ の下にないかもしれません。getint.php
ファイルを置くことが出来たら、少しだけ C++ ファイルをいじります(Apache のポート番号を変えていなかったら必要ないかも)。
C++ 側の URL を編集
GetHttpActor.cpp
を開いて下さい。
そして、void AGetHttpActor::MyHttpCall()
の中を見ます。
Request->SetURL("http://localhost:8081/WebApi/getint.php");
という一文があると思いますが、この SetURL の中身を変えます。 見ると分かるかもしれませんが、中身は URL を表しています。 注意してほしいのは、 localhost:8081 の 8081 です。Apache のポート番号とここのポート番号が合っていないと、正しく処理することが出来ません(URLの場所が見つからないからです)。
サーバを立てる
SetURL の中身を書き換えたら、XAMPP の Apache の Start のボタンをクリックして下さい。すると Apache の文字の周りが黄緑色みたいになると思います。黄色や赤色だと何か問題が発生していると思います。エラーの内容をコピペして検索してみましょう。
問題がない場合、サーバを立てることに成功しています。実際に確認してみましょう。ブラウザを開いて、以下のように入力します。
例えば、http://localhost:94/ とします(ポート番号は人によって異なります)。すると以下のような画面が表示されます。
SetURL と同じURLを指定してみましょう
http://localhost:94/WebApi/getint.php
すると以下のような文章が出ると思います。
{"customInt":5}
これが出ていれば成功です。
では、VisualStudio の方でビルドをします。成功したら、UE4 エディタに戻り、GetHttpActor
を選択します。右クリックをして、「GetHttpActor に基づくブループリントクラスを作成します。」
を選択します。そして、てきとうなファイル名(自分は、MyGetHttpActor にしました)を設定し、てきとうな場所に作って下さい。そして、
MyGetHttpActor
を開きます。開いたら、コンポーネントを追加で視覚的に置いてあることが分かるようなものを追加しましょう。自分はキューブを追加しました。そしてコンパイルをして、レベル上に配置します。
確認
最後に確認です。
- XAMPP は正常に動作していますか?
- Apache は正常に動作していますか?
- ブラウザを開いて、
http://localhost:94/WebApi/getint.php
にアクセスすると、php のコードが正しく動いていますか? PHP のコードをコピペしていたら {“customInt”:5} のように表示されるはずです。 - VisualStudio は正しくビルド出来ていますか?
- SetURL の中身は
http://localhost:94/WebApi/getint.php
になっていますか? - UE4 のレベル上に GetHttpActor を継承したブループリントクラスのインスタンスが置かれていますか?
では、UE4 エディタのプレイをクリックしましょう。
以下の画像のように文字が表示されるはずです。
無事、http 通信をすることが出来ました。とても小さなことですが、これだけでもいろいろ幅が広がります。長くなりましたが、以上です。 お疲れさまでした。
おわりに
http 周りのソースコード調べないとなぁ……。
あと、分かりづらいところがあればコメントを下さい。
Amazon Prime Music の曲紹介
はじめに
Amazon Prime Music で聴ける曲で、気に入った曲を紹介していく記事。
別々の記事にすると面倒そうなので、計画としてはこの記事に追記していく形。
いつまでも聴けるとは限らないので、その点ご注意。
あと主にOSTです。自分、歌(日本語)は疲れるのであまり聴きません。
OST:オリジナルサウンドトラック
#1.GRAVITY DAZE/重力的眩暈:上層への帰還において、彼女の内宇宙に生じた摂動 OST
タイトル長い。これは、ずっと前から聴ける曲なので、今後も聴ける気がする。
SCE(現SIE)のゲーム。ゲームは癖があるけど、このOSTは本当に最高。Prime Music にはまったきっかけ。万有引力の発見
とGRAVITY DAZE/重力的眩暈
は必聴。
あ、自分はこのゲーム好きです。2 はまだ終わってないけど(2017/03/04 現在)。
Prime で聴けるけどサントラ欲しい。
GRAVITY DAZE/重力的眩暈:上層への帰還において、彼女の内宇宙に生じた摂動 OST
- アーティスト: 田中公平,ゲーム・ミュージック
- 出版社/メーカー: SMD itaku (music)
- 発売日: 2012/03/21
- メディア: CD
- クリック: 12回
- この商品を含むブログ (10件) を見る
#2.仮面の勇者~心の迷宮RPG~ OST
また良曲を見つけてしまった。これだから Prime Music は止められない。
ゲーム自体はやったことないのだけど、Android アプリのゲームらしい。
聴いたらピンと来るかもしれないけど、作曲者は、景山将太(かげやま しょうた)さん。主にポケモンを作曲している人らしい。というのも自分は、スマブラの印象が強かった。
一部しか聴けないし、短いからループが鬱陶しいかもしれないけど、一度は聴いてみて。
- アーティスト: 景山将太
- 出版社/メーカー: SPICA MUSICA
- 発売日: 2016/01/23
- メディア: CD
- この商品を含むブログを見る
C++ のエラー処理
本に書いてあったエラー処理の話がためになりそうだったので、ここにまとめておこうかと思います。
参考にした本は、以下の2冊です。
4.7.4 「エラーの処理方法」を参考にしました。
ゲームエンジン・アーキテクチャは、3.3 「エラーの補足と処理」を参考にしました。
また、自分が持っている本は第1版です。
では、まとめていきます。
エラーが発生した場合に対処する方法は、主に3つです。
- エラーコードを返す
- 例外を投げる
- プログラムを停止する
では、一つずつ見ていきます。
エラーコードを返す
これは、エラー内容を値に置き換えることで対処する方法です。単純でありながら非常に便利です。今回はC++なのでエラーの対処法の一つになりますが、純粋なCの場合だと唯一の方法になります。
また、関数自体が何か値を返したいときにどちらか片方しか返せません。この場合は、エラーコードを返し、引数に値を入れることで対処します。
boost::tuple を使うと複数の結果を返すことが出来るようです。
例外を投げる
これはC++の例外処理を使います。関数が投げてきたオブジェクトを受け取り、対処します。例外を使うとエラー処理と通常の処理を別に切り分けることが出来るので、コードが読みやすくなります。また、シンプルなエラーコードよりも多くの情報を持たせることができます。さらに、大抵のデバッガは例外が発生するとそこで処理が止まってくれる(ブレーク)ので、デバッグが行いやすくなります。しかし、想定外の例外が発生するとプログラムが停止する恐れがあります。また通常は一部でも例外を使用する場合、そのアプリ全体で例外処理を正しく行うようにする必要があります。
エラーが発生した場合は、すぐに関数の処理を停止し、直前に割り当てられていたリソースを解放することが最善です。ただし、NULLなどの例外の値は返さないようにしましょう。関数を呼び出した側がさらにその例外の値をチェックする必要があるからです。この場合は、空のリストを返すようにするといいかもしれません。
エラー時に情報はなるべく多く、エラーの原因を早く突き止められるような情報を載せましょう。
エラーには、大きく分けて二つあります。ユーザエラーとプログラマエラーです。
ユーザエラーは、ソフトウェアを使うユーザが引き起こすエラーで、プログラマエラーはコード自体のバグです。ただし、ユーザにはさらに2種類に分けることができます。
一つは、ゲームをプレイしているユーザで、もう一つは、ゲームを制作しているユーザが引き起こすエラーです。
まとめると
ユーザエラー
- プレイヤーエラー(ソフトウェア使用者)
-
開発者エラー
プログラマエラー
大きく分けて2つ。細かく分けて3つのエラーの種類があります。
このユーザエラーとプログラマエラー、2つのタイプによってエラーの対処法は変わります。
ユーザエラーの場合、ゲームをプレイしていたりゲーム制作の作業をしているのでゲームや作業を止めるようなことはあってはいけません。処理を継続しながらも、ユーザの役に立つ情報を提供することが大事です。
逆にプログラマエラーの場合は、処理を止める必要があります。そして、デバッグを行うために必要な情報を提供します。
では、さらに細かく見ていきます。
プレイヤーエラー
ゲームをプレイしているときのエラーです。当然ゲームが止まるようなことはあってはいけません。そのような重度なエラーでなくとも、例えば、アイテムが0のときにアイテムを使用すると、そのアイテムは使用されずにプレイヤーに今は使うことが出来ないということを知らせる。これもプレイヤーエラーになるでしょう。
開発者エラー
これは、アーティストやアニメーターなどに起こるエラーです。例えばアセット(や画像)のようなデータを読みこませるときに、そのアセット自身が原因で起こるエラーです。このような問題のあるデータをエラーに気づかずまたは無視されていると、そのデータが入ったまま製品として出されることになります。かといって、問題があるから停止をさせると開発者の手が止まり、作業が進まなくなってしまいます。
なのでこの場合は、エラーであることを開発者に分かりやすく伝えながらも処理を継続させます。
プログラマーエラー
プログラマーエラーの場合は、本来あってはならないのですぐに処理を止めます。こうすることで、プログラマーは嫌でもバグを取り除くことになります。
では、エラーの検出、実装方法は何があるのでしょうか。
これはC++のためのAPIデザインにもあったエラーコードと例外。そして、アサートです。アサートは、式をチェックする命令行のことで、これを正しく使用することでプログラマはこれに引っかかると、引っかかった原因を取り除く必要があります。なので、上記の3つのエラーの中では、プログラマーエラーに使われるでしょう。またアサートは通常マクロで定義されるので、製品として出すときは取り除くことが出来ます。
以上、雑なまとめになりますが終わります。
今回のことで分かったのは、エラーの種類には複数あり、その種類によって対処の仕方は全然変わってくるということです。プログラマーのエラーには主にアサートを使い、開発者エラーの場合は、例外処理で処理を継続しつつエラーの情報を提供し、プレイヤーエラーのときは、エラーコードを使用して対処するという感じでしょうか。
そうすると統一性がなくぐちゃぐちゃになりそうですが……。アサートの使い道は分かりやすいですが、エラーコードと例外をどう使い分ければいいかがまだ分かりませんので、エラー処理の対処は、とにかく試行錯誤していくしかなさそうです……。
UE4 の Blueprint インタフェースについて
概要
出来ること
- 関数の作成
- 関数の入出力の追加
- 純粋(const)化*1
出来ないこと
- 変数の追加
- グラフの編集
- コンポーネントの追加
詳細
- 出力される値(アウトプット)がある
- 純粋関数である(Const にチェックをしている)