ラベル Sleep の投稿を表示しています。 すべての投稿を表示
ラベル Sleep の投稿を表示しています。 すべての投稿を表示

2018年3月8日木曜日

マウスの座標を取得し続ける(その1)

ウインドウに点が打てるようになったので、グラフィック処理は一旦置いておいて、次はマウスの座標を取得する方法を学びました。


リスト1:Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace sample25
{
    static class Program
    {
        /// 
        /// アプリケーションのメイン エントリ ポイントです。
        /// 
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

リスト1は、いつも通りのApplication.Runです。Visual Studioが吐き出したコードそのままです。

次のリスト2がマウス座標を取得し続けるプログラムの本体です。Lassyの戯言様で勉強させていただきました。

リスト2:Form1.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;

namespace sample25
{
    public partial class Form1 : Form
    {

        private Thread thread = null;

        public Form1()
        {
            InitializeComponent();

            thread = new Thread(new ThreadStart(GetMousePosition));
            thread.Start();
        }

        public void GetMousePosition()
        {
            while(true)
            {
                SetText();
                Thread.Sleep(100);
            }
        }

        public delegate void SetTextDelegate();

        public void SetText()
        {
            if(InvokeRequired)
            {
                Invoke(new SetTextDelegate(SetText));
                return;
            }
            this.Text = "x=" + Cursor.Position.X + " :y=" + Cursor.Position.Y;
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            if( thread != null )
            {
                thread.Abort();
                //return();
            }
        }
    }
}

このプログラムでは、Form1のコンストラクタの中で、マウス座標を取得し続ける処理を別スレッド(サブスレッド)で起動する、ということをしています。100msごとにマウス座標を取得して、Form1のTextプロパティに座標を書き込んでいます。

Form1のTextプロパティに対して、Form1で生成したサブスレッド(つまり別のスレッド)からForm1のプロパティの操作を行う為に、難しい処理をしています。

具体的には、

            if(InvokeRequired)
            {
                Invoke(new SetTextDelegate(SetText));
                return;
            }


の部分です。呼び出し元が自スレッド(Form1)ではない時、このプログラムの場合はサブスレッドとして動いているGetMousePoisition()から呼び出されたとき、Invokeメソッドを使ってSetTextメソッドへの参照(関数ポインタのようなもの)を生成し、参照から間接的にSetTextメソッドを呼び出しています。Invoke経由の動作や自スレッドからの呼び出しの場合は、InvokeRequiredがfalseとなり、Invokeメソッドは実行されずにifブロックの直後の処理(this.Text〜)が動くのだと思います。

マルチスレッドで動作するプログラムの場合、自スレッド以外のスレッドからのアクセス場合、参照・ポインタによるアクセスでなければならないようで、間接呼び出しを行う為にこのような処理をするのだと思います。

他のサイトでも同様の処理が紹介されていたので、別スレッドからの呼び出しで使われる定番処理なのだと思います。

なお、参考にさせていただいているサイトでは、他にもフックというものを使用する、といった処理の紹介もされています。

そのプログラムも、入力して動かしてみましたが、この記事では割愛します。

さて、サブスレッドでマウス座標を取得し続けるのは、親スレッドが何をしているときでも座標の取得を止めどなく続ける、ということです。しかし、今回の目的に対して必要かどうかを考えると、もう少し簡便な方法でも良いと思います。

今回はここまでです。次回、マウスの話の続きをやります。