[C++]ドラッグ&ドロップの再現

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
table_in_trace
記事: 12
登録日時: 9年前
住所: 大阪

[C++]ドラッグ&ドロップの再現

#1

投稿記事 by table_in_trace » 9年前

VC++のWin32 Applicationで、遠隔操作用ツールを作成したいと思っています。
まずはマウスによる操作をプログラムで再現したいと思っているのですが、
マウス位置の移動、クリック、右クリック、ダブルクリックは再現できたものの
ドラッグ&ドロップの再現が出来なくて困っています。
(ドラッグ&ドロップを再現して、デスクトップ上のアイコンを移動させようとしたのですが期待通りの動作になりませんでした。
 単純に”移動→クリック→移動→クリックOFF”となるだけで、アイコンが移動しない状態です。)

どうすればドラッグ&ドロップを再現することが出来るのか教えて頂けないでしょうか。
初投稿で質問自体に不備があるかもしれませんが、そちらも合わせて指摘して頂けると幸いです。
よろしくお願いします。
下記が問題のプログラムです。

コード:

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#pragma comment(lib, "wsock32.lib")
#define WIN32_LEAN_AND_MEAN

//【関数】
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);	//  コールバック関数

//【変数】
HINSTANCE global_hInstance;
HDC global_hInvisibleMainDC;

////////////////////  Main 関数 ////////////////////
int WINAPI WinMain(HINSTANCE hInst,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nShowCmd){
	MSG msg;
	WNDCLASS wndclass;
	HWND hMainWindow;
	HDC hMainDC, hInvisibleMainDC;
	HBITMAP hInvisibleMainBitmap;
	global_hInstance = hInst;

	//  MainWindow 作成
	wndclass.style				= CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;	//  縦、横のサイズ変更時に再描画。ダブルクリック検出。
	wndclass.lpfnWndProc		= WndProc;
	wndclass.cbClsExtra			= wndclass.cbWndExtra = 0;
	wndclass.hInstance			= hInst;
	wndclass.hIcon				= LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor			= LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground		= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName		= NULL;
	wndclass.lpszClassName		= TEXT("MainWindow");
	if(!RegisterClass(&wndclass))	return NULL;
	hMainWindow = CreateWindow(
		TEXT("MainWindow"), TEXT("MainWindow (RemoteController)"),
		WS_VISIBLE | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX ,
		100, 100, (60*4)+8, (60*3)+27,
		NULL, NULL, hInst, NULL
	);
	if(hMainWindow==NULL)	return -1;
	hMainDC = GetDC(hMainWindow);

	//  InvisibleWindow 作成
	hInvisibleMainDC = CreateCompatibleDC(hMainDC);	//  メモリデバイスコンテキストのハンドル取得(幅1、高さ1)
	hInvisibleMainBitmap = CreateCompatibleBitmap(hMainDC, (60*4), (60*3));	//  hBitmap の幅、高さを設定
	SelectObject(hInvisibleMainDC , hInvisibleMainBitmap);	//  メモリデバイスコンテキストに hBitmap の幅、高さを取得(拡張)
	global_hInvisibleMainDC = hInvisibleMainDC;

	//  InvisibleWindow を白で塗潰し
	SelectObject(hInvisibleMainDC , GetStockObject(WHITE_BRUSH));	//  ブラシを白に変更
	Rectangle(hInvisibleMainDC, -1, -1, (60*4)+1, (60*3)+1);		//  InvisibleWindow の描画領域より1ピクセル外側に黒線の枠
	TextOut(hInvisibleMainDC, (60*4)/2-50, (60*3)/3, TEXT("なうろ~でぃんぐ"), 16);
	SelectObject(hInvisibleMainDC , GetStockObject(NULL_BRUSH));	//  ブラシをNULLに変更


	while(GetMessage(&msg, NULL, 0, 0)){
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}


//////////////////////////////  CALLBACK 関数  //////////////////////////////
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp){
	static LONG width, height;


	switch(msg){

	case WM_KEYDOWN:
		switch(wp){
		case VK_LEFT:	//  デスクトップ上の点(x=20, y=100)クリックを再現
			SetCursorPos(20, 100);
			mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
			mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
			break;

		case VK_UP:	//  デスクトップ上の点(x=20, y=100)ダブルクリックを再現
			SetCursorPos(20, 100);
			mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
			mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
			mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
			mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
			break;

		case VK_RIGHT:	//  デスクトップ上の点(x=20, y=100)右クリックを再現
			SetCursorPos(20, 100);
			mouse_event(MOUSEEVENTF_RIGHTDOWN, 0, 0, 0, 0);
			mouse_event(MOUSEEVENTF_RIGHTUP, 0, 0, 0, 0);
			break;

		case VK_DOWN:	//  デスクトップ上の点(x=20, y=100)→(x=220, y=100)にドラッグ&ドロップ
			SetCursorPos(20, 100);
			mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
			SetCursorPos(220, 100);
			mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
			break;

		case VK_RETURN:
			break;

		default:
			break;
		}
		return 0;

	case WM_PAINT:	//  再描画
		HDC hPaintDC;
		HDC hInvisiblePaintDC;
		PAINTSTRUCT ps;

		hPaintDC = BeginPaint(hwnd, &ps);
		hInvisiblePaintDC = global_hInvisibleMainDC;
		StretchBlt(
			hPaintDC, 0 , 0 , width, height,
			hInvisiblePaintDC, 0 , 0 , (60*4), (60*3), SRCCOPY
		);
		EndPaint(hwnd, &ps);
		return 0;

	case WM_SIZE:	//  サイズ変更
		RECT rect;
		GetWindowRect(hwnd, &rect);
		width = (rect.right - rect.left) - 8;	//  Window の枠:8ピクセル
		height = (rect.bottom - rect.top) - 27;	//  Window の枠:27ピクセル
		return 0;

	case WM_CREATE:
		width = (60*4);
		height = (60*3);
		return 0;

	case WM_CLOSE:	//  ウィンドウを閉じる
		//MessageBox(hBaseWindow, TEXT("終了します\n\n(保存確認など)"), TEXT("close"), MB_OK);
		break;

	case WM_DESTROY:	//  ウィンドウ破棄
		//ReleaseDC(hMainWindow, hMainDC);
		//DeleteObject(hInvisibleMainBitmap);
		//DeleteDC(hInvisibleMainDC);
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hwnd, msg, wp, lp);
}

zeek

Re: [C++]ドラッグ&ドロップの再現

#2

投稿記事 by zeek » 9年前

> どうすればドラッグ&ドロップを再現することが出来るのか教えて頂けないでしょうか。
キャプチャーが必要なのではないでしょうか。
疑似クリック DOWN 時に
SetCapture(hwnd);
疑似クリック UP 時に
ReleaseCapture();
してみてください。

アバター
milfeulle
記事: 47
登録日時: 10年前
住所: マリーランド
連絡を取る:

Re: [C++]ドラッグ&ドロップの再現

#3

投稿記事 by milfeulle » 9年前

質問者ではないですが、試してみても駄目みたいですね……。取り敢えずSendInputが現在は推奨されているみたいのでそちらも使ってみましたが。MSペイントなどではちゃんとドラッグの処理をしてくれるのですけどね。

SendInputにより3連のイベントを実行させると、このシーケンス(3つのイベント)の間に割り込まれることがないため確実だと思ったのですが上手く動作してくれないですね。

コード:

// キーが押された時の処理
SetCursorPos(53, 57);
SetCapture(hwnd);
INPUT inputs[3];
inputs[0].type = INPUT_MOUSE;
inputs[0].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;

inputs[1].type = INPUT_MOUSE;
inputs[1].mi.dwFlags = MOUSEEVENTF_MOVE;
inputs[1].mi.dx = 200;
inputs[1].mi.dy = 200;

inputs[2].type = INPUT_MOUSE;
inputs[2].mi.dwFlags = MOUSEEVENTF_LEFTUP;
SendInput(3, inputs, sizeof(INPUT));
ReleaseCapture();
ζ*'ヮ')ζプログラミングはみんなで奏でるシンフォニー

table_in_trace
記事: 12
登録日時: 9年前
住所: 大阪

Re: [C++]ドラッグ&ドロップの再現

#4

投稿記事 by table_in_trace » 9年前

zeekさんの指摘どおり、「SetCapture(hwnd);」を入れてみたところ、
10回試して(VK_DOWNを発生させると)1~2回ドラッグ&ドロップが成功するような妙な状況になりました。

コード:

case VK_DOWN:	//  デスクトップ上の点(x=20, y=100)→(x=220, y=100)にドラッグ&ドロップ
	SetCapture(GetDesktopWindow());			
	SetCursorPos(20, 100);
	mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
	SetCursorPos(220, 100);
	mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
	ReleaseCapture();
	break;
取得するハンドルをメインのウィンドウにしてみたり、直前に生成した無関係のウィンドウハンドルにしてみたところ
動作自体は同じでドラッグ&ドロップが成功したり失敗したりという状況でした。
なので、各命令の合間合間にSleep()を入れてみたところほぼ確実に成功するようになりました。
試行錯誤の結果、下記のタイミングでSleep()を入れると正しく動作するようです。

コード:

case VK_DOWN:	//  デスクトップ上の点(x=20, y=100)→(x=220, y=100)にドラッグ&ドロップ
	SetCursorPos(20, 100);
	mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
	Sleep(10);
	SetCursorPos(220, 100);
	Sleep(10);
	mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
	break;
どうやらPCの状況によって命令どおりの順序で処理されていない事が原因のようです。
zeekさん、milfeulleさん、どうもありがとうございました。
お二人のおかげでマウス操作の再現が完成しそうです。

sleep

Re: [C++]ドラッグ&ドロップの再現

#5

投稿記事 by sleep » 9年前

私もいつもなら返信しない類の投稿なのですが・・・

>僕の玩具はC言語さん
今回の件は、内容的にマウス操作のみなので「遠隔操作」という言葉は不要です。
世間ではかなり気を使う用語となってしまったため、常連の方々からレスが付かないのはそれが原因です。
不要の際は使用を控えてください。

>milfeulleさん
考え方は合ってますよ。

コード:

#include <windows.h>

#define MOUSE_LOCATION_X(x) ((DWORD)x * 65535 / GetSystemMetrics(SM_CXSCREEN))
#define MOUSE_LOCATION_Y(y) ((DWORD)y * 65535 / GetSystemMetrics(SM_CYSCREEN))

int main()
{
	INPUT inputs[4];

	inputs[0].type = INPUT_MOUSE;
	inputs[0].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
	inputs[0].mi.dx = MOUSE_LOCATION_X(53);
	inputs[0].mi.dy = MOUSE_LOCATION_Y(57);

	inputs[1].type = INPUT_MOUSE;
	inputs[1].mi.dwFlags = MOUSEEVENTF_LEFTDOWN;

	inputs[2].type = INPUT_MOUSE;
	inputs[2].mi.dwFlags = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN;
	inputs[2].mi.dx = MOUSE_LOCATION_X(200);
	inputs[2].mi.dy = MOUSE_LOCATION_Y(200);

	inputs[3].type = INPUT_MOUSE;
	inputs[3].mi.dwFlags = MOUSEEVENTF_LEFTUP;

	SendInput(4, inputs, sizeof(INPUT));

	return 0;
}

アバター
milfeulle
記事: 47
登録日時: 10年前
住所: マリーランド
連絡を取る:

Re: [C++]ドラッグ&ドロップの再現

#6

投稿記事 by milfeulle » 9年前

sleepさん、態々ありがとうございました。なるほど、(1) SetCursorPosを実行して移動させた (2) 移動中にMOUSEEVENTF_LEFTDOWNフラグも立てていなかったのが誤りでしたね; 一つ勉強になりました。この方法ならSleep関数がいりませんね。
ζ*'ヮ')ζプログラミングはみんなで奏でるシンフォニー

table_in_trace
記事: 12
登録日時: 9年前
住所: 大阪

Re: [C++]ドラッグ&ドロップの再現

#7

投稿記事 by table_in_trace » 9年前

> sleepさん
私も勉強方法のほとんどが参考書か、ネット上でも教科書に近い形式のサイトを読み込む事なので
気を使う用語であるという認識がありませんでした。
人から物を教えてもらううえで、目的や完成形のイメージを伝えた方が誤解が生じにくいかと思い
なるべく多くの情報を記載させて頂いたつもりでしたが、不必要でしたようで失礼致しました。
教えて頂いてありがとうございます。
世間情勢などは最も知識が足りない部分ですのでご、指摘頂きとても参考になります。

閉鎖

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