マウスのプログラム制御方法について(winAPI-C)

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
turkey

マウスのプログラム制御方法について(winAPI-C)

#1

投稿記事 by turkey » 11年前

今、非クライアント領域も含むマウスを制御するプログラムをwinAPIをつかって組んでいます。今のところマウスの制御(移動、クリック)をSendInput()を使って実現していますが、クリックの動作が不安定です。そこで不安定になる原因の目星をつけているのですが、ここで質問です。

1.SendInput()を一定時間の間に多く実行過ぎるといけないのか(今のところ一秒間に60回程度)

2.解決策として考えたのですが、別スレッドでDefWindowProc()を使用してマウス制御することは可能かどうか
//別スレッド
HWND hWnd = GetDesktopWindow;
while(1) {
DefWindowProc(hWnd, WM_MOUSEMOVE, ?????, /*マウス座標*/);
}

3.2の方法が可能ならば例で書いたコードであっているのかどうか(特にデスクトップウィンドウハンドルにメッセージを送るとパソコン全体を操作してマウスを制御したことになるのか)と????にいれる引数を知りたいです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: マウスのプログラム制御方法について(winAPI-C)

#2

投稿記事 by softya(ソフト屋) » 11年前

1秒間に60回も連打する事自体が意味が無いと思います。
そんなに連打されたらアプリがイベント処理できないのでは?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

turkey

Re: マウスのプログラム制御方法について(winAPI-C)

#3

投稿記事 by turkey » 11年前

メッセージをキュー-に送らずにマウスを制御することは可能なのでしょうか?

マウスの移動をなめらかにするためにSendInput()を一秒間に60回程度実行しています

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: マウスのプログラム制御方法について(winAPI-C)

#4

投稿記事 by softya(ソフト屋) » 11年前

turkey さんが書きました:メッセージをキュー-に送らずにマウスを制御することは可能なのでしょうか?

マウスの移動をなめらかにするためにSendInput()を一秒間に60回程度実行しています
マウスが動いてもアプリはメッセージキューから受け取らざるおえませんがマウスだけ動けば良いのですか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

turkey
記事: 8
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#5

投稿記事 by turkey » 11年前

このプログラムは他のPCを遠隔操作するプログラムですのでマウスを移動させるのとクリックがないといけないです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: マウスのプログラム制御方法について(winAPI-C)

#6

投稿記事 by softya(ソフト屋) » 11年前

問題はですね。マウス移動とクリック動作をさせられてもアプリ側は秒間60回に追従出来ませんってのが問題です。
あと一秒に60回も遠隔通信できませんがLAN内の通信なのでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

turkey
記事: 8
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#7

投稿記事 by turkey » 11年前

いえ、LAN内の通信ではありません。200ms間隔にマウス座標を送り、その間は受信側がマウス座標を補完して直線上に進めています。なのでマウスの座標を移動させるのは一秒間に60回ということです。

ということは、SendInput()をつかうにしろメッセージを送るにしろ無理ということでしょうか。

リアルタイムにマウスを動かすのはこの方法しか見つからなかったんです。
(リモートデスクトップ等のソフトはもっと低レベル層で操作している?)

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: マウスのプログラム制御方法について(winAPI-C)

#8

投稿記事 by softya(ソフト屋) » 11年前

じゃあ60回動くのはマウスの移動だけでクリックはしないのですね?
なら、SendInput()で問題ないと思いますがアプリでのコントールをどうやっているかがわからないので、そこで問題があり失敗している可能性があります。

ただ、秒間60回もリモートデスクトップ等でも動いていないと思いますけどね。
リモートデスクトップでも、まともに絵をかけないと思いますが如何でしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

turkey
記事: 8
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#9

投稿記事 by turkey » 11年前

たしかに、そうですね。秒間60回更新はあきらめることにします。

SendInput()を使うにしたがって不安があるのでお聞きしてもよろしいでしょうか。現時点でのコードを概略で書くとこんなかんじです。

コード:

//受信スレッド-----------------
next_point = /*(受信座標x, 受信座標y)*/;  //200ms秒ごとにおくられてくる座標
isLButton = true; //左クリックを押した情報がきたら
isRButton = true; //右クリックを押した情報がきたら

isLButton = false; //左クリックを押した情報がきたら
isRButton = false; //右クリックを押した情報がきたら
//------------------------------

//マウスを移動させる関数 (別スレッドから呼び出される)------------------
void MouseMove::MoveMousePosion(void)
{
	POINT now_point;
	INPUT mouse_info;
	char str[STR_BUFF];

	GetCursorPos(&now_point);	//今のマウス座標取得

	if((now_point.x != next_point.x)||(now_point.y != next_point.y))
	{
		mouse_info.type           = INPUT_MOUSE;
		mouse_info.mi.dx	      = MI_H((int)(now_point.x + (next_point.x - now_point.x)/10.0));
		mouse_info.mi.dy          = MI_V((int)(now_point.y + (next_point.y - now_point.y)/10.0));
		mouse_info.mi.dwFlags     = MouseState();
		mouse_info.mi.dwExtraInfo = GetMessageExtraInfo();

		if (SendInput(1, &mouse_info, sizeof(mouse_info)) == false) {
			sprintf(str, "InputErrorCode:%d", GetLastError());
			miq.msgEnQueue(ERROR, str);//エラーメッセージをおくります
		}
	}
}

//mi.dwExtraInfo に入れるマウス状態を返す
DWORD MouseMove::MouseState(void)
{
	if(isRButton == true) {
		return MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN;
	}

	if(isLButton == true) {
		return MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_RIGHTDOWN;
	}

	return MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
}
このような使い方でいいでしょうか。送信側でクリックを押すと、受信側で始めの一回だけキーを押した情報をSendInput()に送るやり方のほうがただしいのか、今のでいいのかわかりません。

AKIЯA
記事: 58
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#10

投稿記事 by AKIЯA » 11年前

動作の仕様がまだあまり理解できませんが、この仕様で遠隔操作させてパケット送信すると
間違いなくフリーズするレベルかと思います。

送信スレッド側では200msごとに常にマウスイベントが発生するのでしょうか?
なぜ聞くかというと無駄な処理を回避するためです。マウスの移動やクリックイベントが発生した場合のみ
イベントを受信してみては?

パケット送信するならばできるだけ無駄を省き通信回数を減らす方法を考えます。
あとは無駄な処理をさせないこと。

受信側では19行目でマウス座標に変化がないと破棄しているので
送信側でもマウス座標に変化がないもしくはマウス移動したイベントを受信しない限り
受信スレッドには送信しなくてもいいと思います。
WM_MOUSEMOVEイベントで処理の軽減ができるかと思います。

通信対戦させるために似たようなものをこれから実装するのですが、
この辺を考慮して作るといいと思います。
全然違ったらごめんなさい。

turkey
記事: 8
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#11

投稿記事 by turkey » 11年前

たいしかに送信側での今の仕様だと、クリックはイベントが起きて送信はいいですが、マウス移動については常に200ms毎に座標を送ります。これは改善したほうがいいかもしれません。

返答ありがとうございました。

加えて、これまでのテストの結果マウスの不安定要素は以下のとおりです。
1.リアルタイムにクリックされない(クリックデータのみ遅れて受信されたりしているので送信側の問題?)
2.アプリケーションを閉じた後、左クリックが押せなくなる。
3.アプリケーションを閉じた後、一定時間(10秒くらい)右クリックが連打される

1はただのバグだと思いますが、2,3については目星がつかない状況です。

AKIЯA
記事: 58
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#12

投稿記事 by AKIЯA » 11年前

AのPCからBのPCへやり取りするのでしょうか?
それとも同一のPC上でのスレッド間通信でしょうか?

送信側と受信側のソースコード見ないとなんとも言えませんね。
皆目検討もつきません。

AKIЯA
記事: 58
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#13

投稿記事 by AKIЯA » 11年前

クライアント側の処理はこんな感じになるかと思います。
サーバー側はソケット開いて待っていればいいと思います。

ここでポイントになるのはWM_LBUTTONUPです。
あえてWM_LBUTTONDOWNにはしませんでした。
WM_LBUTTONDOWNで判定すると押している間にもいっぱいイベントが発生するからです。
よってクリックを押してから離された時に判定しています。

あとはコメントしてあるところにそれに対応した処理を書けばよいかと思います。
スレッドは使ってもできますが、そこはお好みで

コード:

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	int wmId, wmEvent;
	PAINTSTRUCT ps;
	HDC hdc;

	switch (message)
	{
	case WM_CREATE:
		// ここでソケットを用意する
		break;
	case WM_COMMAND:
		wmId    = LOWORD(wParam);
		wmEvent = HIWORD(wParam);
		switch (wmId)
		{
		case IDM_ABOUT:
			DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
			break;
		case IDM_EXIT:
			DestroyWindow(hWnd);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
		break;
	// 追加箇所
	case WM_MOUSEMOVE:
		// マウス座標を取得してソケットに送る。
		break;
	case WM_LBUTTONUP:
		// マウス左クリックされたのでソケットに送る。
		break;
	case WM_RBUTTONUP:
		// マウス右クリックされたのでソケットに送る。
		break;
	// ここまで追加
	case WM_PAINT:
		hdc = BeginPaint(hWnd, &ps);
		EndPaint(hWnd, &ps);
		break;
	case WM_DESTROY:
		// ここでソケットをクローズ
		PostQuitMessage(0);
		break;
	default:
		return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}

turkey
記事: 8
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#14

投稿記事 by turkey » 11年前

すみません、書き忘れてましたが、たしかに送信側のWM_LBUTTONDOWNについては不安な点があったのでメッセージのLPARMの30bit目で判断して押した時と離した時はそれぞれ一回ずつ送っていました。

そこでコードにもあるように受信側が受信データを元にマウスのクリック状態をフラグに格納していました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: マウスのプログラム制御方法について(winAPI-C)

#15

投稿記事 by softya(ソフト屋) » 11年前

turkey さんが書きました:すみません、書き忘れてましたが、たしかに送信側のWM_LBUTTONDOWNについては不安な点があったのでメッセージのLPARMの30bit目で判断して押した時と離した時はそれぞれ一回ずつ送っていました。

そこでコードにもあるように受信側が受信データを元にマウスのクリック状態をフラグに格納していました。
何か問題がある時は通信なのか受信側の動作なのか確認するのがセオリーです。
受信する代わりにテキストでコマンドを書いて動くようにして想定通りに動作する確認してみてはどうでしょうか?
後々問題が出た時にも通信ログを残して再現テストにも使える機能に出来るはずです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

turkey
記事: 8
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#16

投稿記事 by turkey » 11年前

たしかにそうですね。一つ一つの機能を確認していきたいと思います。

またわからないことがあったら掲示板を利用させてもらいます。

返答ありがとうございました。

ISLe
記事: 2650
登録日時: 13年前
連絡を取る:

Re: マウスのプログラム制御方法について(winAPI-C)

#17

投稿記事 by ISLe » 11年前

送信側は、マウスボタンのアップダウンのイベントも200msごとにまとめて送っているわけですよね。

マウスボタンのダウン→アップがクリックとして認識される間隔はシステムに設定があり変更もできます。
現在の仕様では、そもそも確実にクリック操作を送信できる保証がありません。
  • マウスボタンのアップダウンだけではクリックかどうか分からないので、クリックをひとつのイベントとして送受信する必要がある。
  • 受信側でクリックは、システムの設定を読み取り、その間隔以内(早すぎてもいけない)でマウスボタンダウン→マウスボタンアップを行う。
というふうにしなければいけないと思います。

データの送受信間隔を短くしてもリアルタイムに届くとは限りませんし。

turkey
記事: 8
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#18

投稿記事 by turkey » 11年前

ISLe さんが書きました:
  • マウスボタンのアップダウンだけではクリックかどうか分からないので、クリックをひとつのイベントとして送受信する必要がある。
  • 受信側でクリックは、システムの設定を読み取り、その間隔以内(早すぎてもいけない)でマウスボタンダウン→マウスボタンアップを行う。
というふうにしなければいけないと思います。
前者の場合にしてクリックを実装することができました。しかし、不可解なことがあります。
まずクリックについて,下のコードは問題のないコードです。

コード:

POINT now_position;
GetCursorPos(&now_position);
INPUT mouse_info;

mouse_info.type                 = INPUT_MOUSE;
mouse_info.mi.dx	          = MI_H(now_position.x);
mouse_info.mi.dy               = MI_V(now_position.y);
mouse_info.mi.dwFlags       = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
mouse_info.mi.dwExtraInfo = GetMessageExtraInfo();
SendInput(1, &mouse_info, sizeof(mouse_info));

mouse_info.type                 = INPUT_MOUSE;
mouse_info.mi.dx               = 0;
mouse_info.mi.dy               = 0;
mouse_info.mi.dwFlags       = MOUSEEVENTF_LEFTDOWN;
mouse_info.mi.dwExtraInfo = GetMessageExtraInfo();
SendInput(1, &mouse_info, sizeof(mouse_info));

mouse_info.type                 = INPUT_MOUSE;
mouse_info.mi.dx	          = 0;
mouse_info.mi.dy               = 0;
mouse_info.mi.dwFlags       = MOUSEEVENTF_LEFTUP;
mouse_info.mi.dwExtraInfo = GetMessageExtraInfo();
SendInput(1, &mouse_info, sizeof(mouse_info));
このように、Input構造体であるmouse_infoにデータを入れるごとにSendInput()を実行すると問題ないのですが、

コード:

POINT now_position;
GetCursorPos(&now_position);
INPUT mouse_info[3];

mouse_info[0].type           = INPUT_MOUSE;
mouse_info[0].mi.dx			 = MI_H(now_position.x);
mouse_info[0].mi.dy          = MI_V(now_position.y);
mouse_info[0].mi.dwFlags     = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
mouse_info[0].mi.dwExtraInfo = GetMessageExtraInfo();

mouse_info[1].type           = INPUT_MOUSE;
mouse_info[1].mi.dx			 = 0;
mouse_info[1].mi.dy          = 0;
mouse_info[1].mi.dwFlags     = MOUSEEVENTF_LEFTDOWN;
mouse_info[1].mi.dwExtraInfo = GetMessageExtraInfo();

mouse_info[2].type           = INPUT_MOUSE;
mouse_info[2].mi.dx	         = 0;
mouse_info[2].mi.dy          = 0;
mouse_info[2].mi.dwFlags     = MOUSEEVENTF_LEFTUP;
mouse_info[2].mi.dwExtraInfo = GetMessageExtraInfo();

SendInput(3, mouse_info, sizeof(mouse_info));
このように最後にまとめてSendInput()を実行するとSendInput()からエラーが返り、GetLastError()で確認すると『パラメータが間違っています。』のエラーとなります。どこがおかしいのでしょうか

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: マウスのプログラム制御方法について(winAPI-C)

#19

投稿記事 by softya(ソフト屋) » 11年前

http://msdn.microsoft.com/ja-jp/library/cc411004.aspx
> INPUT 構造体のサイズを指定します。cbSize パラメータの値が INPUT 構造体のサイズと等しくない場合、関数は失敗します。
との事ですので、配列全体サイズを指定すると失敗すると思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

AKIЯA
記事: 58
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#20

投稿記事 by AKIЯA » 11年前

多分 ヒントはINPUT構造体です。
初期化していない。

INPUT mouse_info[3]の下にこれを追加したらどうですかね?
ZeroMemory(mouse_info, sizeof(mouse_info));

違ったらごめんなさい。
最後に編集したユーザー AKIЯA on 2012年10月31日(水) 21:50 [ 編集 1 回目 ]

turkey
記事: 8
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#21

投稿記事 by turkey » 11年前

無事解決しました。ありがとうございました。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: マウスのプログラム制御方法について(winAPI-C)

#22

投稿記事 by softya(ソフト屋) » 11年前

turkey さんが書きました:無事解決しました。ありがとうございました。
結局何処が問題だったのでしょうか?
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

AKIЯA
記事: 58
登録日時: 11年前

Re: マウスのプログラム制御方法について(winAPI-C)

#23

投稿記事 by AKIЯA » 11年前

SendInput(3, mouse_info, sizeof(INPUT));

多分 ここですかね?
softyaさんので正解だと思います。

閉鎖

“C言語何でも質問掲示板” へ戻る